Commit 53d5c394b6979dac4cfe74eb816a1ca9e98c55eb
Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/iot
# Conflicts: # yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java # yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceMqttConnectionParamsRespVO.java # yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/device/IotDeviceRespVO.java # yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaFirmwareController.java # yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/IotOtaUpgradeRecordController.java # yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/vo/IotThingModelRespVO.java
Showing
227 changed files
with
1854 additions
and
1227 deletions
Too many changes.
To preserve performance only 100 of 227 files are displayed.
pom.xml
| @@ -32,7 +32,7 @@ | @@ -32,7 +32,7 @@ | ||
| 32 | <url>https://github.com/YunaiV/ruoyi-vue-pro</url> | 32 | <url>https://github.com/YunaiV/ruoyi-vue-pro</url> |
| 33 | 33 | ||
| 34 | <properties> | 34 | <properties> |
| 35 | - <revision>2.6.0-SNAPSHOT</revision> | 35 | + <revision>2025.08-SNAPSHOT</revision> |
| 36 | <!-- Maven 相关 --> | 36 | <!-- Maven 相关 --> |
| 37 | <java.version>17</java.version> | 37 | <java.version>17</java.version> |
| 38 | <maven.compiler.source>${java.version}</maven.compiler.source> | 38 | <maven.compiler.source>${java.version}</maven.compiler.source> |
| @@ -88,6 +88,13 @@ | @@ -88,6 +88,13 @@ | ||
| 88 | <version>${lombok.version}</version> | 88 | <version>${lombok.version}</version> |
| 89 | </path> | 89 | </path> |
| 90 | <path> | 90 | <path> |
| 91 | + <!-- 确保 Lombok 生成的 getter/setter 方法能被 MapStruct 正确识别, | ||
| 92 | + 避免出现 No property named “xxx" exists 的编译错误 --> | ||
| 93 | + <groupId>org.projectlombok</groupId> | ||
| 94 | + <artifactId>lombok-mapstruct-binding</artifactId> | ||
| 95 | + <version>0.2.0</version> | ||
| 96 | + </path> | ||
| 97 | + <path> | ||
| 91 | <groupId>org.mapstruct</groupId> | 98 | <groupId>org.mapstruct</groupId> |
| 92 | <artifactId>mapstruct-processor</artifactId> | 99 | <artifactId>mapstruct-processor</artifactId> |
| 93 | <version>${mapstruct.version}</version> | 100 | <version>${mapstruct.version}</version> |
sql/postgresql/quartz.sql
| 1 | --- ---------------------------- | ||
| 2 | --- qrtz_blob_triggers | ||
| 3 | --- ---------------------------- | ||
| 4 | -CREATE TABLE qrtz_blob_triggers | 1 | +-- https://github.com/quartz-scheduler/quartz/blob/main/quartz/src/main/resources/org/quartz/impl/jdbcjobstore/tables_postgres.sql |
| 2 | +-- Thanks to Patrick Lightbody for submitting this... | ||
| 3 | +-- | ||
| 4 | +-- In your Quartz properties file, you'll need to set | ||
| 5 | +-- org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.PostgreSQLDelegate | ||
| 6 | + | ||
| 7 | +DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS; | ||
| 8 | +DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS; | ||
| 9 | +DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE; | ||
| 10 | +DROP TABLE IF EXISTS QRTZ_LOCKS; | ||
| 11 | +DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS; | ||
| 12 | +DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS; | ||
| 13 | +DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS; | ||
| 14 | +DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS; | ||
| 15 | +DROP TABLE IF EXISTS QRTZ_TRIGGERS; | ||
| 16 | +DROP TABLE IF EXISTS QRTZ_JOB_DETAILS; | ||
| 17 | +DROP TABLE IF EXISTS QRTZ_CALENDARS; | ||
| 18 | + | ||
| 19 | +CREATE TABLE QRTZ_JOB_DETAILS | ||
| 5 | ( | 20 | ( |
| 6 | - sched_name varchar(120) NOT NULL, | ||
| 7 | - trigger_name varchar(190) NOT NULL, | ||
| 8 | - trigger_group varchar(190) NOT NULL, | ||
| 9 | - blob_data bytea NULL, | ||
| 10 | - PRIMARY KEY (sched_name, trigger_name, trigger_group) | 21 | + SCHED_NAME VARCHAR(120) NOT NULL, |
| 22 | + JOB_NAME VARCHAR(200) NOT NULL, | ||
| 23 | + JOB_GROUP VARCHAR(200) NOT NULL, | ||
| 24 | + DESCRIPTION VARCHAR(250) NULL, | ||
| 25 | + JOB_CLASS_NAME VARCHAR(250) NOT NULL, | ||
| 26 | + IS_DURABLE BOOL NOT NULL, | ||
| 27 | + IS_NONCONCURRENT BOOL NOT NULL, | ||
| 28 | + IS_UPDATE_DATA BOOL NOT NULL, | ||
| 29 | + REQUESTS_RECOVERY BOOL NOT NULL, | ||
| 30 | + JOB_DATA BYTEA NULL, | ||
| 31 | + PRIMARY KEY (SCHED_NAME, JOB_NAME, JOB_GROUP) | ||
| 11 | ); | 32 | ); |
| 12 | 33 | ||
| 13 | -CREATE INDEX idx_qrtz_blob_triggers_sched_name ON qrtz_blob_triggers (sched_name, trigger_name, trigger_group); | ||
| 14 | - | ||
| 15 | --- ---------------------------- | ||
| 16 | --- qrtz_calendars | ||
| 17 | --- ---------------------------- | ||
| 18 | -CREATE TABLE qrtz_calendars | 34 | +CREATE TABLE QRTZ_TRIGGERS |
| 19 | ( | 35 | ( |
| 20 | - sched_name varchar(120) NOT NULL, | ||
| 21 | - calendar_name varchar(190) NOT NULL, | ||
| 22 | - calendar bytea NOT NULL, | ||
| 23 | - PRIMARY KEY (sched_name, calendar_name) | 36 | + SCHED_NAME VARCHAR(120) NOT NULL, |
| 37 | + TRIGGER_NAME VARCHAR(200) NOT NULL, | ||
| 38 | + TRIGGER_GROUP VARCHAR(200) NOT NULL, | ||
| 39 | + JOB_NAME VARCHAR(200) NOT NULL, | ||
| 40 | + JOB_GROUP VARCHAR(200) NOT NULL, | ||
| 41 | + DESCRIPTION VARCHAR(250) NULL, | ||
| 42 | + NEXT_FIRE_TIME BIGINT NULL, | ||
| 43 | + PREV_FIRE_TIME BIGINT NULL, | ||
| 44 | + PRIORITY INTEGER NULL, | ||
| 45 | + TRIGGER_STATE VARCHAR(16) NOT NULL, | ||
| 46 | + TRIGGER_TYPE VARCHAR(8) NOT NULL, | ||
| 47 | + START_TIME BIGINT NOT NULL, | ||
| 48 | + END_TIME BIGINT NULL, | ||
| 49 | + CALENDAR_NAME VARCHAR(200) NULL, | ||
| 50 | + MISFIRE_INSTR SMALLINT NULL, | ||
| 51 | + JOB_DATA BYTEA NULL, | ||
| 52 | + PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP), | ||
| 53 | + FOREIGN KEY (SCHED_NAME, JOB_NAME, JOB_GROUP) | ||
| 54 | + REFERENCES QRTZ_JOB_DETAILS (SCHED_NAME, JOB_NAME, JOB_GROUP) | ||
| 24 | ); | 55 | ); |
| 25 | 56 | ||
| 26 | - | ||
| 27 | --- ---------------------------- | ||
| 28 | --- qrtz_cron_triggers | ||
| 29 | --- ---------------------------- | ||
| 30 | -CREATE TABLE qrtz_cron_triggers | 57 | +CREATE TABLE QRTZ_SIMPLE_TRIGGERS |
| 31 | ( | 58 | ( |
| 32 | - sched_name varchar(120) NOT NULL, | ||
| 33 | - trigger_name varchar(190) NOT NULL, | ||
| 34 | - trigger_group varchar(190) NOT NULL, | ||
| 35 | - cron_expression varchar(120) NOT NULL, | ||
| 36 | - time_zone_id varchar(80) NULL DEFAULT NULL, | ||
| 37 | - PRIMARY KEY (sched_name, trigger_name, trigger_group) | 59 | + SCHED_NAME VARCHAR(120) NOT NULL, |
| 60 | + TRIGGER_NAME VARCHAR(200) NOT NULL, | ||
| 61 | + TRIGGER_GROUP VARCHAR(200) NOT NULL, | ||
| 62 | + REPEAT_COUNT BIGINT NOT NULL, | ||
| 63 | + REPEAT_INTERVAL BIGINT NOT NULL, | ||
| 64 | + TIMES_TRIGGERED BIGINT NOT NULL, | ||
| 65 | + PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP), | ||
| 66 | + FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP) | ||
| 67 | + REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP) | ||
| 38 | ); | 68 | ); |
| 39 | 69 | ||
| 40 | --- @formatter:off | ||
| 41 | -BEGIN; | ||
| 42 | -COMMIT; | ||
| 43 | --- @formatter:on | ||
| 44 | - | ||
| 45 | --- ---------------------------- | ||
| 46 | --- qrtz_fired_triggers | ||
| 47 | --- ---------------------------- | ||
| 48 | -CREATE TABLE qrtz_fired_triggers | 70 | +CREATE TABLE QRTZ_CRON_TRIGGERS |
| 49 | ( | 71 | ( |
| 50 | - sched_name varchar(120) NOT NULL, | ||
| 51 | - entry_id varchar(95) NOT NULL, | ||
| 52 | - trigger_name varchar(190) NOT NULL, | ||
| 53 | - trigger_group varchar(190) NOT NULL, | ||
| 54 | - instance_name varchar(190) NOT NULL, | ||
| 55 | - fired_time int8 NOT NULL, | ||
| 56 | - sched_time int8 NOT NULL, | ||
| 57 | - priority int4 NOT NULL, | ||
| 58 | - state varchar(16) NOT NULL, | ||
| 59 | - job_name varchar(190) NULL DEFAULT NULL, | ||
| 60 | - job_group varchar(190) NULL DEFAULT NULL, | ||
| 61 | - is_nonconcurrent varchar(1) NULL DEFAULT NULL, | ||
| 62 | - requests_recovery varchar(1) NULL DEFAULT NULL, | ||
| 63 | - PRIMARY KEY (sched_name, entry_id) | 72 | + SCHED_NAME VARCHAR(120) NOT NULL, |
| 73 | + TRIGGER_NAME VARCHAR(200) NOT NULL, | ||
| 74 | + TRIGGER_GROUP VARCHAR(200) NOT NULL, | ||
| 75 | + CRON_EXPRESSION VARCHAR(120) NOT NULL, | ||
| 76 | + TIME_ZONE_ID VARCHAR(80), | ||
| 77 | + PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP), | ||
| 78 | + FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP) | ||
| 79 | + REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP) | ||
| 64 | ); | 80 | ); |
| 65 | 81 | ||
| 66 | -CREATE INDEX idx_qrtz_ft_trig_inst_name ON qrtz_fired_triggers (sched_name, instance_name); | ||
| 67 | -CREATE INDEX idx_qrtz_ft_inst_job_req_rcvry ON qrtz_fired_triggers (sched_name, instance_name, requests_recovery); | ||
| 68 | -CREATE INDEX idx_qrtz_ft_j_g ON qrtz_fired_triggers (sched_name, job_name, job_group); | ||
| 69 | -CREATE INDEX idx_qrtz_ft_jg ON qrtz_fired_triggers (sched_name, job_group); | ||
| 70 | -CREATE INDEX idx_qrtz_ft_t_g ON qrtz_fired_triggers (sched_name, trigger_name, trigger_group); | ||
| 71 | -CREATE INDEX idx_qrtz_ft_tg ON qrtz_fired_triggers (sched_name, trigger_group); | ||
| 72 | - | ||
| 73 | --- ---------------------------- | ||
| 74 | --- qrtz_job_details | ||
| 75 | --- ---------------------------- | ||
| 76 | -CREATE TABLE qrtz_job_details | 82 | +CREATE TABLE QRTZ_SIMPROP_TRIGGERS |
| 77 | ( | 83 | ( |
| 78 | - sched_name varchar(120) NOT NULL, | ||
| 79 | - job_name varchar(190) NOT NULL, | ||
| 80 | - job_group varchar(190) NOT NULL, | ||
| 81 | - description varchar(250) NULL DEFAULT NULL, | ||
| 82 | - job_class_name varchar(250) NOT NULL, | ||
| 83 | - is_durable varchar(1) NOT NULL, | ||
| 84 | - is_nonconcurrent varchar(1) NOT NULL, | ||
| 85 | - is_update_data varchar(1) NOT NULL, | ||
| 86 | - requests_recovery varchar(1) NOT NULL, | ||
| 87 | - job_data bytea NULL, | ||
| 88 | - PRIMARY KEY (sched_name, job_name, job_group) | 84 | + SCHED_NAME VARCHAR(120) NOT NULL, |
| 85 | + TRIGGER_NAME VARCHAR(200) NOT NULL, | ||
| 86 | + TRIGGER_GROUP VARCHAR(200) NOT NULL, | ||
| 87 | + STR_PROP_1 VARCHAR(512) NULL, | ||
| 88 | + STR_PROP_2 VARCHAR(512) NULL, | ||
| 89 | + STR_PROP_3 VARCHAR(512) NULL, | ||
| 90 | + INT_PROP_1 INT NULL, | ||
| 91 | + INT_PROP_2 INT NULL, | ||
| 92 | + LONG_PROP_1 BIGINT NULL, | ||
| 93 | + LONG_PROP_2 BIGINT NULL, | ||
| 94 | + DEC_PROP_1 NUMERIC(13, 4) NULL, | ||
| 95 | + DEC_PROP_2 NUMERIC(13, 4) NULL, | ||
| 96 | + BOOL_PROP_1 BOOL NULL, | ||
| 97 | + BOOL_PROP_2 BOOL NULL, | ||
| 98 | + PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP), | ||
| 99 | + FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP) | ||
| 100 | + REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP) | ||
| 89 | ); | 101 | ); |
| 90 | 102 | ||
| 91 | -CREATE INDEX idx_qrtz_j_req_recovery ON qrtz_job_details (sched_name, requests_recovery); | ||
| 92 | -CREATE INDEX idx_qrtz_j_grp ON qrtz_job_details (sched_name, job_group); | ||
| 93 | - | ||
| 94 | --- @formatter:off | ||
| 95 | -BEGIN; | ||
| 96 | -COMMIT; | ||
| 97 | --- @formatter:on | ||
| 98 | - | ||
| 99 | --- ---------------------------- | ||
| 100 | --- qrtz_locks | ||
| 101 | --- ---------------------------- | ||
| 102 | -CREATE TABLE qrtz_locks | 103 | +CREATE TABLE QRTZ_BLOB_TRIGGERS |
| 103 | ( | 104 | ( |
| 104 | - sched_name varchar(120) NOT NULL, | ||
| 105 | - lock_name varchar(40) NOT NULL, | ||
| 106 | - PRIMARY KEY (sched_name, lock_name) | 105 | + SCHED_NAME VARCHAR(120) NOT NULL, |
| 106 | + TRIGGER_NAME VARCHAR(200) NOT NULL, | ||
| 107 | + TRIGGER_GROUP VARCHAR(200) NOT NULL, | ||
| 108 | + BLOB_DATA BYTEA NULL, | ||
| 109 | + PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP), | ||
| 110 | + FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP) | ||
| 111 | + REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP) | ||
| 107 | ); | 112 | ); |
| 108 | 113 | ||
| 109 | --- @formatter:off | ||
| 110 | -BEGIN; | ||
| 111 | -COMMIT; | ||
| 112 | --- @formatter:on | ||
| 113 | - | ||
| 114 | --- ---------------------------- | ||
| 115 | --- qrtz_paused_trigger_grps | ||
| 116 | --- ---------------------------- | ||
| 117 | -CREATE TABLE qrtz_paused_trigger_grps | 114 | +CREATE TABLE QRTZ_CALENDARS |
| 118 | ( | 115 | ( |
| 119 | - sched_name varchar(120) NOT NULL, | ||
| 120 | - trigger_group varchar(190) NOT NULL, | ||
| 121 | - PRIMARY KEY (sched_name, trigger_group) | 116 | + SCHED_NAME VARCHAR(120) NOT NULL, |
| 117 | + CALENDAR_NAME VARCHAR(200) NOT NULL, | ||
| 118 | + CALENDAR BYTEA NOT NULL, | ||
| 119 | + PRIMARY KEY (SCHED_NAME, CALENDAR_NAME) | ||
| 122 | ); | 120 | ); |
| 123 | 121 | ||
| 124 | --- ---------------------------- | ||
| 125 | --- qrtz_scheduler_state | ||
| 126 | --- ---------------------------- | ||
| 127 | -CREATE TABLE qrtz_scheduler_state | 122 | + |
| 123 | +CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS | ||
| 128 | ( | 124 | ( |
| 129 | - sched_name varchar(120) NOT NULL, | ||
| 130 | - instance_name varchar(190) NOT NULL, | ||
| 131 | - last_checkin_time int8 NOT NULL, | ||
| 132 | - checkin_interval int8 NOT NULL, | ||
| 133 | - PRIMARY KEY (sched_name, instance_name) | 125 | + SCHED_NAME VARCHAR(120) NOT NULL, |
| 126 | + TRIGGER_GROUP VARCHAR(200) NOT NULL, | ||
| 127 | + PRIMARY KEY (SCHED_NAME, TRIGGER_GROUP) | ||
| 134 | ); | 128 | ); |
| 135 | 129 | ||
| 136 | --- @formatter:off | ||
| 137 | -BEGIN; | ||
| 138 | -COMMIT; | ||
| 139 | --- @formatter:on | ||
| 140 | - | ||
| 141 | --- ---------------------------- | ||
| 142 | --- qrtz_simple_triggers | ||
| 143 | --- ---------------------------- | ||
| 144 | -CREATE TABLE qrtz_simple_triggers | 130 | +CREATE TABLE QRTZ_FIRED_TRIGGERS |
| 145 | ( | 131 | ( |
| 146 | - sched_name varchar(120) NOT NULL, | ||
| 147 | - trigger_name varchar(190) NOT NULL, | ||
| 148 | - trigger_group varchar(190) NOT NULL, | ||
| 149 | - repeat_count int8 NOT NULL, | ||
| 150 | - repeat_interval int8 NOT NULL, | ||
| 151 | - times_triggered int8 NOT NULL, | ||
| 152 | - PRIMARY KEY (sched_name, trigger_name, trigger_group) | 132 | + SCHED_NAME VARCHAR(120) NOT NULL, |
| 133 | + ENTRY_ID VARCHAR(95) NOT NULL, | ||
| 134 | + TRIGGER_NAME VARCHAR(200) NOT NULL, | ||
| 135 | + TRIGGER_GROUP VARCHAR(200) NOT NULL, | ||
| 136 | + INSTANCE_NAME VARCHAR(200) NOT NULL, | ||
| 137 | + FIRED_TIME BIGINT NOT NULL, | ||
| 138 | + SCHED_TIME BIGINT NOT NULL, | ||
| 139 | + PRIORITY INTEGER NOT NULL, | ||
| 140 | + STATE VARCHAR(16) NOT NULL, | ||
| 141 | + JOB_NAME VARCHAR(200) NULL, | ||
| 142 | + JOB_GROUP VARCHAR(200) NULL, | ||
| 143 | + IS_NONCONCURRENT BOOL NULL, | ||
| 144 | + REQUESTS_RECOVERY BOOL NULL, | ||
| 145 | + PRIMARY KEY (SCHED_NAME, ENTRY_ID) | ||
| 153 | ); | 146 | ); |
| 154 | 147 | ||
| 155 | --- ---------------------------- | ||
| 156 | --- qrtz_simprop_triggers | ||
| 157 | --- ---------------------------- | ||
| 158 | -CREATE TABLE qrtz_simprop_triggers | 148 | +CREATE TABLE QRTZ_SCHEDULER_STATE |
| 159 | ( | 149 | ( |
| 160 | - sched_name varchar(120) NOT NULL, | ||
| 161 | - trigger_name varchar(190) NOT NULL, | ||
| 162 | - trigger_group varchar(190) NOT NULL, | ||
| 163 | - str_prop_1 varchar(512) NULL DEFAULT NULL, | ||
| 164 | - str_prop_2 varchar(512) NULL DEFAULT NULL, | ||
| 165 | - str_prop_3 varchar(512) NULL DEFAULT NULL, | ||
| 166 | - int_prop_1 int4 NULL DEFAULT NULL, | ||
| 167 | - int_prop_2 int4 NULL DEFAULT NULL, | ||
| 168 | - long_prop_1 int8 NULL DEFAULT NULL, | ||
| 169 | - long_prop_2 int8 NULL DEFAULT NULL, | ||
| 170 | - dec_prop_1 numeric(13, 4) NULL DEFAULT NULL, | ||
| 171 | - dec_prop_2 numeric(13, 4) NULL DEFAULT NULL, | ||
| 172 | - bool_prop_1 varchar(1) NULL DEFAULT NULL, | ||
| 173 | - bool_prop_2 varchar(1) NULL DEFAULT NULL, | ||
| 174 | - PRIMARY KEY (sched_name, trigger_name, trigger_group) | 150 | + SCHED_NAME VARCHAR(120) NOT NULL, |
| 151 | + INSTANCE_NAME VARCHAR(200) NOT NULL, | ||
| 152 | + LAST_CHECKIN_TIME BIGINT NOT NULL, | ||
| 153 | + CHECKIN_INTERVAL BIGINT NOT NULL, | ||
| 154 | + PRIMARY KEY (SCHED_NAME, INSTANCE_NAME) | ||
| 175 | ); | 155 | ); |
| 176 | 156 | ||
| 177 | --- ---------------------------- | ||
| 178 | --- qrtz_triggers | ||
| 179 | --- ---------------------------- | ||
| 180 | -CREATE TABLE qrtz_triggers | 157 | +CREATE TABLE QRTZ_LOCKS |
| 181 | ( | 158 | ( |
| 182 | - sched_name varchar(120) NOT NULL, | ||
| 183 | - trigger_name varchar(190) NOT NULL, | ||
| 184 | - trigger_group varchar(190) NOT NULL, | ||
| 185 | - job_name varchar(190) NOT NULL, | ||
| 186 | - job_group varchar(190) NOT NULL, | ||
| 187 | - description varchar(250) NULL DEFAULT NULL, | ||
| 188 | - next_fire_time int8 NULL DEFAULT NULL, | ||
| 189 | - prev_fire_time int8 NULL DEFAULT NULL, | ||
| 190 | - priority int4 NULL DEFAULT NULL, | ||
| 191 | - trigger_state varchar(16) NOT NULL, | ||
| 192 | - trigger_type varchar(8) NOT NULL, | ||
| 193 | - start_time int8 NOT NULL, | ||
| 194 | - end_time int8 NULL DEFAULT NULL, | ||
| 195 | - calendar_name varchar(190) NULL DEFAULT NULL, | ||
| 196 | - misfire_instr int2 NULL DEFAULT NULL, | ||
| 197 | - job_data bytea NULL, | ||
| 198 | - PRIMARY KEY (sched_name, trigger_name, trigger_group) | 159 | + SCHED_NAME VARCHAR(120) NOT NULL, |
| 160 | + LOCK_NAME VARCHAR(40) NOT NULL, | ||
| 161 | + PRIMARY KEY (SCHED_NAME, LOCK_NAME) | ||
| 199 | ); | 162 | ); |
| 200 | 163 | ||
| 201 | -CREATE INDEX idx_qrtz_t_j ON qrtz_triggers (sched_name, job_name, job_group); | ||
| 202 | -CREATE INDEX idx_qrtz_t_jg ON qrtz_triggers (sched_name, job_group); | ||
| 203 | -CREATE INDEX idx_qrtz_t_c ON qrtz_triggers (sched_name, calendar_name); | ||
| 204 | -CREATE INDEX idx_qrtz_t_g ON qrtz_triggers (sched_name, trigger_group); | ||
| 205 | -CREATE INDEX idx_qrtz_t_state ON qrtz_triggers (sched_name, trigger_state); | ||
| 206 | -CREATE INDEX idx_qrtz_t_n_state ON qrtz_triggers (sched_name, trigger_name, trigger_group, trigger_state); | ||
| 207 | -CREATE INDEX idx_qrtz_t_n_g_state ON qrtz_triggers (sched_name, trigger_group, trigger_state); | ||
| 208 | -CREATE INDEX idx_qrtz_t_next_fire_time ON qrtz_triggers (sched_name, next_fire_time); | ||
| 209 | -CREATE INDEX idx_qrtz_t_nft_st ON qrtz_triggers (sched_name, trigger_state, next_fire_time); | ||
| 210 | -CREATE INDEX idx_qrtz_t_nft_misfire ON qrtz_triggers (sched_name, misfire_instr, next_fire_time); | ||
| 211 | -CREATE INDEX idx_qrtz_t_nft_st_misfire ON qrtz_triggers (sched_name, misfire_instr, next_fire_time, trigger_state); | ||
| 212 | -CREATE INDEX idx_qrtz_t_nft_st_misfire_grp ON qrtz_triggers (sched_name, misfire_instr, next_fire_time, trigger_group, | ||
| 213 | - trigger_state); | ||
| 214 | - | ||
| 215 | --- @formatter:off | ||
| 216 | -BEGIN; | ||
| 217 | -COMMIT; | ||
| 218 | --- @formatter:on | ||
| 219 | - | ||
| 220 | - | ||
| 221 | --- ---------------------------- | ||
| 222 | --- FK: qrtz_blob_triggers | ||
| 223 | --- ---------------------------- | ||
| 224 | -ALTER TABLE qrtz_blob_triggers | ||
| 225 | - ADD CONSTRAINT qrtz_blob_triggers_ibfk_1 FOREIGN KEY (sched_name, trigger_name, trigger_group) REFERENCES qrtz_triggers (sched_name, | ||
| 226 | - trigger_name, | ||
| 227 | - trigger_group); | ||
| 228 | - | ||
| 229 | --- ---------------------------- | ||
| 230 | --- FK: qrtz_cron_triggers | ||
| 231 | --- ---------------------------- | ||
| 232 | -ALTER TABLE qrtz_cron_triggers | ||
| 233 | - ADD CONSTRAINT qrtz_cron_triggers_ibfk_1 FOREIGN KEY (sched_name, trigger_name, trigger_group) REFERENCES qrtz_triggers (sched_name, trigger_name, trigger_group); | ||
| 234 | - | ||
| 235 | --- ---------------------------- | ||
| 236 | --- FK: qrtz_simple_triggers | ||
| 237 | --- ---------------------------- | ||
| 238 | -ALTER TABLE qrtz_simple_triggers | ||
| 239 | - ADD CONSTRAINT qrtz_simple_triggers_ibfk_1 FOREIGN KEY (sched_name, trigger_name, trigger_group) REFERENCES qrtz_triggers (sched_name, | ||
| 240 | - trigger_name, | ||
| 241 | - trigger_group); | ||
| 242 | - | ||
| 243 | --- ---------------------------- | ||
| 244 | --- FK: qrtz_simprop_triggers | ||
| 245 | --- ---------------------------- | ||
| 246 | -ALTER TABLE qrtz_simprop_triggers | ||
| 247 | - ADD CONSTRAINT qrtz_simprop_triggers_ibfk_1 FOREIGN KEY (sched_name, trigger_name, trigger_group) REFERENCES qrtz_triggers (sched_name, trigger_name, trigger_group); | ||
| 248 | - | ||
| 249 | --- ---------------------------- | ||
| 250 | --- FK: qrtz_triggers | ||
| 251 | --- ---------------------------- | ||
| 252 | -ALTER TABLE qrtz_triggers | ||
| 253 | - ADD CONSTRAINT qrtz_triggers_ibfk_1 FOREIGN KEY (sched_name, job_name, job_group) REFERENCES qrtz_job_details (sched_name, job_name, job_group); | 164 | +CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY |
| 165 | + ON QRTZ_JOB_DETAILS (SCHED_NAME, REQUESTS_RECOVERY); | ||
| 166 | +CREATE INDEX IDX_QRTZ_J_GRP | ||
| 167 | + ON QRTZ_JOB_DETAILS (SCHED_NAME, JOB_GROUP); | ||
| 168 | + | ||
| 169 | +CREATE INDEX IDX_QRTZ_T_J | ||
| 170 | + ON QRTZ_TRIGGERS (SCHED_NAME, JOB_NAME, JOB_GROUP); | ||
| 171 | +CREATE INDEX IDX_QRTZ_T_JG | ||
| 172 | + ON QRTZ_TRIGGERS (SCHED_NAME, JOB_GROUP); | ||
| 173 | +CREATE INDEX IDX_QRTZ_T_C | ||
| 174 | + ON QRTZ_TRIGGERS (SCHED_NAME, CALENDAR_NAME); | ||
| 175 | +CREATE INDEX IDX_QRTZ_T_G | ||
| 176 | + ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_GROUP); | ||
| 177 | +CREATE INDEX IDX_QRTZ_T_STATE | ||
| 178 | + ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_STATE); | ||
| 179 | +CREATE INDEX IDX_QRTZ_T_N_STATE | ||
| 180 | + ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP, TRIGGER_STATE); | ||
| 181 | +CREATE INDEX IDX_QRTZ_T_N_G_STATE | ||
| 182 | + ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_GROUP, TRIGGER_STATE); | ||
| 183 | +CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME | ||
| 184 | + ON QRTZ_TRIGGERS (SCHED_NAME, NEXT_FIRE_TIME); | ||
| 185 | +CREATE INDEX IDX_QRTZ_T_NFT_ST | ||
| 186 | + ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_STATE, NEXT_FIRE_TIME); | ||
| 187 | +CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE | ||
| 188 | + ON QRTZ_TRIGGERS (SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME); | ||
| 189 | +CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE | ||
| 190 | + ON QRTZ_TRIGGERS (SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME, TRIGGER_STATE); | ||
| 191 | +CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP | ||
| 192 | + ON QRTZ_TRIGGERS (SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME, TRIGGER_GROUP, TRIGGER_STATE); | ||
| 193 | + | ||
| 194 | +CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME | ||
| 195 | + ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, INSTANCE_NAME); | ||
| 196 | +CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY | ||
| 197 | + ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, INSTANCE_NAME, REQUESTS_RECOVERY); | ||
| 198 | +CREATE INDEX IDX_QRTZ_FT_J_G | ||
| 199 | + ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, JOB_NAME, JOB_GROUP); | ||
| 200 | +CREATE INDEX IDX_QRTZ_FT_JG | ||
| 201 | + ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, JOB_GROUP); | ||
| 202 | +CREATE INDEX IDX_QRTZ_FT_T_G | ||
| 203 | + ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP); | ||
| 204 | +CREATE INDEX IDX_QRTZ_FT_TG | ||
| 205 | + ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, TRIGGER_GROUP); | ||
| 206 | + | ||
| 207 | + | ||
| 208 | +COMMIT; | ||
| 254 | \ No newline at end of file | 209 | \ No newline at end of file |
sql/tools/convertor.py
| @@ -17,6 +17,7 @@ uv run --with simple-ddl-parser convertor.py dm8 > ../dm/ruoyi-vue-pro-dm8.sql | @@ -17,6 +17,7 @@ uv run --with simple-ddl-parser convertor.py dm8 > ../dm/ruoyi-vue-pro-dm8.sql | ||
| 17 | import argparse | 17 | import argparse |
| 18 | import pathlib | 18 | import pathlib |
| 19 | import re | 19 | import re |
| 20 | +import sys | ||
| 20 | import time | 21 | import time |
| 21 | from abc import ABC, abstractmethod | 22 | from abc import ABC, abstractmethod |
| 22 | from typing import Dict, Generator, Optional, Tuple, Union | 23 | from typing import Dict, Generator, Optional, Tuple, Union |
| @@ -293,8 +294,10 @@ class Convertor(ABC): | @@ -293,8 +294,10 @@ class Convertor(ABC): | ||
| 293 | 294 | ||
| 294 | # 将parse失败的脚本打印出来 | 295 | # 将parse失败的脚本打印出来 |
| 295 | if error_scripts: | 296 | if error_scripts: |
| 297 | + print("!!! 以下内容无法正常解析", file=sys.stderr) | ||
| 296 | for script in error_scripts: | 298 | for script in error_scripts: |
| 297 | - print(script) | 299 | + # print to stderr |
| 300 | + print(script, file=sys.stderr) | ||
| 298 | 301 | ||
| 299 | 302 | ||
| 300 | class PostgreSQLConvertor(Convertor): | 303 | class PostgreSQLConvertor(Convertor): |
yudao-dependencies/pom.xml
| @@ -14,7 +14,7 @@ | @@ -14,7 +14,7 @@ | ||
| 14 | <url>https://github.com/YunaiV/ruoyi-vue-pro</url> | 14 | <url>https://github.com/YunaiV/ruoyi-vue-pro</url> |
| 15 | 15 | ||
| 16 | <properties> | 16 | <properties> |
| 17 | - <revision>2.6.0-SNAPSHOT</revision> | 17 | + <revision>2025.08-SNAPSHOT</revision> |
| 18 | <flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version> | 18 | <flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version> |
| 19 | <!-- 统一依赖管理 --> | 19 | <!-- 统一依赖管理 --> |
| 20 | <spring.boot.version>3.4.5</spring.boot.version> | 20 | <spring.boot.version>3.4.5</spring.boot.version> |
| @@ -24,9 +24,9 @@ | @@ -24,9 +24,9 @@ | ||
| 24 | <!-- DB 相关 --> | 24 | <!-- DB 相关 --> |
| 25 | <druid.version>1.2.24</druid.version> | 25 | <druid.version>1.2.24</druid.version> |
| 26 | <mybatis.version>3.5.19</mybatis.version> | 26 | <mybatis.version>3.5.19</mybatis.version> |
| 27 | - <mybatis-plus.version>3.5.10.1</mybatis-plus.version> | 27 | + <mybatis-plus.version>3.5.12</mybatis-plus.version> |
| 28 | + <mybatis-plus-join.version>1.5.4</mybatis-plus-join.version> | ||
| 28 | <dynamic-datasource.version>4.3.1</dynamic-datasource.version> | 29 | <dynamic-datasource.version>4.3.1</dynamic-datasource.version> |
| 29 | - <mybatis-plus-join.version>1.4.13</mybatis-plus-join.version> | ||
| 30 | <easy-trans.version>3.0.6</easy-trans.version> | 30 | <easy-trans.version>3.0.6</easy-trans.version> |
| 31 | <redisson.version>3.41.0</redisson.version> | 31 | <redisson.version>3.41.0</redisson.version> |
| 32 | <dm8.jdbc.version>8.1.3.140</dm8.jdbc.version> | 32 | <dm8.jdbc.version>8.1.3.140</dm8.jdbc.version> |
| @@ -54,7 +54,7 @@ | @@ -54,7 +54,7 @@ | ||
| 54 | <mapstruct.version>1.6.3</mapstruct.version> | 54 | <mapstruct.version>1.6.3</mapstruct.version> |
| 55 | <hutool-5.version>5.8.35</hutool-5.version> | 55 | <hutool-5.version>5.8.35</hutool-5.version> |
| 56 | <hutool-6.version>6.0.0-M19</hutool-6.version> | 56 | <hutool-6.version>6.0.0-M19</hutool-6.version> |
| 57 | - <easyexcel.version>4.0.3</easyexcel.version> | 57 | + <fastexcel.version>1.2.0</fastexcel.version> |
| 58 | <velocity.version>2.4.1</velocity.version> | 58 | <velocity.version>2.4.1</velocity.version> |
| 59 | <fastjson.version>1.2.83</fastjson.version> | 59 | <fastjson.version>1.2.83</fastjson.version> |
| 60 | <guava.version>33.4.8-jre</guava.version> | 60 | <guava.version>33.4.8-jre</guava.version> |
| @@ -69,12 +69,11 @@ | @@ -69,12 +69,11 @@ | ||
| 69 | <pf4j-spring.version>0.9.0</pf4j-spring.version> | 69 | <pf4j-spring.version>0.9.0</pf4j-spring.version> |
| 70 | <vertx.version>4.5.13</vertx.version> | 70 | <vertx.version>4.5.13</vertx.version> |
| 71 | <!-- 三方云服务相关 --> | 71 | <!-- 三方云服务相关 --> |
| 72 | - <commons-io.version>2.17.0</commons-io.version> | ||
| 73 | - <commons-compress.version>1.27.1</commons-compress.version> | ||
| 74 | <awssdk.version>2.30.14</awssdk.version> | 72 | <awssdk.version>2.30.14</awssdk.version> |
| 75 | <justauth.version>1.16.7</justauth.version> | 73 | <justauth.version>1.16.7</justauth.version> |
| 76 | <justauth-starter.version>1.4.0</justauth-starter.version> | 74 | <justauth-starter.version>1.4.0</justauth-starter.version> |
| 77 | - <jimureport.version>1.9.4</jimureport.version> | 75 | + <jimureport.version>2.0.0</jimureport.version> |
| 76 | + <jimubi.version>1.9.5</jimubi.version> | ||
| 78 | <weixin-java.version>4.7.5.B</weixin-java.version> | 77 | <weixin-java.version>4.7.5.B</weixin-java.version> |
| 79 | </properties> | 78 | </properties> |
| 80 | 79 | ||
| @@ -479,20 +478,11 @@ | @@ -479,20 +478,11 @@ | ||
| 479 | </dependency> | 478 | </dependency> |
| 480 | 479 | ||
| 481 | <dependency> | 480 | <dependency> |
| 482 | - <groupId>com.alibaba</groupId> | ||
| 483 | - <artifactId>easyexcel</artifactId> | ||
| 484 | - <version>${easyexcel.version}</version> | ||
| 485 | - </dependency> | ||
| 486 | - <dependency> | ||
| 487 | - <groupId>commons-io</groupId> | ||
| 488 | - <artifactId>commons-io</artifactId> | ||
| 489 | - <version>${commons-io.version}</version> | ||
| 490 | - </dependency> | ||
| 491 | - <dependency> | ||
| 492 | - <groupId>org.apache.commons</groupId> | ||
| 493 | - <artifactId>commons-compress</artifactId> | ||
| 494 | - <version>${commons-compress.version}</version> | 481 | + <groupId>cn.idev.excel</groupId> |
| 482 | + <artifactId>fastexcel</artifactId> | ||
| 483 | + <version>${fastexcel.version}</version> | ||
| 495 | </dependency> | 484 | </dependency> |
| 485 | + | ||
| 496 | <dependency> | 486 | <dependency> |
| 497 | <groupId>org.apache.tika</groupId> | 487 | <groupId>org.apache.tika</groupId> |
| 498 | <artifactId>tika-core</artifactId> <!-- 文件类型的识别 --> | 488 | <artifactId>tika-core</artifactId> <!-- 文件类型的识别 --> |
| @@ -603,7 +593,7 @@ | @@ -603,7 +593,7 @@ | ||
| 603 | <dependency> | 593 | <dependency> |
| 604 | <groupId>org.jeecgframework.jimureport</groupId> | 594 | <groupId>org.jeecgframework.jimureport</groupId> |
| 605 | <artifactId>jimubi-spring-boot3-starter</artifactId> | 595 | <artifactId>jimubi-spring-boot3-starter</artifactId> |
| 606 | - <version>${jimureport.version}</version> | 596 | + <version>${jimubi.version}</version> |
| 607 | <exclusions> | 597 | <exclusions> |
| 608 | <exclusion> | 598 | <exclusion> |
| 609 | <groupId>com.github.jsqlparser</groupId> | 599 | <groupId>com.github.jsqlparser</groupId> |
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/cache/CacheUtils.java
| @@ -15,6 +15,13 @@ import java.util.concurrent.Executors; | @@ -15,6 +15,13 @@ import java.util.concurrent.Executors; | ||
| 15 | public class CacheUtils { | 15 | public class CacheUtils { |
| 16 | 16 | ||
| 17 | /** | 17 | /** |
| 18 | + * 异步刷新的 LoadingCache 最大缓存数量 | ||
| 19 | + * | ||
| 20 | + * @see <a href="">本地缓存 CacheUtils 工具类建议</a> | ||
| 21 | + */ | ||
| 22 | + private static final Integer CACHE_MAX_SIZE = 10000; | ||
| 23 | + | ||
| 24 | + /** | ||
| 18 | * 构建异步刷新的 LoadingCache 对象 | 25 | * 构建异步刷新的 LoadingCache 对象 |
| 19 | * | 26 | * |
| 20 | * 注意:如果你的缓存和 ThreadLocal 有关系,要么自己处理 ThreadLocal 的传递,要么使用 {@link #buildCache(Duration, CacheLoader)} 方法 | 27 | * 注意:如果你的缓存和 ThreadLocal 有关系,要么自己处理 ThreadLocal 的传递,要么使用 {@link #buildCache(Duration, CacheLoader)} 方法 |
| @@ -29,6 +36,7 @@ public class CacheUtils { | @@ -29,6 +36,7 @@ public class CacheUtils { | ||
| 29 | */ | 36 | */ |
| 30 | public static <K, V> LoadingCache<K, V> buildAsyncReloadingCache(Duration duration, CacheLoader<K, V> loader) { | 37 | public static <K, V> LoadingCache<K, V> buildAsyncReloadingCache(Duration duration, CacheLoader<K, V> loader) { |
| 31 | return CacheBuilder.newBuilder() | 38 | return CacheBuilder.newBuilder() |
| 39 | + .maximumSize(CACHE_MAX_SIZE) | ||
| 32 | // 只阻塞当前数据加载线程,其他线程返回旧值 | 40 | // 只阻塞当前数据加载线程,其他线程返回旧值 |
| 33 | .refreshAfterWrite(duration) | 41 | .refreshAfterWrite(duration) |
| 34 | // 通过 asyncReloading 实现全异步加载,包括 refreshAfterWrite 被阻塞的加载线程 | 42 | // 通过 asyncReloading 实现全异步加载,包括 refreshAfterWrite 被阻塞的加载线程 |
| @@ -43,7 +51,11 @@ public class CacheUtils { | @@ -43,7 +51,11 @@ public class CacheUtils { | ||
| 43 | * @return LoadingCache 对象 | 51 | * @return LoadingCache 对象 |
| 44 | */ | 52 | */ |
| 45 | public static <K, V> LoadingCache<K, V> buildCache(Duration duration, CacheLoader<K, V> loader) { | 53 | public static <K, V> LoadingCache<K, V> buildCache(Duration duration, CacheLoader<K, V> loader) { |
| 46 | - return CacheBuilder.newBuilder().refreshAfterWrite(duration).build(loader); | 54 | + return CacheBuilder.newBuilder() |
| 55 | + .maximumSize(CACHE_MAX_SIZE) | ||
| 56 | + // 只阻塞当前数据加载线程,其他线程返回旧值 | ||
| 57 | + .refreshAfterWrite(duration) | ||
| 58 | + .build(loader); | ||
| 47 | } | 59 | } |
| 48 | 60 | ||
| 49 | } | 61 | } |
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java
| @@ -74,30 +74,30 @@ public class DateUtils { | @@ -74,30 +74,30 @@ public class DateUtils { | ||
| 74 | * 创建指定时间 | 74 | * 创建指定时间 |
| 75 | * | 75 | * |
| 76 | * @param year 年 | 76 | * @param year 年 |
| 77 | - * @param mouth 月 | 77 | + * @param month 月 |
| 78 | * @param day 日 | 78 | * @param day 日 |
| 79 | * @return 指定时间 | 79 | * @return 指定时间 |
| 80 | */ | 80 | */ |
| 81 | - public static Date buildTime(int year, int mouth, int day) { | ||
| 82 | - return buildTime(year, mouth, day, 0, 0, 0); | 81 | + public static Date buildTime(int year, int month, int day) { |
| 82 | + return buildTime(year, month, day, 0, 0, 0); | ||
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | /** | 85 | /** |
| 86 | * 创建指定时间 | 86 | * 创建指定时间 |
| 87 | * | 87 | * |
| 88 | * @param year 年 | 88 | * @param year 年 |
| 89 | - * @param mouth 月 | 89 | + * @param month 月 |
| 90 | * @param day 日 | 90 | * @param day 日 |
| 91 | * @param hour 小时 | 91 | * @param hour 小时 |
| 92 | * @param minute 分钟 | 92 | * @param minute 分钟 |
| 93 | * @param second 秒 | 93 | * @param second 秒 |
| 94 | * @return 指定时间 | 94 | * @return 指定时间 |
| 95 | */ | 95 | */ |
| 96 | - public static Date buildTime(int year, int mouth, int day, | 96 | + public static Date buildTime(int year, int month, int day, |
| 97 | int hour, int minute, int second) { | 97 | int hour, int minute, int second) { |
| 98 | Calendar calendar = Calendar.getInstance(); | 98 | Calendar calendar = Calendar.getInstance(); |
| 99 | calendar.set(Calendar.YEAR, year); | 99 | calendar.set(Calendar.YEAR, year); |
| 100 | - calendar.set(Calendar.MONTH, mouth - 1); | 100 | + calendar.set(Calendar.MONTH, month - 1); |
| 101 | calendar.set(Calendar.DAY_OF_MONTH, day); | 101 | calendar.set(Calendar.DAY_OF_MONTH, day); |
| 102 | calendar.set(Calendar.HOUR_OF_DAY, hour); | 102 | calendar.set(Calendar.HOUR_OF_DAY, hour); |
| 103 | calendar.set(Calendar.MINUTE, minute); | 103 | calendar.set(Calendar.MINUTE, minute); |
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java
| @@ -69,17 +69,17 @@ public class LocalDateTimeUtils { | @@ -69,17 +69,17 @@ public class LocalDateTimeUtils { | ||
| 69 | * 创建指定时间 | 69 | * 创建指定时间 |
| 70 | * | 70 | * |
| 71 | * @param year 年 | 71 | * @param year 年 |
| 72 | - * @param mouth 月 | 72 | + * @param month 月 |
| 73 | * @param day 日 | 73 | * @param day 日 |
| 74 | * @return 指定时间 | 74 | * @return 指定时间 |
| 75 | */ | 75 | */ |
| 76 | - public static LocalDateTime buildTime(int year, int mouth, int day) { | ||
| 77 | - return LocalDateTime.of(year, mouth, day, 0, 0, 0); | 76 | + public static LocalDateTime buildTime(int year, int month, int day) { |
| 77 | + return LocalDateTime.of(year, month, day, 0, 0, 0); | ||
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | - public static LocalDateTime[] buildBetweenTime(int year1, int mouth1, int day1, | ||
| 81 | - int year2, int mouth2, int day2) { | ||
| 82 | - return new LocalDateTime[]{buildTime(year1, mouth1, day1), buildTime(year2, mouth2, day2)}; | 80 | + public static LocalDateTime[] buildBetweenTime(int year1, int month1, int day1, |
| 81 | + int year2, int month2, int day2) { | ||
| 82 | + return new LocalDateTime[]{buildTime(year1, month1, day1), buildTime(year2, month2, day2)}; | ||
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | /** | 85 | /** |
yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/config/YudaoDeptDataPermissionAutoConfiguration.java
| @@ -18,7 +18,7 @@ import java.util.List; | @@ -18,7 +18,7 @@ import java.util.List; | ||
| 18 | */ | 18 | */ |
| 19 | @AutoConfiguration | 19 | @AutoConfiguration |
| 20 | @ConditionalOnClass(LoginUser.class) | 20 | @ConditionalOnClass(LoginUser.class) |
| 21 | -@ConditionalOnBean(value = {PermissionCommonApi.class, DeptDataPermissionRuleCustomizer.class}) | 21 | +@ConditionalOnBean(value = {DeptDataPermissionRuleCustomizer.class}) |
| 22 | public class YudaoDeptDataPermissionAutoConfiguration { | 22 | public class YudaoDeptDataPermissionAutoConfiguration { |
| 23 | 23 | ||
| 24 | @Bean | 24 | @Bean |
yudao-framework/yudao-spring-boot-starter-excel/pom.xml
| @@ -42,18 +42,19 @@ | @@ -42,18 +42,19 @@ | ||
| 42 | 42 | ||
| 43 | <!-- 工具类相关 --> | 43 | <!-- 工具类相关 --> |
| 44 | <dependency> | 44 | <dependency> |
| 45 | - <groupId>com.alibaba</groupId> | ||
| 46 | - <artifactId>easyexcel</artifactId> | 45 | + <groupId>cn.idev.excel</groupId> |
| 46 | + <artifactId>fastexcel</artifactId> | ||
| 47 | </dependency> | 47 | </dependency> |
| 48 | 48 | ||
| 49 | <dependency> | 49 | <dependency> |
| 50 | - <groupId>com.google.guava</groupId> | ||
| 51 | - <artifactId>guava</artifactId> | 50 | + <groupId>jakarta.validation</groupId> |
| 51 | + <artifactId>jakarta.validation-api</artifactId> | ||
| 52 | + <scope>provided</scope> | ||
| 52 | </dependency> | 53 | </dependency> |
| 53 | 54 | ||
| 54 | <dependency> | 55 | <dependency> |
| 55 | - <groupId>org.apache.commons</groupId> | ||
| 56 | - <artifactId>commons-compress</artifactId> <!-- 解决 https://github.com/alibaba/easyexcel/issues/3954 问题 --> | 56 | + <groupId>com.google.guava</groupId> |
| 57 | + <artifactId>guava</artifactId> | ||
| 57 | </dependency> | 58 | </dependency> |
| 58 | 59 | ||
| 59 | <dependency> | 60 | <dependency> |
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/core/DictFrameworkUtils.java
| @@ -76,4 +76,9 @@ public class DictFrameworkUtils { | @@ -76,4 +76,9 @@ public class DictFrameworkUtils { | ||
| 76 | return dictData!= null ? dictData.getValue(): null; | 76 | return dictData!= null ? dictData.getValue(): null; |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | + @SneakyThrows | ||
| 80 | + public static List<String> getDictDataValueList(String dictType) { | ||
| 81 | + List<DictDataRespDTO> dictDatas = GET_DICT_DATA_CACHE.get(dictType); | ||
| 82 | + return convertList(dictDatas, DictDataRespDTO::getValue); | ||
| 83 | + } | ||
| 79 | } | 84 | } |
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/validation/InDict.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.dict.validation; | ||
| 2 | + | ||
| 3 | +import jakarta.validation.Constraint; | ||
| 4 | +import jakarta.validation.Payload; | ||
| 5 | + | ||
| 6 | +import java.lang.annotation.Documented; | ||
| 7 | +import java.lang.annotation.ElementType; | ||
| 8 | +import java.lang.annotation.Retention; | ||
| 9 | +import java.lang.annotation.RetentionPolicy; | ||
| 10 | +import java.lang.annotation.Target; | ||
| 11 | + | ||
| 12 | +@Target({ | ||
| 13 | + ElementType.METHOD, | ||
| 14 | + ElementType.FIELD, | ||
| 15 | + ElementType.ANNOTATION_TYPE, | ||
| 16 | + ElementType.CONSTRUCTOR, | ||
| 17 | + ElementType.PARAMETER, | ||
| 18 | + ElementType.TYPE_USE | ||
| 19 | +}) | ||
| 20 | +@Retention(RetentionPolicy.RUNTIME) | ||
| 21 | +@Documented | ||
| 22 | +@Constraint( | ||
| 23 | + validatedBy = {InDictValidator.class, InDictCollectionValidator.class} | ||
| 24 | +) | ||
| 25 | +public @interface InDict { | ||
| 26 | + | ||
| 27 | + /** | ||
| 28 | + * 数据字典 type | ||
| 29 | + */ | ||
| 30 | + String type(); | ||
| 31 | + | ||
| 32 | + String message() default "必须在指定范围 {value}"; | ||
| 33 | + | ||
| 34 | + Class<?>[] groups() default {}; | ||
| 35 | + | ||
| 36 | + Class<? extends Payload>[] payload() default {}; | ||
| 37 | + | ||
| 38 | +} |
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/validation/InDictCollectionValidator.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.dict.validation; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.collection.CollUtil; | ||
| 4 | +import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils; | ||
| 5 | +import jakarta.validation.ConstraintValidator; | ||
| 6 | +import jakarta.validation.ConstraintValidatorContext; | ||
| 7 | + | ||
| 8 | +import java.util.Collection; | ||
| 9 | +import java.util.List; | ||
| 10 | + | ||
| 11 | +public class InDictCollectionValidator implements ConstraintValidator<InDict, Collection<?>> { | ||
| 12 | + | ||
| 13 | + private String dictType; | ||
| 14 | + | ||
| 15 | + @Override | ||
| 16 | + public void initialize(InDict annotation) { | ||
| 17 | + this.dictType = annotation.type(); | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + @Override | ||
| 21 | + public boolean isValid(Collection<?> list, ConstraintValidatorContext context) { | ||
| 22 | + // 为空时,默认不校验,即认为通过 | ||
| 23 | + if (CollUtil.isEmpty(list)) { | ||
| 24 | + return true; | ||
| 25 | + } | ||
| 26 | + // 校验全部通过 | ||
| 27 | + List<String> dbValues = DictFrameworkUtils.getDictDataValueList(dictType); | ||
| 28 | + boolean match = list.stream().allMatch(v -> dbValues.stream() | ||
| 29 | + .anyMatch(dbValue -> dbValue.equalsIgnoreCase(v.toString()))); | ||
| 30 | + if (match) { | ||
| 31 | + return true; | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + // 校验不通过,自定义提示语句 | ||
| 35 | + context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值 | ||
| 36 | + context.buildConstraintViolationWithTemplate( | ||
| 37 | + context.getDefaultConstraintMessageTemplate().replaceAll("\\{value}", dbValues.toString()) | ||
| 38 | + ).addConstraintViolation(); // 重新添加错误提示语句 | ||
| 39 | + return false; | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | +} | ||
| 43 | + |
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/validation/InDictValidator.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.dict.validation; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.util.StrUtil; | ||
| 4 | +import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils; | ||
| 5 | +import jakarta.validation.ConstraintValidator; | ||
| 6 | +import jakarta.validation.ConstraintValidatorContext; | ||
| 7 | + | ||
| 8 | +import java.util.List; | ||
| 9 | + | ||
| 10 | +public class InDictValidator implements ConstraintValidator<InDict, Object> { | ||
| 11 | + | ||
| 12 | + private String dictType; | ||
| 13 | + | ||
| 14 | + @Override | ||
| 15 | + public void initialize(InDict annotation) { | ||
| 16 | + this.dictType = annotation.type(); | ||
| 17 | + } | ||
| 18 | + | ||
| 19 | + @Override | ||
| 20 | + public boolean isValid(Object value, ConstraintValidatorContext context) { | ||
| 21 | + // 为空时,默认不校验,即认为通过 | ||
| 22 | + if (value == null) { | ||
| 23 | + return true; | ||
| 24 | + } | ||
| 25 | + // 校验通过 | ||
| 26 | + final List<String> values = DictFrameworkUtils.getDictDataValueList(dictType); | ||
| 27 | + boolean match = values.stream().anyMatch(v -> StrUtil.equalsIgnoreCase(v, value.toString())); | ||
| 28 | + if (match) { | ||
| 29 | + return true; | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + // 校验不通过,自定义提示语句 | ||
| 33 | + context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值 | ||
| 34 | + context.buildConstraintViolationWithTemplate( | ||
| 35 | + context.getDefaultConstraintMessageTemplate().replaceAll("\\{value}", values.toString()) | ||
| 36 | + ).addConstraintViolation(); // 重新添加错误提示语句 | ||
| 37 | + return false; | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | +} | ||
| 41 | + |
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/AreaConvert.java
| @@ -3,11 +3,11 @@ package cn.iocoder.yudao.framework.excel.core.convert; | @@ -3,11 +3,11 @@ package cn.iocoder.yudao.framework.excel.core.convert; | ||
| 3 | import cn.hutool.core.convert.Convert; | 3 | import cn.hutool.core.convert.Convert; |
| 4 | import cn.iocoder.yudao.framework.ip.core.Area; | 4 | import cn.iocoder.yudao.framework.ip.core.Area; |
| 5 | import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; | 5 | import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; |
| 6 | -import com.alibaba.excel.converters.Converter; | ||
| 7 | -import com.alibaba.excel.enums.CellDataTypeEnum; | ||
| 8 | -import com.alibaba.excel.metadata.GlobalConfiguration; | ||
| 9 | -import com.alibaba.excel.metadata.data.ReadCellData; | ||
| 10 | -import com.alibaba.excel.metadata.property.ExcelContentProperty; | 6 | +import cn.idev.excel.converters.Converter; |
| 7 | +import cn.idev.excel.enums.CellDataTypeEnum; | ||
| 8 | +import cn.idev.excel.metadata.GlobalConfiguration; | ||
| 9 | +import cn.idev.excel.metadata.data.ReadCellData; | ||
| 10 | +import cn.idev.excel.metadata.property.ExcelContentProperty; | ||
| 11 | import lombok.extern.slf4j.Slf4j; | 11 | import lombok.extern.slf4j.Slf4j; |
| 12 | 12 | ||
| 13 | /** | 13 | /** |
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/DictConvert.java
| @@ -3,12 +3,12 @@ package cn.iocoder.yudao.framework.excel.core.convert; | @@ -3,12 +3,12 @@ package cn.iocoder.yudao.framework.excel.core.convert; | ||
| 3 | import cn.hutool.core.convert.Convert; | 3 | import cn.hutool.core.convert.Convert; |
| 4 | import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils; | 4 | import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils; |
| 5 | import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; | 5 | import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; |
| 6 | -import com.alibaba.excel.converters.Converter; | ||
| 7 | -import com.alibaba.excel.enums.CellDataTypeEnum; | ||
| 8 | -import com.alibaba.excel.metadata.GlobalConfiguration; | ||
| 9 | -import com.alibaba.excel.metadata.data.ReadCellData; | ||
| 10 | -import com.alibaba.excel.metadata.data.WriteCellData; | ||
| 11 | -import com.alibaba.excel.metadata.property.ExcelContentProperty; | 6 | +import cn.idev.excel.converters.Converter; |
| 7 | +import cn.idev.excel.enums.CellDataTypeEnum; | ||
| 8 | +import cn.idev.excel.metadata.GlobalConfiguration; | ||
| 9 | +import cn.idev.excel.metadata.data.ReadCellData; | ||
| 10 | +import cn.idev.excel.metadata.data.WriteCellData; | ||
| 11 | +import cn.idev.excel.metadata.property.ExcelContentProperty; | ||
| 12 | import lombok.extern.slf4j.Slf4j; | 12 | import lombok.extern.slf4j.Slf4j; |
| 13 | 13 | ||
| 14 | /** | 14 | /** |
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/JsonConvert.java
| 1 | package cn.iocoder.yudao.framework.excel.core.convert; | 1 | package cn.iocoder.yudao.framework.excel.core.convert; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | 3 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 4 | -import com.alibaba.excel.converters.Converter; | ||
| 5 | -import com.alibaba.excel.enums.CellDataTypeEnum; | ||
| 6 | -import com.alibaba.excel.metadata.GlobalConfiguration; | ||
| 7 | -import com.alibaba.excel.metadata.data.WriteCellData; | ||
| 8 | -import com.alibaba.excel.metadata.property.ExcelContentProperty; | 4 | +import cn.idev.excel.converters.Converter; |
| 5 | +import cn.idev.excel.enums.CellDataTypeEnum; | ||
| 6 | +import cn.idev.excel.metadata.GlobalConfiguration; | ||
| 7 | +import cn.idev.excel.metadata.data.WriteCellData; | ||
| 8 | +import cn.idev.excel.metadata.property.ExcelContentProperty; | ||
| 9 | 9 | ||
| 10 | /** | 10 | /** |
| 11 | * Excel Json 转换器 | 11 | * Excel Json 转换器 |
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/MoneyConvert.java
| 1 | package cn.iocoder.yudao.framework.excel.core.convert; | 1 | package cn.iocoder.yudao.framework.excel.core.convert; |
| 2 | 2 | ||
| 3 | -import com.alibaba.excel.converters.Converter; | ||
| 4 | -import com.alibaba.excel.enums.CellDataTypeEnum; | ||
| 5 | -import com.alibaba.excel.metadata.GlobalConfiguration; | ||
| 6 | -import com.alibaba.excel.metadata.data.WriteCellData; | ||
| 7 | -import com.alibaba.excel.metadata.property.ExcelContentProperty; | 3 | +import cn.idev.excel.converters.Converter; |
| 4 | +import cn.idev.excel.enums.CellDataTypeEnum; | ||
| 5 | +import cn.idev.excel.metadata.GlobalConfiguration; | ||
| 6 | +import cn.idev.excel.metadata.data.WriteCellData; | ||
| 7 | +import cn.idev.excel.metadata.property.ExcelContentProperty; | ||
| 8 | 8 | ||
| 9 | import java.math.BigDecimal; | 9 | import java.math.BigDecimal; |
| 10 | import java.math.RoundingMode; | 10 | import java.math.RoundingMode; |
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/ColumnWidthMatchStyleStrategy.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.excel.core.handler; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.collection.CollUtil; | ||
| 4 | +import cn.idev.excel.enums.CellDataTypeEnum; | ||
| 5 | +import cn.idev.excel.metadata.Head; | ||
| 6 | +import cn.idev.excel.metadata.data.WriteCellData; | ||
| 7 | +import cn.idev.excel.util.MapUtils; | ||
| 8 | +import cn.idev.excel.write.metadata.holder.WriteSheetHolder; | ||
| 9 | +import cn.idev.excel.write.style.column.AbstractColumnWidthStyleStrategy; | ||
| 10 | +import cn.idev.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; | ||
| 11 | +import org.apache.poi.ss.usermodel.Cell; | ||
| 12 | + | ||
| 13 | +import java.util.HashMap; | ||
| 14 | +import java.util.List; | ||
| 15 | +import java.util.Map; | ||
| 16 | + | ||
| 17 | +/** | ||
| 18 | + * Excel 自适应列宽处理器 | ||
| 19 | + * | ||
| 20 | + * 相比 {@link LongestMatchColumnWidthStyleStrategy} 来说,额外处理了 DATE 类型! | ||
| 21 | + * | ||
| 22 | + * @see <a href="https://github.com/YunaiV/yudao-cloud/pull/196/">添加自适应列宽处理器,并替换默认列宽策略</a> | ||
| 23 | + * @author hmb | ||
| 24 | + */ | ||
| 25 | +public class ColumnWidthMatchStyleStrategy extends AbstractColumnWidthStyleStrategy { | ||
| 26 | + | ||
| 27 | + private static final int MAX_COLUMN_WIDTH = 255; | ||
| 28 | + | ||
| 29 | + private final Map<Integer, Map<Integer, Integer>> cache = MapUtils.newHashMapWithExpectedSize(8); | ||
| 30 | + | ||
| 31 | + @Override | ||
| 32 | + protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell, | ||
| 33 | + Head head, Integer relativeRowIndex, Boolean isHead) { | ||
| 34 | + boolean needSetWidth = isHead || CollUtil.isNotEmpty(cellDataList); | ||
| 35 | + if (!needSetWidth) { | ||
| 36 | + return; | ||
| 37 | + } | ||
| 38 | + Map<Integer, Integer> maxColumnWidthMap = cache.computeIfAbsent(writeSheetHolder.getSheetNo(), | ||
| 39 | + key -> new HashMap<>(16)); | ||
| 40 | + Integer columnWidth = dataLength(cellDataList, cell, isHead); | ||
| 41 | + if (columnWidth < 0) { | ||
| 42 | + return; | ||
| 43 | + } | ||
| 44 | + if (columnWidth > MAX_COLUMN_WIDTH) { | ||
| 45 | + columnWidth = MAX_COLUMN_WIDTH; | ||
| 46 | + } | ||
| 47 | + Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex()); | ||
| 48 | + if (maxColumnWidth == null || columnWidth > maxColumnWidth) { | ||
| 49 | + maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth); | ||
| 50 | + writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256); | ||
| 51 | + } | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + @SuppressWarnings("EnhancedSwitchMigration") | ||
| 55 | + private Integer dataLength(List<WriteCellData<?>> cellDataList, Cell cell, Boolean isHead) { | ||
| 56 | + if (isHead) { | ||
| 57 | + return cell.getStringCellValue().getBytes().length; | ||
| 58 | + } | ||
| 59 | + WriteCellData<?> cellData = cellDataList.get(0); | ||
| 60 | + CellDataTypeEnum type = cellData.getType(); | ||
| 61 | + if (type == null) { | ||
| 62 | + return -1; | ||
| 63 | + } | ||
| 64 | + switch (type) { | ||
| 65 | + case STRING: | ||
| 66 | + return cellData.getStringValue().getBytes().length; | ||
| 67 | + case BOOLEAN: | ||
| 68 | + return cellData.getBooleanValue().toString().getBytes().length; | ||
| 69 | + case NUMBER: | ||
| 70 | + return cellData.getNumberValue().toString().getBytes().length; | ||
| 71 | + case DATE: | ||
| 72 | + return cellData.getDateValue().toString().getBytes().length; | ||
| 73 | + default: | ||
| 74 | + return -1; | ||
| 75 | + } | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | +} | ||
| 0 | \ No newline at end of file | 79 | \ No newline at end of file |
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java
| @@ -3,7 +3,6 @@ package cn.iocoder.yudao.framework.excel.core.handler; | @@ -3,7 +3,6 @@ package cn.iocoder.yudao.framework.excel.core.handler; | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | import cn.hutool.core.lang.Assert; | 4 | import cn.hutool.core.lang.Assert; |
| 5 | import cn.hutool.core.util.ObjectUtil; | 5 | import cn.hutool.core.util.ObjectUtil; |
| 6 | -import cn.hutool.core.util.ReflectUtil; | ||
| 7 | import cn.hutool.core.util.StrUtil; | 6 | import cn.hutool.core.util.StrUtil; |
| 8 | import cn.hutool.extra.spring.SpringUtil; | 7 | import cn.hutool.extra.spring.SpringUtil; |
| 9 | import cn.hutool.poi.excel.ExcelUtil; | 8 | import cn.hutool.poi.excel.ExcelUtil; |
| @@ -11,12 +10,12 @@ import cn.iocoder.yudao.framework.common.core.KeyValue; | @@ -11,12 +10,12 @@ import cn.iocoder.yudao.framework.common.core.KeyValue; | ||
| 11 | import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils; | 10 | import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils; |
| 12 | import cn.iocoder.yudao.framework.excel.core.annotations.ExcelColumnSelect; | 11 | import cn.iocoder.yudao.framework.excel.core.annotations.ExcelColumnSelect; |
| 13 | import cn.iocoder.yudao.framework.excel.core.function.ExcelColumnSelectFunction; | 12 | import cn.iocoder.yudao.framework.excel.core.function.ExcelColumnSelectFunction; |
| 14 | -import com.alibaba.excel.annotation.ExcelIgnore; | ||
| 15 | -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; | ||
| 16 | -import com.alibaba.excel.annotation.ExcelProperty; | ||
| 17 | -import com.alibaba.excel.write.handler.SheetWriteHandler; | ||
| 18 | -import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; | ||
| 19 | -import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; | 13 | +import cn.idev.excel.annotation.ExcelIgnore; |
| 14 | +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; | ||
| 15 | +import cn.idev.excel.annotation.ExcelProperty; | ||
| 16 | +import cn.idev.excel.write.handler.SheetWriteHandler; | ||
| 17 | +import cn.idev.excel.write.metadata.holder.WriteSheetHolder; | ||
| 18 | +import cn.idev.excel.write.metadata.holder.WriteWorkbookHolder; | ||
| 20 | import lombok.extern.slf4j.Slf4j; | 19 | import lombok.extern.slf4j.Slf4j; |
| 21 | import org.apache.poi.hssf.usermodel.HSSFDataValidation; | 20 | import org.apache.poi.hssf.usermodel.HSSFDataValidation; |
| 22 | import org.apache.poi.ss.usermodel.*; | 21 | import org.apache.poi.ss.usermodel.*; |
| @@ -87,7 +86,7 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { | @@ -87,7 +86,7 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { | ||
| 87 | 86 | ||
| 88 | /** | 87 | /** |
| 89 | * 判断字段是否是静态的、最终的、 transient 的 | 88 | * 判断字段是否是静态的、最终的、 transient 的 |
| 90 | - * 原因:EasyExcel 默认是忽略 static final 或 transient 的字段,所以需要判断 | 89 | + * 原因:FastExcel 默认是忽略 static final 或 transient 的字段,所以需要判断 |
| 91 | * | 90 | * |
| 92 | * @param field 字段 | 91 | * @param field 字段 |
| 93 | * @return 是否是静态的、最终的、transient 的 | 92 | * @return 是否是静态的、最终的、transient 的 |
| @@ -185,4 +184,4 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { | @@ -185,4 +184,4 @@ public class SelectSheetWriteHandler implements SheetWriteHandler { | ||
| 185 | writeSheetHolder.getSheet().addValidationData(validation); | 184 | writeSheetHolder.getSheet().addValidationData(validation); |
| 186 | } | 185 | } |
| 187 | 186 | ||
| 188 | -} | ||
| 189 | \ No newline at end of file | 187 | \ No newline at end of file |
| 188 | +} |
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java
| 1 | package cn.iocoder.yudao.framework.excel.core.util; | 1 | package cn.iocoder.yudao.framework.excel.core.util; |
| 2 | 2 | ||
| 3 | +import cn.idev.excel.FastExcelFactory; | ||
| 4 | +import cn.idev.excel.converters.longconverter.LongStringConverter; | ||
| 3 | import cn.iocoder.yudao.framework.common.util.http.HttpUtils; | 5 | import cn.iocoder.yudao.framework.common.util.http.HttpUtils; |
| 6 | +import cn.iocoder.yudao.framework.excel.core.handler.ColumnWidthMatchStyleStrategy; | ||
| 4 | import cn.iocoder.yudao.framework.excel.core.handler.SelectSheetWriteHandler; | 7 | import cn.iocoder.yudao.framework.excel.core.handler.SelectSheetWriteHandler; |
| 5 | -import com.alibaba.excel.EasyExcel; | ||
| 6 | -import com.alibaba.excel.converters.longconverter.LongStringConverter; | ||
| 7 | -import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; | ||
| 8 | import jakarta.servlet.http.HttpServletResponse; | 8 | import jakarta.servlet.http.HttpServletResponse; |
| 9 | import org.springframework.web.multipart.MultipartFile; | 9 | import org.springframework.web.multipart.MultipartFile; |
| 10 | 10 | ||
| @@ -32,9 +32,9 @@ public class ExcelUtils { | @@ -32,9 +32,9 @@ public class ExcelUtils { | ||
| 32 | public static <T> void write(HttpServletResponse response, String filename, String sheetName, | 32 | public static <T> void write(HttpServletResponse response, String filename, String sheetName, |
| 33 | Class<T> head, List<T> data) throws IOException { | 33 | Class<T> head, List<T> data) throws IOException { |
| 34 | // 输出 Excel | 34 | // 输出 Excel |
| 35 | - EasyExcel.write(response.getOutputStream(), head) | 35 | + FastExcelFactory.write(response.getOutputStream(), head) |
| 36 | .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 | 36 | .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 |
| 37 | - .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度 | 37 | + .registerWriteHandler(new ColumnWidthMatchStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度 |
| 38 | .registerWriteHandler(new SelectSheetWriteHandler(head)) // 基于固定 sheet 实现下拉框 | 38 | .registerWriteHandler(new SelectSheetWriteHandler(head)) // 基于固定 sheet 实现下拉框 |
| 39 | .registerConverter(new LongStringConverter()) // 避免 Long 类型丢失精度 | 39 | .registerConverter(new LongStringConverter()) // 避免 Long 类型丢失精度 |
| 40 | .sheet(sheetName).doWrite(data); | 40 | .sheet(sheetName).doWrite(data); |
| @@ -44,7 +44,7 @@ public class ExcelUtils { | @@ -44,7 +44,7 @@ public class ExcelUtils { | ||
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | public static <T> List<T> read(MultipartFile file, Class<T> head) throws IOException { | 46 | public static <T> List<T> read(MultipartFile file, Class<T> head) throws IOException { |
| 47 | - return EasyExcel.read(file.getInputStream(), head, null) | 47 | + return FastExcelFactory.read(file.getInputStream(), head, null) |
| 48 | .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 | 48 | .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 |
| 49 | .doReadAllSync(); | 49 | .doReadAllSync(); |
| 50 | } | 50 | } |
yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/package-info.java
yudao-framework/yudao-spring-boot-starter-job/src/main/java/cn/iocoder/yudao/framework/quartz/config/YudaoAsyncAutoConfiguration.java
| @@ -7,6 +7,7 @@ import org.springframework.boot.autoconfigure.AutoConfiguration; | @@ -7,6 +7,7 @@ import org.springframework.boot.autoconfigure.AutoConfiguration; | ||
| 7 | import org.springframework.context.annotation.Bean; | 7 | import org.springframework.context.annotation.Bean; |
| 8 | import org.springframework.scheduling.annotation.EnableAsync; | 8 | import org.springframework.scheduling.annotation.EnableAsync; |
| 9 | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; | 9 | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; |
| 10 | +import org.springframework.core.task.SimpleAsyncTaskExecutor; | ||
| 10 | 11 | ||
| 11 | /** | 12 | /** |
| 12 | * 异步任务 Configuration | 13 | * 异步任务 Configuration |
| @@ -21,13 +22,20 @@ public class YudaoAsyncAutoConfiguration { | @@ -21,13 +22,20 @@ public class YudaoAsyncAutoConfiguration { | ||
| 21 | 22 | ||
| 22 | @Override | 23 | @Override |
| 23 | public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { | 24 | public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { |
| 24 | - if (!(bean instanceof ThreadPoolTaskExecutor)) { | ||
| 25 | - return bean; | 25 | + // 处理 ThreadPoolTaskExecutor |
| 26 | + if (bean instanceof ThreadPoolTaskExecutor) { | ||
| 27 | + ThreadPoolTaskExecutor executor = (ThreadPoolTaskExecutor) bean; | ||
| 28 | + executor.setTaskDecorator(TtlRunnable::get); | ||
| 29 | + return executor; | ||
| 26 | } | 30 | } |
| 27 | - // 修改提交的任务,接入 TransmittableThreadLocal | ||
| 28 | - ThreadPoolTaskExecutor executor = (ThreadPoolTaskExecutor) bean; | ||
| 29 | - executor.setTaskDecorator(TtlRunnable::get); | ||
| 30 | - return executor; | 31 | + // 处理 SimpleAsyncTaskExecutor |
| 32 | + // 参考 https://t.zsxq.com/CBoks 增加 | ||
| 33 | + if (bean instanceof SimpleAsyncTaskExecutor) { | ||
| 34 | + SimpleAsyncTaskExecutor executor = (SimpleAsyncTaskExecutor) bean; | ||
| 35 | + executor.setTaskDecorator(TtlRunnable::get); | ||
| 36 | + return executor; | ||
| 37 | + } | ||
| 38 | + return bean; | ||
| 31 | } | 39 | } |
| 32 | 40 | ||
| 33 | }; | 41 | }; |
yudao-framework/yudao-spring-boot-starter-monitor/pom.xml
| @@ -44,29 +44,35 @@ | @@ -44,29 +44,35 @@ | ||
| 44 | <dependency> | 44 | <dependency> |
| 45 | <groupId>io.opentracing</groupId> | 45 | <groupId>io.opentracing</groupId> |
| 46 | <artifactId>opentracing-util</artifactId> | 46 | <artifactId>opentracing-util</artifactId> |
| 47 | + <optional>true</optional> | ||
| 47 | </dependency> | 48 | </dependency> |
| 48 | <dependency> | 49 | <dependency> |
| 49 | <groupId>org.apache.skywalking</groupId> | 50 | <groupId>org.apache.skywalking</groupId> |
| 50 | <artifactId>apm-toolkit-trace</artifactId> | 51 | <artifactId>apm-toolkit-trace</artifactId> |
| 52 | + <optional>true</optional> | ||
| 51 | </dependency> | 53 | </dependency> |
| 52 | <dependency> | 54 | <dependency> |
| 53 | <groupId>org.apache.skywalking</groupId> | 55 | <groupId>org.apache.skywalking</groupId> |
| 54 | <artifactId>apm-toolkit-logback-1.x</artifactId> | 56 | <artifactId>apm-toolkit-logback-1.x</artifactId> |
| 57 | + <optional>true</optional> | ||
| 55 | </dependency> | 58 | </dependency> |
| 56 | <dependency> | 59 | <dependency> |
| 57 | <groupId>org.apache.skywalking</groupId> | 60 | <groupId>org.apache.skywalking</groupId> |
| 58 | <artifactId>apm-toolkit-opentracing</artifactId> | 61 | <artifactId>apm-toolkit-opentracing</artifactId> |
| 62 | + <optional>true</optional> | ||
| 59 | </dependency> | 63 | </dependency> |
| 60 | 64 | ||
| 61 | <!-- Micrometer 对 Prometheus 的支持 --> | 65 | <!-- Micrometer 对 Prometheus 的支持 --> |
| 62 | <dependency> | 66 | <dependency> |
| 63 | <groupId>io.micrometer</groupId> | 67 | <groupId>io.micrometer</groupId> |
| 64 | <artifactId>micrometer-registry-prometheus</artifactId> | 68 | <artifactId>micrometer-registry-prometheus</artifactId> |
| 69 | + <optional>true</optional> | ||
| 65 | </dependency> | 70 | </dependency> |
| 66 | 71 | ||
| 67 | <dependency> | 72 | <dependency> |
| 68 | <groupId>de.codecentric</groupId> | 73 | <groupId>de.codecentric</groupId> |
| 69 | - <artifactId>spring-boot-admin-starter-client</artifactId> <!-- 实现 Spring Boot Admin Server 服务端 --> | 74 | + <artifactId>spring-boot-admin-starter-client</artifactId> <!-- 实现 Spring Boot Admin Client 客户端 --> |
| 75 | + <optional>true</optional> | ||
| 70 | </dependency> | 76 | </dependency> |
| 71 | </dependencies> | 77 | </dependencies> |
| 72 | 78 |
yudao-framework/yudao-spring-boot-starter-monitor/src/main/java/cn/iocoder/yudao/framework/tracer/config/YudaoTracerAutoConfiguration.java
| @@ -3,6 +3,9 @@ package cn.iocoder.yudao.framework.tracer.config; | @@ -3,6 +3,9 @@ package cn.iocoder.yudao.framework.tracer.config; | ||
| 3 | import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum; | 3 | import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum; |
| 4 | import cn.iocoder.yudao.framework.tracer.core.aop.BizTraceAspect; | 4 | import cn.iocoder.yudao.framework.tracer.core.aop.BizTraceAspect; |
| 5 | import cn.iocoder.yudao.framework.tracer.core.filter.TraceFilter; | 5 | import cn.iocoder.yudao.framework.tracer.core.filter.TraceFilter; |
| 6 | +import io.opentracing.Tracer; | ||
| 7 | +import io.opentracing.util.GlobalTracer; | ||
| 8 | +import org.apache.skywalking.apm.toolkit.opentracing.SkywalkingTracer; | ||
| 6 | import org.springframework.boot.autoconfigure.AutoConfiguration; | 9 | import org.springframework.boot.autoconfigure.AutoConfiguration; |
| 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; | 10 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; |
| 8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | 11 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| @@ -16,30 +19,32 @@ import org.springframework.context.annotation.Bean; | @@ -16,30 +19,32 @@ import org.springframework.context.annotation.Bean; | ||
| 16 | * @author mashu | 19 | * @author mashu |
| 17 | */ | 20 | */ |
| 18 | @AutoConfiguration | 21 | @AutoConfiguration |
| 19 | -@ConditionalOnClass({BizTraceAspect.class}) | 22 | +@ConditionalOnClass(name = { |
| 23 | + "org.apache.skywalking.apm.toolkit.opentracing.SkywalkingTracer", | ||
| 24 | + "io.opentracing.Tracer" | ||
| 25 | +}) | ||
| 20 | @EnableConfigurationProperties(TracerProperties.class) | 26 | @EnableConfigurationProperties(TracerProperties.class) |
| 21 | @ConditionalOnProperty(prefix = "yudao.tracer", value = "enable", matchIfMissing = true) | 27 | @ConditionalOnProperty(prefix = "yudao.tracer", value = "enable", matchIfMissing = true) |
| 22 | public class YudaoTracerAutoConfiguration { | 28 | public class YudaoTracerAutoConfiguration { |
| 23 | 29 | ||
| 24 | - // TODO @芋艿:重要。目前 opentracing 版本存在冲突,要么保证 skywalking,要么保证阿里云短信 sdk | ||
| 25 | -// @Bean | ||
| 26 | -// public TracerProperties bizTracerProperties() { | ||
| 27 | -// return new TracerProperties(); | ||
| 28 | -// } | ||
| 29 | -// | ||
| 30 | -// @Bean | ||
| 31 | -// public BizTraceAspect bizTracingAop() { | ||
| 32 | -// return new BizTraceAspect(tracer()); | ||
| 33 | -// } | ||
| 34 | -// | ||
| 35 | -// @Bean | ||
| 36 | -// public Tracer tracer() { | ||
| 37 | -// // 创建 SkywalkingTracer 对象 | ||
| 38 | -// SkywalkingTracer tracer = new SkywalkingTracer(); | ||
| 39 | -// // 设置为 GlobalTracer 的追踪器 | ||
| 40 | -// GlobalTracer.register(tracer); | ||
| 41 | -// return tracer; | ||
| 42 | -// } | 30 | + @Bean |
| 31 | + public TracerProperties bizTracerProperties() { | ||
| 32 | + return new TracerProperties(); | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + @Bean | ||
| 36 | + public BizTraceAspect bizTracingAop() { | ||
| 37 | + return new BizTraceAspect(tracer()); | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + @Bean | ||
| 41 | + public Tracer tracer() { | ||
| 42 | + // 创建 SkywalkingTracer 对象 | ||
| 43 | + SkywalkingTracer tracer = new SkywalkingTracer(); | ||
| 44 | + // 设置为 GlobalTracer 的追踪器 | ||
| 45 | + GlobalTracer.registerIfAbsent(tracer); | ||
| 46 | + return tracer; | ||
| 47 | + } | ||
| 43 | 48 | ||
| 44 | /** | 49 | /** |
| 45 | * 创建 TraceFilter 过滤器,响应 header 设置 traceId | 50 | * 创建 TraceFilter 过滤器,响应 header 设置 traceId |
yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml
| @@ -24,8 +24,8 @@ | @@ -24,8 +24,8 @@ | ||
| 24 | <!-- Web 相关 --> | 24 | <!-- Web 相关 --> |
| 25 | <dependency> | 25 | <dependency> |
| 26 | <groupId>cn.iocoder.boot</groupId> | 26 | <groupId>cn.iocoder.boot</groupId> |
| 27 | - <artifactId>yudao-spring-boot-starter-web</artifactId> | ||
| 28 | - <scope>provided</scope> <!-- 设置为 provided,只有 OncePerRequestFilter 使用到 --> | 27 | + <artifactId>yudao-spring-boot-starter-security</artifactId> |
| 28 | + <scope>provided</scope> <!-- 设置为 provided,只有 DefaultDBFieldHandler 使用到 --> | ||
| 29 | </dependency> | 29 | </dependency> |
| 30 | 30 | ||
| 31 | <!-- DB 相关 --> | 31 | <!-- DB 相关 --> |
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/handler/DefaultDBFieldHandler.java
| 1 | package cn.iocoder.yudao.framework.mybatis.core.handler; | 1 | package cn.iocoder.yudao.framework.mybatis.core.handler; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | 3 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 4 | -import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; | 4 | +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; |
| 5 | import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; | 5 | import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; |
| 6 | import org.apache.ibatis.reflection.MetaObject; | 6 | import org.apache.ibatis.reflection.MetaObject; |
| 7 | 7 | ||
| @@ -32,7 +32,7 @@ public class DefaultDBFieldHandler implements MetaObjectHandler { | @@ -32,7 +32,7 @@ public class DefaultDBFieldHandler implements MetaObjectHandler { | ||
| 32 | baseDO.setUpdateTime(current); | 32 | baseDO.setUpdateTime(current); |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | - Long userId = WebFrameworkUtils.getLoginUserId(); | 35 | + Long userId = SecurityFrameworkUtils.getLoginUserId(); |
| 36 | // 当前登录用户不为空,创建人为空,则当前登录用户为创建人 | 36 | // 当前登录用户不为空,创建人为空,则当前登录用户为创建人 |
| 37 | if (Objects.nonNull(userId) && Objects.isNull(baseDO.getCreator())) { | 37 | if (Objects.nonNull(userId) && Objects.isNull(baseDO.getCreator())) { |
| 38 | baseDO.setCreator(userId.toString()); | 38 | baseDO.setCreator(userId.toString()); |
| @@ -54,7 +54,7 @@ public class DefaultDBFieldHandler implements MetaObjectHandler { | @@ -54,7 +54,7 @@ public class DefaultDBFieldHandler implements MetaObjectHandler { | ||
| 54 | 54 | ||
| 55 | // 当前登录用户不为空,更新人为空,则当前登录用户为更新人 | 55 | // 当前登录用户不为空,更新人为空,则当前登录用户为更新人 |
| 56 | Object modifier = getFieldValByName("updater", metaObject); | 56 | Object modifier = getFieldValByName("updater", metaObject); |
| 57 | - Long userId = WebFrameworkUtils.getLoginUserId(); | 57 | + Long userId = SecurityFrameworkUtils.getLoginUserId(); |
| 58 | if (Objects.nonNull(userId) && Objects.isNull(modifier)) { | 58 | if (Objects.nonNull(userId) && Objects.isNull(modifier)) { |
| 59 | setFieldValByName("updater", userId.toString(), metaObject); | 59 | setFieldValByName("updater", userId.toString(), metaObject); |
| 60 | } | 60 | } |
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
| @@ -42,6 +42,7 @@ public interface BaseMapperX<T> extends MPJBaseMapper<T> { | @@ -42,6 +42,7 @@ public interface BaseMapperX<T> extends MPJBaseMapper<T> { | ||
| 42 | default PageResult<T> selectPage(PageParam pageParam, Collection<SortingField> sortingFields, @Param("ew") Wrapper<T> queryWrapper) { | 42 | default PageResult<T> selectPage(PageParam pageParam, Collection<SortingField> sortingFields, @Param("ew") Wrapper<T> queryWrapper) { |
| 43 | // 特殊:不分页,直接查询全部 | 43 | // 特殊:不分页,直接查询全部 |
| 44 | if (PageParam.PAGE_SIZE_NONE.equals(pageParam.getPageSize())) { | 44 | if (PageParam.PAGE_SIZE_NONE.equals(pageParam.getPageSize())) { |
| 45 | + MyBatisUtils.addOrder(queryWrapper, sortingFields); | ||
| 45 | List<T> list = selectList(queryWrapper); | 46 | List<T> list = selectList(queryWrapper); |
| 46 | return new PageResult<>(list, (long) list.size()); | 47 | return new PageResult<>(list, (long) list.size()); |
| 47 | } | 48 | } |
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/MPJLambdaWrapperX.java
| @@ -118,7 +118,6 @@ public class MPJLambdaWrapperX<T> extends MPJLambdaWrapper<T> { | @@ -118,7 +118,6 @@ public class MPJLambdaWrapperX<T> extends MPJLambdaWrapper<T> { | ||
| 118 | 118 | ||
| 119 | @Override | 119 | @Override |
| 120 | public <X> MPJLambdaWrapperX<T> orderByDesc(SFunction<X, ?> column) { | 120 | public <X> MPJLambdaWrapperX<T> orderByDesc(SFunction<X, ?> column) { |
| 121 | - //noinspection unchecked | ||
| 122 | super.orderByDesc(true, column); | 121 | super.orderByDesc(true, column); |
| 123 | return this; | 122 | return this; |
| 124 | } | 123 | } |
| @@ -208,7 +207,7 @@ public class MPJLambdaWrapperX<T> extends MPJLambdaWrapper<T> { | @@ -208,7 +207,7 @@ public class MPJLambdaWrapperX<T> extends MPJLambdaWrapper<T> { | ||
| 208 | } | 207 | } |
| 209 | 208 | ||
| 210 | @Override | 209 | @Override |
| 211 | - public <S, X> MPJLambdaWrapperX<T> selectCount(SFunction<S, ?> column, String alias) { | 210 | + public <S> MPJLambdaWrapperX<T> selectCount(SFunction<S, ?> column, String alias) { |
| 212 | super.selectCount(column, alias); | 211 | super.selectCount(column, alias); |
| 213 | return this; | 212 | return this; |
| 214 | } | 213 | } |
| @@ -226,7 +225,7 @@ public class MPJLambdaWrapperX<T> extends MPJLambdaWrapper<T> { | @@ -226,7 +225,7 @@ public class MPJLambdaWrapperX<T> extends MPJLambdaWrapper<T> { | ||
| 226 | } | 225 | } |
| 227 | 226 | ||
| 228 | @Override | 227 | @Override |
| 229 | - public <S, X> MPJLambdaWrapperX<T> selectSum(SFunction<S, ?> column, String alias) { | 228 | + public <S> MPJLambdaWrapperX<T> selectSum(SFunction<S, ?> column, String alias) { |
| 230 | super.selectSum(column, alias); | 229 | super.selectSum(column, alias); |
| 231 | return this; | 230 | return this; |
| 232 | } | 231 | } |
| @@ -244,7 +243,7 @@ public class MPJLambdaWrapperX<T> extends MPJLambdaWrapper<T> { | @@ -244,7 +243,7 @@ public class MPJLambdaWrapperX<T> extends MPJLambdaWrapper<T> { | ||
| 244 | } | 243 | } |
| 245 | 244 | ||
| 246 | @Override | 245 | @Override |
| 247 | - public <S, X> MPJLambdaWrapperX<T> selectMax(SFunction<S, ?> column, String alias) { | 246 | + public <S> MPJLambdaWrapperX<T> selectMax(SFunction<S, ?> column, String alias) { |
| 248 | super.selectMax(column, alias); | 247 | super.selectMax(column, alias); |
| 249 | return this; | 248 | return this; |
| 250 | } | 249 | } |
| @@ -262,7 +261,7 @@ public class MPJLambdaWrapperX<T> extends MPJLambdaWrapper<T> { | @@ -262,7 +261,7 @@ public class MPJLambdaWrapperX<T> extends MPJLambdaWrapper<T> { | ||
| 262 | } | 261 | } |
| 263 | 262 | ||
| 264 | @Override | 263 | @Override |
| 265 | - public <S, X> MPJLambdaWrapperX<T> selectMin(SFunction<S, ?> column, String alias) { | 264 | + public <S> MPJLambdaWrapperX<T> selectMin(SFunction<S, ?> column, String alias) { |
| 266 | super.selectMin(column, alias); | 265 | super.selectMin(column, alias); |
| 267 | return this; | 266 | return this; |
| 268 | } | 267 | } |
| @@ -280,7 +279,7 @@ public class MPJLambdaWrapperX<T> extends MPJLambdaWrapper<T> { | @@ -280,7 +279,7 @@ public class MPJLambdaWrapperX<T> extends MPJLambdaWrapper<T> { | ||
| 280 | } | 279 | } |
| 281 | 280 | ||
| 282 | @Override | 281 | @Override |
| 283 | - public <S, X> MPJLambdaWrapperX<T> selectAvg(SFunction<S, ?> column, String alias) { | 282 | + public <S> MPJLambdaWrapperX<T> selectAvg(SFunction<S, ?> column, String alias) { |
| 284 | super.selectAvg(column, alias); | 283 | super.selectAvg(column, alias); |
| 285 | return this; | 284 | return this; |
| 286 | } | 285 | } |
| @@ -298,7 +297,7 @@ public class MPJLambdaWrapperX<T> extends MPJLambdaWrapper<T> { | @@ -298,7 +297,7 @@ public class MPJLambdaWrapperX<T> extends MPJLambdaWrapper<T> { | ||
| 298 | } | 297 | } |
| 299 | 298 | ||
| 300 | @Override | 299 | @Override |
| 301 | - public <S, X> MPJLambdaWrapperX<T> selectLen(SFunction<S, ?> column, String alias) { | 300 | + public <S> MPJLambdaWrapperX<T> selectLen(SFunction<S, ?> column, String alias) { |
| 302 | super.selectLen(column, alias); | 301 | super.selectLen(column, alias); |
| 303 | return this; | 302 | return this; |
| 304 | } | 303 | } |
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java
| 1 | package cn.iocoder.yudao.framework.mybatis.core.util; | 1 | package cn.iocoder.yudao.framework.mybatis.core.util; |
| 2 | 2 | ||
| 3 | -import cn.hutool.core.collection.CollectionUtil; | 3 | +import cn.hutool.core.collection.CollUtil; |
| 4 | import cn.hutool.core.lang.func.Func1; | 4 | import cn.hutool.core.lang.func.Func1; |
| 5 | import cn.hutool.core.lang.func.LambdaUtil; | 5 | import cn.hutool.core.lang.func.LambdaUtil; |
| 6 | import cn.hutool.core.util.StrUtil; | 6 | import cn.hutool.core.util.StrUtil; |
| @@ -8,6 +8,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam; | @@ -8,6 +8,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam; | ||
| 8 | import cn.iocoder.yudao.framework.common.pojo.SortingField; | 8 | import cn.iocoder.yudao.framework.common.pojo.SortingField; |
| 9 | import cn.iocoder.yudao.framework.mybatis.core.enums.DbTypeEnum; | 9 | import cn.iocoder.yudao.framework.mybatis.core.enums.DbTypeEnum; |
| 10 | import com.baomidou.mybatisplus.annotation.DbType; | 10 | import com.baomidou.mybatisplus.annotation.DbType; |
| 11 | +import com.baomidou.mybatisplus.core.conditions.Wrapper; | ||
| 12 | +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | ||
| 11 | import com.baomidou.mybatisplus.core.metadata.OrderItem; | 13 | import com.baomidou.mybatisplus.core.metadata.OrderItem; |
| 12 | import com.baomidou.mybatisplus.core.toolkit.StringPool; | 14 | import com.baomidou.mybatisplus.core.toolkit.StringPool; |
| 13 | import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; | 15 | import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; |
| @@ -20,7 +22,6 @@ import net.sf.jsqlparser.schema.Table; | @@ -20,7 +22,6 @@ import net.sf.jsqlparser.schema.Table; | ||
| 20 | import java.util.ArrayList; | 22 | import java.util.ArrayList; |
| 21 | import java.util.Collection; | 23 | import java.util.Collection; |
| 22 | import java.util.List; | 24 | import java.util.List; |
| 23 | -import java.util.stream.Collectors; | ||
| 24 | 25 | ||
| 25 | /** | 26 | /** |
| 26 | * MyBatis 工具类 | 27 | * MyBatis 工具类 |
| @@ -37,15 +38,27 @@ public class MyBatisUtils { | @@ -37,15 +38,27 @@ public class MyBatisUtils { | ||
| 37 | // 页码 + 数量 | 38 | // 页码 + 数量 |
| 38 | Page<T> page = new Page<>(pageParam.getPageNo(), pageParam.getPageSize()); | 39 | Page<T> page = new Page<>(pageParam.getPageNo(), pageParam.getPageSize()); |
| 39 | // 排序字段 | 40 | // 排序字段 |
| 40 | - if (!CollectionUtil.isEmpty(sortingFields)) { | ||
| 41 | - page.addOrder(sortingFields.stream().map(sortingField -> SortingField.ORDER_ASC.equals(sortingField.getOrder()) | ||
| 42 | - ? OrderItem.asc(StrUtil.toUnderlineCase(sortingField.getField())) | ||
| 43 | - : OrderItem.desc(StrUtil.toUnderlineCase(sortingField.getField()))) | ||
| 44 | - .collect(Collectors.toList())); | 41 | + if (CollUtil.isNotEmpty(sortingFields)) { |
| 42 | + for (SortingField sortingField : sortingFields) { | ||
| 43 | + page.addOrder(new OrderItem().setAsc(SortingField.ORDER_ASC.equals(sortingField.getOrder())) | ||
| 44 | + .setColumn(StrUtil.toUnderlineCase(sortingField.getField()))); | ||
| 45 | + } | ||
| 45 | } | 46 | } |
| 46 | return page; | 47 | return page; |
| 47 | } | 48 | } |
| 48 | 49 | ||
| 50 | + public static <T> void addOrder(Wrapper<T> wrapper, Collection<SortingField> sortingFields) { | ||
| 51 | + if (CollUtil.isEmpty(sortingFields)) { | ||
| 52 | + return; | ||
| 53 | + } | ||
| 54 | + QueryWrapper<T> query = (QueryWrapper<T>) wrapper; | ||
| 55 | + for (SortingField sortingField : sortingFields) { | ||
| 56 | + query.orderBy(true, | ||
| 57 | + SortingField.ORDER_ASC.equals(sortingField.getOrder()), | ||
| 58 | + StrUtil.toUnderlineCase(sortingField.getField())); | ||
| 59 | + } | ||
| 60 | + } | ||
| 61 | + | ||
| 49 | /** | 62 | /** |
| 50 | * 将拦截器添加到链中 | 63 | * 将拦截器添加到链中 |
| 51 | * 由于 MybatisPlusInterceptor 不支持添加拦截器,所以只能全量设置 | 64 | * 由于 MybatisPlusInterceptor 不支持添加拦截器,所以只能全量设置 |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/util/SecurityFrameworkUtils.java
| @@ -126,8 +126,10 @@ public class SecurityFrameworkUtils { | @@ -126,8 +126,10 @@ public class SecurityFrameworkUtils { | ||
| 126 | 126 | ||
| 127 | // 额外设置到 request 中,用于 ApiAccessLogFilter 可以获取到用户编号; | 127 | // 额外设置到 request 中,用于 ApiAccessLogFilter 可以获取到用户编号; |
| 128 | // 原因是,Spring Security 的 Filter 在 ApiAccessLogFilter 后面,在它记录访问日志时,线上上下文已经没有用户编号等信息 | 128 | // 原因是,Spring Security 的 Filter 在 ApiAccessLogFilter 后面,在它记录访问日志时,线上上下文已经没有用户编号等信息 |
| 129 | - WebFrameworkUtils.setLoginUserId(request, loginUser.getId()); | ||
| 130 | - WebFrameworkUtils.setLoginUserType(request, loginUser.getUserType()); | 129 | + if (request != null) { |
| 130 | + WebFrameworkUtils.setLoginUserId(request, loginUser.getId()); | ||
| 131 | + WebFrameworkUtils.setLoginUserType(request, loginUser.getUserType()); | ||
| 132 | + } | ||
| 131 | } | 133 | } |
| 132 | 134 | ||
| 133 | private static Authentication buildAuthentication(LoginUser loginUser, HttpServletRequest request) { | 135 | private static Authentication buildAuthentication(LoginUser loginUser, HttpServletRequest request) { |
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/filter/CacheRequestBodyFilter.java
| 1 | package cn.iocoder.yudao.framework.web.core.filter; | 1 | package cn.iocoder.yudao.framework.web.core.filter; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.util.StrUtil; | ||
| 3 | import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; | 4 | import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; |
| 4 | import org.springframework.web.filter.OncePerRequestFilter; | 5 | import org.springframework.web.filter.OncePerRequestFilter; |
| 5 | 6 | ||
| @@ -16,6 +17,14 @@ import java.io.IOException; | @@ -16,6 +17,14 @@ import java.io.IOException; | ||
| 16 | */ | 17 | */ |
| 17 | public class CacheRequestBodyFilter extends OncePerRequestFilter { | 18 | public class CacheRequestBodyFilter extends OncePerRequestFilter { |
| 18 | 19 | ||
| 20 | + /** | ||
| 21 | + * 需要排除的 URI | ||
| 22 | + * | ||
| 23 | + * 1. 排除 Spring Boot Admin 相关请求,避免客户端连接中断导致的异常。 | ||
| 24 | + * 例如说:<a href="https://github.com/YunaiV/ruoyi-vue-pro/issues/795">795 ISSUE</a> | ||
| 25 | + */ | ||
| 26 | + private static final String[] IGNORE_URIS = {"/admin/", "/actuator/"}; | ||
| 27 | + | ||
| 19 | @Override | 28 | @Override |
| 20 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) | 29 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) |
| 21 | throws IOException, ServletException { | 30 | throws IOException, ServletException { |
| @@ -24,7 +33,13 @@ public class CacheRequestBodyFilter extends OncePerRequestFilter { | @@ -24,7 +33,13 @@ public class CacheRequestBodyFilter extends OncePerRequestFilter { | ||
| 24 | 33 | ||
| 25 | @Override | 34 | @Override |
| 26 | protected boolean shouldNotFilter(HttpServletRequest request) { | 35 | protected boolean shouldNotFilter(HttpServletRequest request) { |
| 27 | - // 只处理 json 请求内容 | 36 | + // 1. 校验是否为排除的 URL |
| 37 | + String requestURI = request.getRequestURI(); | ||
| 38 | + if (StrUtil.startWithAny(requestURI, IGNORE_URIS)) { | ||
| 39 | + return true; | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + // 2. 只处理 json 请求内容 | ||
| 28 | return !ServletUtils.isJsonRequest(request); | 43 | return !ServletUtils.isJsonRequest(request); |
| 29 | } | 44 | } |
| 30 | 45 |
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java
| @@ -6,6 +6,8 @@ import cn.hutool.core.map.MapUtil; | @@ -6,6 +6,8 @@ import cn.hutool.core.map.MapUtil; | ||
| 6 | import cn.hutool.core.util.ObjUtil; | 6 | import cn.hutool.core.util.ObjUtil; |
| 7 | import cn.hutool.core.util.StrUtil; | 7 | import cn.hutool.core.util.StrUtil; |
| 8 | import cn.hutool.extra.servlet.JakartaServletUtil; | 8 | import cn.hutool.extra.servlet.JakartaServletUtil; |
| 9 | +import cn.iocoder.yudao.framework.common.biz.infra.logger.ApiErrorLogCommonApi; | ||
| 10 | +import cn.iocoder.yudao.framework.common.biz.infra.logger.dto.ApiErrorLogCreateReqDTO; | ||
| 9 | import cn.iocoder.yudao.framework.common.exception.ServiceException; | 11 | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| 10 | import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; | 12 | import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; |
| 11 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 13 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| @@ -14,8 +16,6 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | @@ -14,8 +16,6 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | ||
| 14 | import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; | 16 | import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; |
| 15 | import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; | 17 | import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; |
| 16 | import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; | 18 | import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; |
| 17 | -import cn.iocoder.yudao.framework.common.biz.infra.logger.ApiErrorLogCommonApi; | ||
| 18 | -import cn.iocoder.yudao.framework.common.biz.infra.logger.dto.ApiErrorLogCreateReqDTO; | ||
| 19 | import com.fasterxml.jackson.databind.exc.InvalidFormatException; | 19 | import com.fasterxml.jackson.databind.exc.InvalidFormatException; |
| 20 | import jakarta.servlet.http.HttpServletRequest; | 20 | import jakarta.servlet.http.HttpServletRequest; |
| 21 | import jakarta.validation.ConstraintViolation; | 21 | import jakarta.validation.ConstraintViolation; |
| @@ -29,6 +29,7 @@ import org.springframework.util.Assert; | @@ -29,6 +29,7 @@ import org.springframework.util.Assert; | ||
| 29 | import org.springframework.validation.BindException; | 29 | import org.springframework.validation.BindException; |
| 30 | import org.springframework.validation.FieldError; | 30 | import org.springframework.validation.FieldError; |
| 31 | import org.springframework.validation.ObjectError; | 31 | import org.springframework.validation.ObjectError; |
| 32 | +import org.springframework.web.HttpMediaTypeNotSupportedException; | ||
| 32 | import org.springframework.web.HttpRequestMethodNotSupportedException; | 33 | import org.springframework.web.HttpRequestMethodNotSupportedException; |
| 33 | import org.springframework.web.bind.MethodArgumentNotValidException; | 34 | import org.springframework.web.bind.MethodArgumentNotValidException; |
| 34 | import org.springframework.web.bind.MissingServletRequestParameterException; | 35 | import org.springframework.web.bind.MissingServletRequestParameterException; |
| @@ -101,6 +102,9 @@ public class GlobalExceptionHandler { | @@ -101,6 +102,9 @@ public class GlobalExceptionHandler { | ||
| 101 | if (ex instanceof HttpRequestMethodNotSupportedException) { | 102 | if (ex instanceof HttpRequestMethodNotSupportedException) { |
| 102 | return httpRequestMethodNotSupportedExceptionHandler((HttpRequestMethodNotSupportedException) ex); | 103 | return httpRequestMethodNotSupportedExceptionHandler((HttpRequestMethodNotSupportedException) ex); |
| 103 | } | 104 | } |
| 105 | + if (ex instanceof HttpMediaTypeNotSupportedException) { | ||
| 106 | + return httpMediaTypeNotSupportedExceptionHandler((HttpMediaTypeNotSupportedException) ex); | ||
| 107 | + } | ||
| 104 | if (ex instanceof ServiceException) { | 108 | if (ex instanceof ServiceException) { |
| 105 | return serviceExceptionHandler((ServiceException) ex); | 109 | return serviceExceptionHandler((ServiceException) ex); |
| 106 | } | 110 | } |
| @@ -171,17 +175,19 @@ public class GlobalExceptionHandler { | @@ -171,17 +175,19 @@ public class GlobalExceptionHandler { | ||
| 171 | /** | 175 | /** |
| 172 | * 处理 SpringMVC 请求参数类型错误 | 176 | * 处理 SpringMVC 请求参数类型错误 |
| 173 | * | 177 | * |
| 174 | - * 例如说,接口上设置了 @RequestBody实体中 xx 属性类型为 Integer,结果传递 xx 参数类型为 String | 178 | + * 例如说,接口上设置了 @RequestBody 实体中 xx 属性类型为 Integer,结果传递 xx 参数类型为 String |
| 175 | */ | 179 | */ |
| 176 | @ExceptionHandler(HttpMessageNotReadableException.class) | 180 | @ExceptionHandler(HttpMessageNotReadableException.class) |
| 177 | public CommonResult<?> methodArgumentTypeInvalidFormatExceptionHandler(HttpMessageNotReadableException ex) { | 181 | public CommonResult<?> methodArgumentTypeInvalidFormatExceptionHandler(HttpMessageNotReadableException ex) { |
| 178 | log.warn("[methodArgumentTypeInvalidFormatExceptionHandler]", ex); | 182 | log.warn("[methodArgumentTypeInvalidFormatExceptionHandler]", ex); |
| 179 | - if(ex.getCause() instanceof InvalidFormatException) { | 183 | + if (ex.getCause() instanceof InvalidFormatException) { |
| 180 | InvalidFormatException invalidFormatException = (InvalidFormatException) ex.getCause(); | 184 | InvalidFormatException invalidFormatException = (InvalidFormatException) ex.getCause(); |
| 181 | return CommonResult.error(BAD_REQUEST.getCode(), String.format("请求参数类型错误:%s", invalidFormatException.getValue())); | 185 | return CommonResult.error(BAD_REQUEST.getCode(), String.format("请求参数类型错误:%s", invalidFormatException.getValue())); |
| 182 | - } else { | ||
| 183 | - return defaultExceptionHandler(ServletUtils.getRequest(), ex); | ||
| 184 | } | 186 | } |
| 187 | + if (StrUtil.startWith(ex.getMessage(), "Required request body is missing")) { | ||
| 188 | + return CommonResult.error(BAD_REQUEST.getCode(), "请求参数类型错误: request body 缺失"); | ||
| 189 | + } | ||
| 190 | + return defaultExceptionHandler(ServletUtils.getRequest(), ex); | ||
| 185 | } | 191 | } |
| 186 | 192 | ||
| 187 | /** | 193 | /** |
| @@ -238,6 +244,17 @@ public class GlobalExceptionHandler { | @@ -238,6 +244,17 @@ public class GlobalExceptionHandler { | ||
| 238 | } | 244 | } |
| 239 | 245 | ||
| 240 | /** | 246 | /** |
| 247 | + * 处理 SpringMVC 请求的 Content-Type 不正确 | ||
| 248 | + * | ||
| 249 | + * 例如说,A 接口的 Content-Type 为 application/json,结果请求的 Content-Type 为 application/octet-stream,导致不匹配 | ||
| 250 | + */ | ||
| 251 | + @ExceptionHandler(HttpMediaTypeNotSupportedException.class) | ||
| 252 | + public CommonResult<?> httpMediaTypeNotSupportedExceptionHandler(HttpMediaTypeNotSupportedException ex) { | ||
| 253 | + log.warn("[httpMediaTypeNotSupportedExceptionHandler]", ex); | ||
| 254 | + return CommonResult.error(BAD_REQUEST.getCode(), String.format("请求类型不正确:%s", ex.getMessage())); | ||
| 255 | + } | ||
| 256 | + | ||
| 257 | + /** | ||
| 241 | * 处理 Spring Security 权限不足的异常 | 258 | * 处理 Spring Security 权限不足的异常 |
| 242 | * | 259 | * |
| 243 | * 来源是,使用 @PreAuthorize 注解,AOP 进行权限拦截 | 260 | * 来源是,使用 @PreAuthorize 注解,AOP 进行权限拦截 |
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/config/YudaoXssAutoConfiguration.java
| @@ -5,7 +5,6 @@ import cn.iocoder.yudao.framework.xss.core.clean.JsoupXssCleaner; | @@ -5,7 +5,6 @@ import cn.iocoder.yudao.framework.xss.core.clean.JsoupXssCleaner; | ||
| 5 | import cn.iocoder.yudao.framework.xss.core.clean.XssCleaner; | 5 | import cn.iocoder.yudao.framework.xss.core.clean.XssCleaner; |
| 6 | import cn.iocoder.yudao.framework.xss.core.filter.XssFilter; | 6 | import cn.iocoder.yudao.framework.xss.core.filter.XssFilter; |
| 7 | import cn.iocoder.yudao.framework.xss.core.json.XssStringJsonDeserializer; | 7 | import cn.iocoder.yudao.framework.xss.core.json.XssStringJsonDeserializer; |
| 8 | -import com.fasterxml.jackson.databind.ObjectMapper; | ||
| 9 | import org.springframework.boot.autoconfigure.AutoConfiguration; | 8 | import org.springframework.boot.autoconfigure.AutoConfiguration; |
| 10 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; | 9 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; |
| 11 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | 10 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; |
| @@ -42,13 +41,13 @@ public class YudaoXssAutoConfiguration implements WebMvcConfigurer { | @@ -42,13 +41,13 @@ public class YudaoXssAutoConfiguration implements WebMvcConfigurer { | ||
| 42 | */ | 41 | */ |
| 43 | @Bean | 42 | @Bean |
| 44 | @ConditionalOnMissingBean(name = "xssJacksonCustomizer") | 43 | @ConditionalOnMissingBean(name = "xssJacksonCustomizer") |
| 45 | - @ConditionalOnBean(ObjectMapper.class) | ||
| 46 | @ConditionalOnProperty(value = "yudao.xss.enable", havingValue = "true") | 44 | @ConditionalOnProperty(value = "yudao.xss.enable", havingValue = "true") |
| 47 | public Jackson2ObjectMapperBuilderCustomizer xssJacksonCustomizer(XssProperties properties, | 45 | public Jackson2ObjectMapperBuilderCustomizer xssJacksonCustomizer(XssProperties properties, |
| 48 | PathMatcher pathMatcher, | 46 | PathMatcher pathMatcher, |
| 49 | XssCleaner xssCleaner) { | 47 | XssCleaner xssCleaner) { |
| 50 | // 在反序列化时进行 xss 过滤,可以替换使用 XssStringJsonSerializer,在序列化时进行处理 | 48 | // 在反序列化时进行 xss 过滤,可以替换使用 XssStringJsonSerializer,在序列化时进行处理 |
| 51 | - return builder -> builder.deserializerByType(String.class, new XssStringJsonDeserializer(properties, pathMatcher, xssCleaner)); | 49 | + return builder -> |
| 50 | + builder.deserializerByType(String.class, new XssStringJsonDeserializer(properties, pathMatcher, xssCleaner)); | ||
| 52 | } | 51 | } |
| 53 | 52 | ||
| 54 | /** | 53 | /** |
yudao-module-ai/pom.xml
| @@ -19,7 +19,8 @@ | @@ -19,7 +19,8 @@ | ||
| 19 | 国外:OpenAI、Ollama、Midjourney、StableDiffusion、Suno | 19 | 国外:OpenAI、Ollama、Midjourney、StableDiffusion、Suno |
| 20 | </description> | 20 | </description> |
| 21 | <properties> | 21 | <properties> |
| 22 | - <spring-ai.version>1.0.0-M6</spring-ai.version> | 22 | + <spring-ai.version>1.0.0</spring-ai.version> |
| 23 | + <alibaba-ai.version>1.0.0.2</alibaba-ai.version> | ||
| 23 | <tinyflow.version>1.0.2</tinyflow.version> | 24 | <tinyflow.version>1.0.2</tinyflow.version> |
| 24 | </properties> | 25 | </properties> |
| 25 | 26 | ||
| @@ -75,65 +76,73 @@ | @@ -75,65 +76,73 @@ | ||
| 75 | <!-- Spring AI Model 模型接入 --> | 76 | <!-- Spring AI Model 模型接入 --> |
| 76 | <dependency> | 77 | <dependency> |
| 77 | <groupId>org.springframework.ai</groupId> | 78 | <groupId>org.springframework.ai</groupId> |
| 78 | - <artifactId>spring-ai-openai-spring-boot-starter</artifactId> | 79 | + <artifactId>spring-ai-starter-model-openai</artifactId> |
| 79 | <version>${spring-ai.version}</version> | 80 | <version>${spring-ai.version}</version> |
| 80 | </dependency> | 81 | </dependency> |
| 81 | <dependency> | 82 | <dependency> |
| 82 | <groupId>org.springframework.ai</groupId> | 83 | <groupId>org.springframework.ai</groupId> |
| 83 | - <artifactId>spring-ai-azure-openai-spring-boot-starter</artifactId> | 84 | + <artifactId>spring-ai-starter-model-azure-openai</artifactId> |
| 84 | <version>${spring-ai.version}</version> | 85 | <version>${spring-ai.version}</version> |
| 85 | </dependency> | 86 | </dependency> |
| 86 | <dependency> | 87 | <dependency> |
| 87 | <groupId>org.springframework.ai</groupId> | 88 | <groupId>org.springframework.ai</groupId> |
| 88 | - <artifactId>spring-ai-ollama-spring-boot-starter</artifactId> | 89 | + <artifactId>spring-ai-starter-model-deepseek</artifactId> |
| 89 | <version>${spring-ai.version}</version> | 90 | <version>${spring-ai.version}</version> |
| 90 | </dependency> | 91 | </dependency> |
| 91 | <dependency> | 92 | <dependency> |
| 92 | <groupId>org.springframework.ai</groupId> | 93 | <groupId>org.springframework.ai</groupId> |
| 93 | - <artifactId>spring-ai-stability-ai-spring-boot-starter</artifactId> | 94 | + <artifactId>spring-ai-starter-model-ollama</artifactId> |
| 94 | <version>${spring-ai.version}</version> | 95 | <version>${spring-ai.version}</version> |
| 95 | </dependency> | 96 | </dependency> |
| 96 | <dependency> | 97 | <dependency> |
| 97 | - <!-- 通义千问 --> | ||
| 98 | - <groupId>com.alibaba.cloud.ai</groupId> | ||
| 99 | - <artifactId>spring-ai-alibaba-starter</artifactId> | ||
| 100 | - <version>${spring-ai.version}.1</version> | ||
| 101 | - </dependency> | ||
| 102 | - <dependency> | ||
| 103 | - <!-- 文心一言 --> | ||
| 104 | <groupId>org.springframework.ai</groupId> | 98 | <groupId>org.springframework.ai</groupId> |
| 105 | - <artifactId>spring-ai-qianfan-spring-boot-starter</artifactId> | 99 | + <artifactId>spring-ai-starter-model-stability-ai</artifactId> |
| 106 | <version>${spring-ai.version}</version> | 100 | <version>${spring-ai.version}</version> |
| 107 | </dependency> | 101 | </dependency> |
| 108 | <dependency> | 102 | <dependency> |
| 109 | <!-- 智谱 GLM --> | 103 | <!-- 智谱 GLM --> |
| 110 | <groupId>org.springframework.ai</groupId> | 104 | <groupId>org.springframework.ai</groupId> |
| 111 | - <artifactId>spring-ai-zhipuai-spring-boot-starter</artifactId> | 105 | + <artifactId>spring-ai-starter-model-zhipuai</artifactId> |
| 112 | <version>${spring-ai.version}</version> | 106 | <version>${spring-ai.version}</version> |
| 113 | </dependency> | 107 | </dependency> |
| 114 | <dependency> | 108 | <dependency> |
| 115 | <groupId>org.springframework.ai</groupId> | 109 | <groupId>org.springframework.ai</groupId> |
| 116 | - <artifactId>spring-ai-minimax-spring-boot-starter</artifactId> | 110 | + <artifactId>spring-ai-starter-model-minimax</artifactId> |
| 117 | <version>${spring-ai.version}</version> | 111 | <version>${spring-ai.version}</version> |
| 118 | </dependency> | 112 | </dependency> |
| 113 | + | ||
| 119 | <dependency> | 114 | <dependency> |
| 120 | - <groupId>org.springframework.ai</groupId> | ||
| 121 | - <artifactId>spring-ai-moonshot-spring-boot-starter</artifactId> | ||
| 122 | - <version>${spring-ai.version}</version> | 115 | + <!-- 通义千问 --> |
| 116 | + <groupId>com.alibaba.cloud.ai</groupId> | ||
| 117 | + <artifactId>spring-ai-alibaba-starter-dashscope</artifactId> | ||
| 118 | + <version>${alibaba-ai.version}</version> | ||
| 119 | + </dependency> | ||
| 120 | + | ||
| 121 | + <dependency> | ||
| 122 | + <!-- 文心一言 --> | ||
| 123 | + <groupId>org.springaicommunity</groupId> | ||
| 124 | + <artifactId>qianfan-spring-boot-starter</artifactId> | ||
| 125 | + <version>1.0.0</version> | ||
| 126 | + </dependency> | ||
| 127 | + <dependency> | ||
| 128 | + <!-- 月之暗灭 --> | ||
| 129 | + <groupId>org.springaicommunity</groupId> | ||
| 130 | + <artifactId>moonshot-spring-boot-starter</artifactId> | ||
| 131 | + <version>1.0.0</version> | ||
| 123 | </dependency> | 132 | </dependency> |
| 124 | 133 | ||
| 125 | <!-- 向量存储:https://db-engines.com/en/ranking/vector+dbms --> | 134 | <!-- 向量存储:https://db-engines.com/en/ranking/vector+dbms --> |
| 126 | <dependency> | 135 | <dependency> |
| 127 | <!-- Qdrant:https://qdrant.tech/ --> | 136 | <!-- Qdrant:https://qdrant.tech/ --> |
| 128 | <groupId>org.springframework.ai</groupId> | 137 | <groupId>org.springframework.ai</groupId> |
| 129 | - <artifactId>spring-ai-qdrant-store</artifactId> | 138 | + <artifactId>spring-ai-starter-vector-store-qdrant</artifactId> |
| 130 | <version>${spring-ai.version}</version> | 139 | <version>${spring-ai.version}</version> |
| 131 | </dependency> | 140 | </dependency> |
| 132 | 141 | ||
| 133 | <dependency> | 142 | <dependency> |
| 134 | <!-- Redis:https://redis.io/docs/latest/develop/get-started/vector-database/ --> | 143 | <!-- Redis:https://redis.io/docs/latest/develop/get-started/vector-database/ --> |
| 135 | <groupId>org.springframework.ai</groupId> | 144 | <groupId>org.springframework.ai</groupId> |
| 136 | - <artifactId>spring-ai-redis-store</artifactId> | 145 | + <artifactId>spring-ai-starter-vector-store-redis</artifactId> |
| 137 | <version>${spring-ai.version}</version> | 146 | <version>${spring-ai.version}</version> |
| 138 | </dependency> | 147 | </dependency> |
| 139 | <dependency> | 148 | <dependency> |
| @@ -144,7 +153,7 @@ | @@ -144,7 +153,7 @@ | ||
| 144 | <dependency> | 153 | <dependency> |
| 145 | <!-- Milvus:https://milvus.io/ --> | 154 | <!-- Milvus:https://milvus.io/ --> |
| 146 | <groupId>org.springframework.ai</groupId> | 155 | <groupId>org.springframework.ai</groupId> |
| 147 | - <artifactId>spring-ai-milvus-store</artifactId> | 156 | + <artifactId>spring-ai-starter-vector-store-milvus</artifactId> |
| 148 | <version>${spring-ai.version}</version> | 157 | <version>${spring-ai.version}</version> |
| 149 | <exclusions> | 158 | <exclusions> |
| 150 | <!-- 解决和 logback 的日志冲突 --> | 159 | <!-- 解决和 logback 的日志冲突 --> |
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatConversationController.java
| @@ -12,6 +12,7 @@ import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatCo | @@ -12,6 +12,7 @@ import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatCo | ||
| 12 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; | 12 | import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; |
| 13 | import cn.iocoder.yudao.module.ai.service.chat.AiChatConversationService; | 13 | import cn.iocoder.yudao.module.ai.service.chat.AiChatConversationService; |
| 14 | import cn.iocoder.yudao.module.ai.service.chat.AiChatMessageService; | 14 | import cn.iocoder.yudao.module.ai.service.chat.AiChatMessageService; |
| 15 | +import com.fhs.core.trans.anno.TransMethodResult; | ||
| 15 | import io.swagger.v3.oas.annotations.Operation; | 16 | import io.swagger.v3.oas.annotations.Operation; |
| 16 | import io.swagger.v3.oas.annotations.Parameter; | 17 | import io.swagger.v3.oas.annotations.Parameter; |
| 17 | import io.swagger.v3.oas.annotations.tags.Tag; | 18 | import io.swagger.v3.oas.annotations.tags.Tag; |
| @@ -54,6 +55,7 @@ public class AiChatConversationController { | @@ -54,6 +55,7 @@ public class AiChatConversationController { | ||
| 54 | 55 | ||
| 55 | @GetMapping("/my-list") | 56 | @GetMapping("/my-list") |
| 56 | @Operation(summary = "获得【我的】聊天对话列表") | 57 | @Operation(summary = "获得【我的】聊天对话列表") |
| 58 | + @TransMethodResult | ||
| 57 | public CommonResult<List<AiChatConversationRespVO>> getChatConversationMyList() { | 59 | public CommonResult<List<AiChatConversationRespVO>> getChatConversationMyList() { |
| 58 | List<AiChatConversationDO> list = chatConversationService.getChatConversationListByUserId(getLoginUserId()); | 60 | List<AiChatConversationDO> list = chatConversationService.getChatConversationListByUserId(getLoginUserId()); |
| 59 | return success(BeanUtils.toBean(list, AiChatConversationRespVO.class)); | 61 | return success(BeanUtils.toBean(list, AiChatConversationRespVO.class)); |
| @@ -62,6 +64,7 @@ public class AiChatConversationController { | @@ -62,6 +64,7 @@ public class AiChatConversationController { | ||
| 62 | @GetMapping("/get-my") | 64 | @GetMapping("/get-my") |
| 63 | @Operation(summary = "获得【我的】聊天对话") | 65 | @Operation(summary = "获得【我的】聊天对话") |
| 64 | @Parameter(name = "id", required = true, description = "对话编号", example = "1024") | 66 | @Parameter(name = "id", required = true, description = "对话编号", example = "1024") |
| 67 | + @TransMethodResult | ||
| 65 | public CommonResult<AiChatConversationRespVO> getChatConversationMy(@RequestParam("id") Long id) { | 68 | public CommonResult<AiChatConversationRespVO> getChatConversationMy(@RequestParam("id") Long id) { |
| 66 | AiChatConversationDO conversation = chatConversationService.getChatConversation(id); | 69 | AiChatConversationDO conversation = chatConversationService.getChatConversation(id); |
| 67 | if (conversation != null && ObjUtil.notEqual(conversation.getUserId(), getLoginUserId())) { | 70 | if (conversation != null && ObjUtil.notEqual(conversation.getUserId(), getLoginUserId())) { |
| @@ -90,6 +93,7 @@ public class AiChatConversationController { | @@ -90,6 +93,7 @@ public class AiChatConversationController { | ||
| 90 | @GetMapping("/page") | 93 | @GetMapping("/page") |
| 91 | @Operation(summary = "获得对话分页", description = "用于【对话管理】菜单") | 94 | @Operation(summary = "获得对话分页", description = "用于【对话管理】菜单") |
| 92 | @PreAuthorize("@ss.hasPermission('ai:chat-conversation:query')") | 95 | @PreAuthorize("@ss.hasPermission('ai:chat-conversation:query')") |
| 96 | + @TransMethodResult | ||
| 93 | public CommonResult<PageResult<AiChatConversationRespVO>> getChatConversationPage(AiChatConversationPageReqVO pageReqVO) { | 97 | public CommonResult<PageResult<AiChatConversationRespVO>> getChatConversationPage(AiChatConversationPageReqVO pageReqVO) { |
| 94 | PageResult<AiChatConversationDO> pageResult = chatConversationService.getChatConversationPage(pageReqVO); | 98 | PageResult<AiChatConversationDO> pageResult = chatConversationService.getChatConversationPage(pageReqVO); |
| 95 | if (CollUtil.isEmpty(pageResult.getList())) { | 99 | if (CollUtil.isEmpty(pageResult.getList())) { |
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatRoleController.java
| @@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleS | @@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleS | ||
| 10 | import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveReqVO; | 10 | import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveReqVO; |
| 11 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; | 11 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; |
| 12 | import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService; | 12 | import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService; |
| 13 | +import com.fhs.core.trans.anno.TransMethodResult; | ||
| 13 | import io.swagger.v3.oas.annotations.Operation; | 14 | import io.swagger.v3.oas.annotations.Operation; |
| 14 | import io.swagger.v3.oas.annotations.Parameter; | 15 | import io.swagger.v3.oas.annotations.Parameter; |
| 15 | import io.swagger.v3.oas.annotations.tags.Tag; | 16 | import io.swagger.v3.oas.annotations.tags.Tag; |
| @@ -35,6 +36,7 @@ public class AiChatRoleController { | @@ -35,6 +36,7 @@ public class AiChatRoleController { | ||
| 35 | 36 | ||
| 36 | @GetMapping("/my-page") | 37 | @GetMapping("/my-page") |
| 37 | @Operation(summary = "获得【我的】聊天角色分页") | 38 | @Operation(summary = "获得【我的】聊天角色分页") |
| 39 | + @TransMethodResult | ||
| 38 | public CommonResult<PageResult<AiChatRoleRespVO>> getChatRoleMyPage(@Valid AiChatRolePageReqVO pageReqVO) { | 40 | public CommonResult<PageResult<AiChatRoleRespVO>> getChatRoleMyPage(@Valid AiChatRolePageReqVO pageReqVO) { |
| 39 | PageResult<AiChatRoleDO> pageResult = chatRoleService.getChatRoleMyPage(pageReqVO, getLoginUserId()); | 41 | PageResult<AiChatRoleDO> pageResult = chatRoleService.getChatRoleMyPage(pageReqVO, getLoginUserId()); |
| 40 | return success(BeanUtils.toBean(pageResult, AiChatRoleRespVO.class)); | 42 | return success(BeanUtils.toBean(pageResult, AiChatRoleRespVO.class)); |
| @@ -43,6 +45,7 @@ public class AiChatRoleController { | @@ -43,6 +45,7 @@ public class AiChatRoleController { | ||
| 43 | @GetMapping("/get-my") | 45 | @GetMapping("/get-my") |
| 44 | @Operation(summary = "获得【我的】聊天角色") | 46 | @Operation(summary = "获得【我的】聊天角色") |
| 45 | @Parameter(name = "id", description = "编号", required = true, example = "1024") | 47 | @Parameter(name = "id", description = "编号", required = true, example = "1024") |
| 48 | + @TransMethodResult | ||
| 46 | public CommonResult<AiChatRoleRespVO> getChatRoleMy(@RequestParam("id") Long id) { | 49 | public CommonResult<AiChatRoleRespVO> getChatRoleMy(@RequestParam("id") Long id) { |
| 47 | AiChatRoleDO chatRole = chatRoleService.getChatRole(id); | 50 | AiChatRoleDO chatRole = chatRoleService.getChatRole(id); |
| 48 | if (ObjUtil.notEqual(chatRole.getUserId(), getLoginUserId())) { | 51 | if (ObjUtil.notEqual(chatRole.getUserId(), getLoginUserId())) { |
| @@ -108,6 +111,7 @@ public class AiChatRoleController { | @@ -108,6 +111,7 @@ public class AiChatRoleController { | ||
| 108 | @Operation(summary = "获得聊天角色") | 111 | @Operation(summary = "获得聊天角色") |
| 109 | @Parameter(name = "id", description = "编号", required = true, example = "1024") | 112 | @Parameter(name = "id", description = "编号", required = true, example = "1024") |
| 110 | @PreAuthorize("@ss.hasPermission('ai:chat-role:query')") | 113 | @PreAuthorize("@ss.hasPermission('ai:chat-role:query')") |
| 114 | + @TransMethodResult | ||
| 111 | public CommonResult<AiChatRoleRespVO> getChatRole(@RequestParam("id") Long id) { | 115 | public CommonResult<AiChatRoleRespVO> getChatRole(@RequestParam("id") Long id) { |
| 112 | AiChatRoleDO chatRole = chatRoleService.getChatRole(id); | 116 | AiChatRoleDO chatRole = chatRoleService.getChatRole(id); |
| 113 | return success(BeanUtils.toBean(chatRole, AiChatRoleRespVO.class)); | 117 | return success(BeanUtils.toBean(chatRole, AiChatRoleRespVO.class)); |
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java
| @@ -5,7 +5,6 @@ import cn.hutool.extra.spring.SpringUtil; | @@ -5,7 +5,6 @@ import cn.hutool.extra.spring.SpringUtil; | ||
| 5 | import cn.iocoder.yudao.module.ai.framework.ai.core.AiModelFactory; | 5 | import cn.iocoder.yudao.module.ai.framework.ai.core.AiModelFactory; |
| 6 | import cn.iocoder.yudao.module.ai.framework.ai.core.AiModelFactoryImpl; | 6 | import cn.iocoder.yudao.module.ai.framework.ai.core.AiModelFactoryImpl; |
| 7 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel; | 7 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel; |
| 8 | -import cn.iocoder.yudao.module.ai.framework.ai.core.model.deepseek.DeepSeekChatModel; | ||
| 9 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel; | 8 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel; |
| 10 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel; | 9 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel; |
| 11 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi; | 10 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi; |
| @@ -14,10 +13,6 @@ import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlo | @@ -14,10 +13,6 @@ import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlo | ||
| 14 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi; | 13 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi; |
| 15 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.XingHuoChatModel; | 14 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.XingHuoChatModel; |
| 16 | import lombok.extern.slf4j.Slf4j; | 15 | import lombok.extern.slf4j.Slf4j; |
| 17 | -import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusServiceClientProperties; | ||
| 18 | -import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreProperties; | ||
| 19 | -import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreProperties; | ||
| 20 | -import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties; | ||
| 21 | import org.springframework.ai.embedding.BatchingStrategy; | 16 | import org.springframework.ai.embedding.BatchingStrategy; |
| 22 | import org.springframework.ai.embedding.TokenCountBatchingStrategy; | 17 | import org.springframework.ai.embedding.TokenCountBatchingStrategy; |
| 23 | import org.springframework.ai.model.tool.ToolCallingManager; | 18 | import org.springframework.ai.model.tool.ToolCallingManager; |
| @@ -26,6 +21,10 @@ import org.springframework.ai.openai.OpenAiChatOptions; | @@ -26,6 +21,10 @@ import org.springframework.ai.openai.OpenAiChatOptions; | ||
| 26 | import org.springframework.ai.openai.api.OpenAiApi; | 21 | import org.springframework.ai.openai.api.OpenAiApi; |
| 27 | import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator; | 22 | import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator; |
| 28 | import org.springframework.ai.tokenizer.TokenCountEstimator; | 23 | import org.springframework.ai.tokenizer.TokenCountEstimator; |
| 24 | +import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusServiceClientProperties; | ||
| 25 | +import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreProperties; | ||
| 26 | +import org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreProperties; | ||
| 27 | +import org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStoreProperties; | ||
| 29 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | 28 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| 30 | import org.springframework.boot.context.properties.EnableConfigurationProperties; | 29 | import org.springframework.boot.context.properties.EnableConfigurationProperties; |
| 31 | import org.springframework.context.annotation.Bean; | 30 | import org.springframework.context.annotation.Bean; |
| @@ -53,33 +52,6 @@ public class AiAutoConfiguration { | @@ -53,33 +52,6 @@ public class AiAutoConfiguration { | ||
| 53 | // ========== 各种 AI Client 创建 ========== | 52 | // ========== 各种 AI Client 创建 ========== |
| 54 | 53 | ||
| 55 | @Bean | 54 | @Bean |
| 56 | - @ConditionalOnProperty(value = "yudao.ai.deepseek.enable", havingValue = "true") | ||
| 57 | - public DeepSeekChatModel deepSeekChatModel(YudaoAiProperties yudaoAiProperties) { | ||
| 58 | - YudaoAiProperties.DeepSeekProperties properties = yudaoAiProperties.getDeepseek(); | ||
| 59 | - return buildDeepSeekChatModel(properties); | ||
| 60 | - } | ||
| 61 | - | ||
| 62 | - public DeepSeekChatModel buildDeepSeekChatModel(YudaoAiProperties.DeepSeekProperties properties) { | ||
| 63 | - if (StrUtil.isEmpty(properties.getModel())) { | ||
| 64 | - properties.setModel(DeepSeekChatModel.MODEL_DEFAULT); | ||
| 65 | - } | ||
| 66 | - OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() | ||
| 67 | - .openAiApi(OpenAiApi.builder() | ||
| 68 | - .baseUrl(DeepSeekChatModel.BASE_URL) | ||
| 69 | - .apiKey(properties.getApiKey()) | ||
| 70 | - .build()) | ||
| 71 | - .defaultOptions(OpenAiChatOptions.builder() | ||
| 72 | - .model(properties.getModel()) | ||
| 73 | - .temperature(properties.getTemperature()) | ||
| 74 | - .maxTokens(properties.getMaxTokens()) | ||
| 75 | - .topP(properties.getTopP()) | ||
| 76 | - .build()) | ||
| 77 | - .toolCallingManager(getToolCallingManager()) | ||
| 78 | - .build(); | ||
| 79 | - return new DeepSeekChatModel(openAiChatModel); | ||
| 80 | - } | ||
| 81 | - | ||
| 82 | - @Bean | ||
| 83 | @ConditionalOnProperty(value = "yudao.ai.doubao.enable", havingValue = "true") | 55 | @ConditionalOnProperty(value = "yudao.ai.doubao.enable", havingValue = "true") |
| 84 | public DouBaoChatModel douBaoChatClient(YudaoAiProperties yudaoAiProperties) { | 56 | public DouBaoChatModel douBaoChatClient(YudaoAiProperties yudaoAiProperties) { |
| 85 | YudaoAiProperties.DouBaoProperties properties = yudaoAiProperties.getDoubao(); | 57 | YudaoAiProperties.DouBaoProperties properties = yudaoAiProperties.getDoubao(); |
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/YudaoAiProperties.java
| @@ -14,12 +14,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties; | @@ -14,12 +14,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties; | ||
| 14 | public class YudaoAiProperties { | 14 | public class YudaoAiProperties { |
| 15 | 15 | ||
| 16 | /** | 16 | /** |
| 17 | - * DeepSeek | ||
| 18 | - */ | ||
| 19 | - @SuppressWarnings("SpellCheckingInspection") | ||
| 20 | - private DeepSeekProperties deepseek; | ||
| 21 | - | ||
| 22 | - /** | ||
| 23 | * 字节豆包 | 17 | * 字节豆包 |
| 24 | */ | 18 | */ |
| 25 | @SuppressWarnings("SpellCheckingInspection") | 19 | @SuppressWarnings("SpellCheckingInspection") |
| @@ -61,19 +55,6 @@ public class YudaoAiProperties { | @@ -61,19 +55,6 @@ public class YudaoAiProperties { | ||
| 61 | private SunoProperties suno; | 55 | private SunoProperties suno; |
| 62 | 56 | ||
| 63 | @Data | 57 | @Data |
| 64 | - public static class DeepSeekProperties { | ||
| 65 | - | ||
| 66 | - private String enable; | ||
| 67 | - private String apiKey; | ||
| 68 | - | ||
| 69 | - private String model; | ||
| 70 | - private Double temperature; | ||
| 71 | - private Integer maxTokens; | ||
| 72 | - private Double topP; | ||
| 73 | - | ||
| 74 | - } | ||
| 75 | - | ||
| 76 | - @Data | ||
| 77 | public static class DouBaoProperties { | 58 | public static class DouBaoProperties { |
| 78 | 59 | ||
| 79 | private String enable; | 60 | private String enable; |
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/AiModelFactoryImpl.java
| @@ -8,11 +8,11 @@ import cn.hutool.core.util.ArrayUtil; | @@ -8,11 +8,11 @@ import cn.hutool.core.util.ArrayUtil; | ||
| 8 | import cn.hutool.core.util.RuntimeUtil; | 8 | import cn.hutool.core.util.RuntimeUtil; |
| 9 | import cn.hutool.core.util.StrUtil; | 9 | import cn.hutool.core.util.StrUtil; |
| 10 | import cn.hutool.extra.spring.SpringUtil; | 10 | import cn.hutool.extra.spring.SpringUtil; |
| 11 | +import cn.iocoder.yudao.framework.common.util.spring.SpringUtils; | ||
| 12 | +import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum; | ||
| 11 | import cn.iocoder.yudao.module.ai.framework.ai.config.AiAutoConfiguration; | 13 | import cn.iocoder.yudao.module.ai.framework.ai.config.AiAutoConfiguration; |
| 12 | import cn.iocoder.yudao.module.ai.framework.ai.config.YudaoAiProperties; | 14 | import cn.iocoder.yudao.module.ai.framework.ai.config.YudaoAiProperties; |
| 13 | -import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum; | ||
| 14 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel; | 15 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel; |
| 15 | -import cn.iocoder.yudao.module.ai.framework.ai.core.model.deepseek.DeepSeekChatModel; | ||
| 16 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel; | 16 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel; |
| 17 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel; | 17 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel; |
| 18 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi; | 18 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi; |
| @@ -22,8 +22,9 @@ import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlo | @@ -22,8 +22,9 @@ import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlo | ||
| 22 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowImageModel; | 22 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowImageModel; |
| 23 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi; | 23 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi; |
| 24 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.XingHuoChatModel; | 24 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.XingHuoChatModel; |
| 25 | -import cn.iocoder.yudao.framework.common.util.spring.SpringUtils; | ||
| 26 | -import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAutoConfiguration; | 25 | +import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeChatAutoConfiguration; |
| 26 | +import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeEmbeddingAutoConfiguration; | ||
| 27 | +import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeImageAutoConfiguration; | ||
| 27 | import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; | 28 | import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; |
| 28 | import com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi; | 29 | import com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi; |
| 29 | import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel; | 30 | import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel; |
| @@ -32,47 +33,55 @@ import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingModel; | @@ -32,47 +33,55 @@ import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingModel; | ||
| 32 | import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingOptions; | 33 | import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingOptions; |
| 33 | import com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel; | 34 | import com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel; |
| 34 | import com.azure.ai.openai.OpenAIClientBuilder; | 35 | import com.azure.ai.openai.OpenAIClientBuilder; |
| 36 | +import com.azure.core.credential.KeyCredential; | ||
| 35 | import io.micrometer.observation.ObservationRegistry; | 37 | import io.micrometer.observation.ObservationRegistry; |
| 36 | import io.milvus.client.MilvusServiceClient; | 38 | import io.milvus.client.MilvusServiceClient; |
| 37 | import io.qdrant.client.QdrantClient; | 39 | import io.qdrant.client.QdrantClient; |
| 38 | import io.qdrant.client.QdrantGrpcClient; | 40 | import io.qdrant.client.QdrantGrpcClient; |
| 39 | import lombok.SneakyThrows; | 41 | import lombok.SneakyThrows; |
| 40 | -import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiAutoConfiguration; | ||
| 41 | -import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiChatProperties; | ||
| 42 | -import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiConnectionProperties; | ||
| 43 | -import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiEmbeddingProperties; | ||
| 44 | -import org.springframework.ai.autoconfigure.minimax.MiniMaxAutoConfiguration; | ||
| 45 | -import org.springframework.ai.autoconfigure.moonshot.MoonshotAutoConfiguration; | ||
| 46 | -import org.springframework.ai.autoconfigure.ollama.OllamaAutoConfiguration; | ||
| 47 | -import org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration; | ||
| 48 | -import org.springframework.ai.autoconfigure.qianfan.QianFanAutoConfiguration; | ||
| 49 | -import org.springframework.ai.autoconfigure.stabilityai.StabilityAiImageAutoConfiguration; | ||
| 50 | -import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusServiceClientConnectionDetails; | ||
| 51 | -import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusServiceClientProperties; | ||
| 52 | -import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreAutoConfiguration; | ||
| 53 | -import org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreProperties; | ||
| 54 | -import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreAutoConfiguration; | ||
| 55 | -import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreProperties; | ||
| 56 | -import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreAutoConfiguration; | ||
| 57 | -import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties; | ||
| 58 | -import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiAutoConfiguration; | 42 | +import org.springaicommunity.moonshot.MoonshotChatModel; |
| 43 | +import org.springaicommunity.moonshot.MoonshotChatOptions; | ||
| 44 | +import org.springaicommunity.moonshot.api.MoonshotApi; | ||
| 45 | +import org.springaicommunity.moonshot.autoconfigure.MoonshotChatAutoConfiguration; | ||
| 46 | +import org.springaicommunity.qianfan.QianFanChatModel; | ||
| 47 | +import org.springaicommunity.qianfan.QianFanEmbeddingModel; | ||
| 48 | +import org.springaicommunity.qianfan.QianFanEmbeddingOptions; | ||
| 49 | +import org.springaicommunity.qianfan.QianFanImageModel; | ||
| 50 | +import org.springaicommunity.qianfan.api.QianFanApi; | ||
| 51 | +import org.springaicommunity.qianfan.api.QianFanImageApi; | ||
| 52 | +import org.springaicommunity.qianfan.autoconfigure.QianFanChatAutoConfiguration; | ||
| 53 | +import org.springaicommunity.qianfan.autoconfigure.QianFanEmbeddingAutoConfiguration; | ||
| 59 | import org.springframework.ai.azure.openai.AzureOpenAiChatModel; | 54 | import org.springframework.ai.azure.openai.AzureOpenAiChatModel; |
| 60 | import org.springframework.ai.azure.openai.AzureOpenAiEmbeddingModel; | 55 | import org.springframework.ai.azure.openai.AzureOpenAiEmbeddingModel; |
| 61 | import org.springframework.ai.chat.model.ChatModel; | 56 | import org.springframework.ai.chat.model.ChatModel; |
| 57 | +import org.springframework.ai.deepseek.DeepSeekChatModel; | ||
| 58 | +import org.springframework.ai.deepseek.DeepSeekChatOptions; | ||
| 59 | +import org.springframework.ai.deepseek.api.DeepSeekApi; | ||
| 62 | import org.springframework.ai.document.MetadataMode; | 60 | import org.springframework.ai.document.MetadataMode; |
| 63 | import org.springframework.ai.embedding.BatchingStrategy; | 61 | import org.springframework.ai.embedding.BatchingStrategy; |
| 64 | import org.springframework.ai.embedding.EmbeddingModel; | 62 | import org.springframework.ai.embedding.EmbeddingModel; |
| 63 | +import org.springframework.ai.embedding.observation.EmbeddingModelObservationConvention; | ||
| 65 | import org.springframework.ai.image.ImageModel; | 64 | import org.springframework.ai.image.ImageModel; |
| 66 | import org.springframework.ai.minimax.MiniMaxChatModel; | 65 | import org.springframework.ai.minimax.MiniMaxChatModel; |
| 67 | import org.springframework.ai.minimax.MiniMaxChatOptions; | 66 | import org.springframework.ai.minimax.MiniMaxChatOptions; |
| 68 | import org.springframework.ai.minimax.MiniMaxEmbeddingModel; | 67 | import org.springframework.ai.minimax.MiniMaxEmbeddingModel; |
| 69 | import org.springframework.ai.minimax.MiniMaxEmbeddingOptions; | 68 | import org.springframework.ai.minimax.MiniMaxEmbeddingOptions; |
| 70 | import org.springframework.ai.minimax.api.MiniMaxApi; | 69 | import org.springframework.ai.minimax.api.MiniMaxApi; |
| 71 | -import org.springframework.ai.model.function.FunctionCallbackResolver; | 70 | +import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiChatAutoConfiguration; |
| 71 | +import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiEmbeddingAutoConfiguration; | ||
| 72 | +import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiEmbeddingProperties; | ||
| 73 | +import org.springframework.ai.model.deepseek.autoconfigure.DeepSeekChatAutoConfiguration; | ||
| 74 | +import org.springframework.ai.model.minimax.autoconfigure.MiniMaxChatAutoConfiguration; | ||
| 75 | +import org.springframework.ai.model.minimax.autoconfigure.MiniMaxEmbeddingAutoConfiguration; | ||
| 76 | +import org.springframework.ai.model.ollama.autoconfigure.OllamaChatAutoConfiguration; | ||
| 77 | +import org.springframework.ai.model.openai.autoconfigure.OpenAiChatAutoConfiguration; | ||
| 78 | +import org.springframework.ai.model.openai.autoconfigure.OpenAiEmbeddingAutoConfiguration; | ||
| 79 | +import org.springframework.ai.model.openai.autoconfigure.OpenAiImageAutoConfiguration; | ||
| 80 | +import org.springframework.ai.model.stabilityai.autoconfigure.StabilityAiImageAutoConfiguration; | ||
| 72 | import org.springframework.ai.model.tool.ToolCallingManager; | 81 | import org.springframework.ai.model.tool.ToolCallingManager; |
| 73 | -import org.springframework.ai.moonshot.MoonshotChatModel; | ||
| 74 | -import org.springframework.ai.moonshot.MoonshotChatOptions; | ||
| 75 | -import org.springframework.ai.moonshot.api.MoonshotApi; | 82 | +import org.springframework.ai.model.zhipuai.autoconfigure.ZhiPuAiChatAutoConfiguration; |
| 83 | +import org.springframework.ai.model.zhipuai.autoconfigure.ZhiPuAiEmbeddingAutoConfiguration; | ||
| 84 | +import org.springframework.ai.model.zhipuai.autoconfigure.ZhiPuAiImageAutoConfiguration; | ||
| 76 | import org.springframework.ai.ollama.OllamaChatModel; | 85 | import org.springframework.ai.ollama.OllamaChatModel; |
| 77 | import org.springframework.ai.ollama.OllamaEmbeddingModel; | 86 | import org.springframework.ai.ollama.OllamaEmbeddingModel; |
| 78 | import org.springframework.ai.ollama.api.OllamaApi; | 87 | import org.springframework.ai.ollama.api.OllamaApi; |
| @@ -84,21 +93,23 @@ import org.springframework.ai.openai.OpenAiImageModel; | @@ -84,21 +93,23 @@ import org.springframework.ai.openai.OpenAiImageModel; | ||
| 84 | import org.springframework.ai.openai.api.OpenAiApi; | 93 | import org.springframework.ai.openai.api.OpenAiApi; |
| 85 | import org.springframework.ai.openai.api.OpenAiImageApi; | 94 | import org.springframework.ai.openai.api.OpenAiImageApi; |
| 86 | import org.springframework.ai.openai.api.common.OpenAiApiConstants; | 95 | import org.springframework.ai.openai.api.common.OpenAiApiConstants; |
| 87 | -import org.springframework.ai.qianfan.QianFanChatModel; | ||
| 88 | -import org.springframework.ai.qianfan.QianFanEmbeddingModel; | ||
| 89 | -import org.springframework.ai.qianfan.QianFanEmbeddingOptions; | ||
| 90 | -import org.springframework.ai.qianfan.QianFanImageModel; | ||
| 91 | -import org.springframework.ai.qianfan.api.QianFanApi; | ||
| 92 | -import org.springframework.ai.qianfan.api.QianFanImageApi; | ||
| 93 | import org.springframework.ai.stabilityai.StabilityAiImageModel; | 96 | import org.springframework.ai.stabilityai.StabilityAiImageModel; |
| 94 | import org.springframework.ai.stabilityai.api.StabilityAiApi; | 97 | import org.springframework.ai.stabilityai.api.StabilityAiApi; |
| 95 | import org.springframework.ai.vectorstore.SimpleVectorStore; | 98 | import org.springframework.ai.vectorstore.SimpleVectorStore; |
| 96 | import org.springframework.ai.vectorstore.VectorStore; | 99 | import org.springframework.ai.vectorstore.VectorStore; |
| 97 | import org.springframework.ai.vectorstore.milvus.MilvusVectorStore; | 100 | import org.springframework.ai.vectorstore.milvus.MilvusVectorStore; |
| 101 | +import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusServiceClientConnectionDetails; | ||
| 102 | +import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusServiceClientProperties; | ||
| 103 | +import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreAutoConfiguration; | ||
| 104 | +import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreProperties; | ||
| 98 | import org.springframework.ai.vectorstore.observation.DefaultVectorStoreObservationConvention; | 105 | import org.springframework.ai.vectorstore.observation.DefaultVectorStoreObservationConvention; |
| 99 | import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention; | 106 | import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention; |
| 100 | import org.springframework.ai.vectorstore.qdrant.QdrantVectorStore; | 107 | import org.springframework.ai.vectorstore.qdrant.QdrantVectorStore; |
| 108 | +import org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreAutoConfiguration; | ||
| 109 | +import org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreProperties; | ||
| 101 | import org.springframework.ai.vectorstore.redis.RedisVectorStore; | 110 | import org.springframework.ai.vectorstore.redis.RedisVectorStore; |
| 111 | +import org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStoreAutoConfiguration; | ||
| 112 | +import org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStoreProperties; | ||
| 102 | import org.springframework.ai.zhipuai.*; | 113 | import org.springframework.ai.zhipuai.*; |
| 103 | import org.springframework.ai.zhipuai.api.ZhiPuAiApi; | 114 | import org.springframework.ai.zhipuai.api.ZhiPuAiApi; |
| 104 | import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi; | 115 | import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi; |
| @@ -190,7 +201,7 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -190,7 +201,7 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 190 | case XING_HUO: | 201 | case XING_HUO: |
| 191 | return SpringUtil.getBean(XingHuoChatModel.class); | 202 | return SpringUtil.getBean(XingHuoChatModel.class); |
| 192 | case BAI_CHUAN: | 203 | case BAI_CHUAN: |
| 193 | - return SpringUtil.getBean(AzureOpenAiChatModel.class); | 204 | + return SpringUtil.getBean(BaiChuanChatModel.class); |
| 194 | case OPENAI: | 205 | case OPENAI: |
| 195 | return SpringUtil.getBean(OpenAiChatModel.class); | 206 | return SpringUtil.getBean(OpenAiChatModel.class); |
| 196 | case AZURE_OPENAI: | 207 | case AZURE_OPENAI: |
| @@ -319,27 +330,34 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -319,27 +330,34 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 319 | // ========== 各种创建 spring-ai 客户端的方法 ========== | 330 | // ========== 各种创建 spring-ai 客户端的方法 ========== |
| 320 | 331 | ||
| 321 | /** | 332 | /** |
| 322 | - * 可参考 {@link DashScopeAutoConfiguration} 的 dashscopeChatModel 方法 | 333 | + * 可参考 {@link DashScopeChatAutoConfiguration} 的 dashscopeChatModel 方法 |
| 323 | */ | 334 | */ |
| 324 | private static DashScopeChatModel buildTongYiChatModel(String key) { | 335 | private static DashScopeChatModel buildTongYiChatModel(String key) { |
| 325 | - DashScopeApi dashScopeApi = new DashScopeApi(key); | 336 | + DashScopeApi dashScopeApi = DashScopeApi.builder().apiKey(key).build(); |
| 326 | DashScopeChatOptions options = DashScopeChatOptions.builder().withModel(DashScopeApi.DEFAULT_CHAT_MODEL) | 337 | DashScopeChatOptions options = DashScopeChatOptions.builder().withModel(DashScopeApi.DEFAULT_CHAT_MODEL) |
| 327 | .withTemperature(0.7).build(); | 338 | .withTemperature(0.7).build(); |
| 328 | - return new DashScopeChatModel(dashScopeApi, options, getFunctionCallbackResolver(), DEFAULT_RETRY_TEMPLATE); | 339 | + return DashScopeChatModel.builder() |
| 340 | + .dashScopeApi(dashScopeApi) | ||
| 341 | + .defaultOptions(options) | ||
| 342 | + .toolCallingManager(getToolCallingManager()) | ||
| 343 | + .build(); | ||
| 329 | } | 344 | } |
| 330 | 345 | ||
| 331 | /** | 346 | /** |
| 332 | - * 可参考 {@link DashScopeAutoConfiguration} 的 dashScopeImageModel 方法 | 347 | + * 可参考 {@link DashScopeImageAutoConfiguration} 的 dashScopeImageModel 方法 |
| 333 | */ | 348 | */ |
| 334 | private static DashScopeImageModel buildTongYiImagesModel(String key) { | 349 | private static DashScopeImageModel buildTongYiImagesModel(String key) { |
| 335 | DashScopeImageApi dashScopeImageApi = new DashScopeImageApi(key); | 350 | DashScopeImageApi dashScopeImageApi = new DashScopeImageApi(key); |
| 336 | - return new DashScopeImageModel(dashScopeImageApi); | 351 | + return DashScopeImageModel.builder() |
| 352 | + .dashScopeApi(dashScopeImageApi) | ||
| 353 | + .build(); | ||
| 337 | } | 354 | } |
| 338 | 355 | ||
| 339 | /** | 356 | /** |
| 340 | - * 可参考 {@link QianFanAutoConfiguration} 的 qianFanChatModel 方法 | 357 | + * 可参考 {@link QianFanChatAutoConfiguration} 的 qianFanChatModel 方法 |
| 341 | */ | 358 | */ |
| 342 | private static QianFanChatModel buildYiYanChatModel(String key) { | 359 | private static QianFanChatModel buildYiYanChatModel(String key) { |
| 360 | + // TODO spring ai qianfan 有 bug,无法使用 https://github.com/spring-ai-community/qianfan/issues/6 | ||
| 343 | List<String> keys = StrUtil.split(key, '|'); | 361 | List<String> keys = StrUtil.split(key, '|'); |
| 344 | Assert.equals(keys.size(), 2, "YiYanChatClient 的密钥需要 (appKey|secretKey) 格式"); | 362 | Assert.equals(keys.size(), 2, "YiYanChatClient 的密钥需要 (appKey|secretKey) 格式"); |
| 345 | String appKey = keys.get(0); | 363 | String appKey = keys.get(0); |
| @@ -349,9 +367,10 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -349,9 +367,10 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 349 | } | 367 | } |
| 350 | 368 | ||
| 351 | /** | 369 | /** |
| 352 | - * 可参考 {@link QianFanAutoConfiguration} 的 qianFanImageModel 方法 | 370 | + * 可参考 {@link QianFanEmbeddingAutoConfiguration} 的 qianFanImageModel 方法 |
| 353 | */ | 371 | */ |
| 354 | private QianFanImageModel buildQianFanImageModel(String key) { | 372 | private QianFanImageModel buildQianFanImageModel(String key) { |
| 373 | + // TODO spring ai qianfan 有 bug,无法使用 https://github.com/spring-ai-community/qianfan/issues/6 | ||
| 355 | List<String> keys = StrUtil.split(key, '|'); | 374 | List<String> keys = StrUtil.split(key, '|'); |
| 356 | Assert.equals(keys.size(), 2, "YiYanChatClient 的密钥需要 (appKey|secretKey) 格式"); | 375 | Assert.equals(keys.size(), 2, "YiYanChatClient 的密钥需要 (appKey|secretKey) 格式"); |
| 357 | String appKey = keys.get(0); | 376 | String appKey = keys.get(0); |
| @@ -361,12 +380,17 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -361,12 +380,17 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 361 | } | 380 | } |
| 362 | 381 | ||
| 363 | /** | 382 | /** |
| 364 | - * 可参考 {@link AiAutoConfiguration#deepSeekChatModel(YudaoAiProperties)} | 383 | + * 可参考 {@link DeepSeekChatAutoConfiguration} 的 deepSeekChatModel 方法 |
| 365 | */ | 384 | */ |
| 366 | private static DeepSeekChatModel buildDeepSeekChatModel(String apiKey) { | 385 | private static DeepSeekChatModel buildDeepSeekChatModel(String apiKey) { |
| 367 | - YudaoAiProperties.DeepSeekProperties properties = new YudaoAiProperties.DeepSeekProperties() | ||
| 368 | - .setApiKey(apiKey); | ||
| 369 | - return new AiAutoConfiguration().buildDeepSeekChatModel(properties); | 386 | + DeepSeekApi deepSeekApi = DeepSeekApi.builder().apiKey(apiKey).build(); |
| 387 | + DeepSeekChatOptions options = DeepSeekChatOptions.builder().model(DeepSeekApi.DEFAULT_CHAT_MODEL) | ||
| 388 | + .temperature(0.7).build(); | ||
| 389 | + return DeepSeekChatModel.builder() | ||
| 390 | + .deepSeekApi(deepSeekApi) | ||
| 391 | + .defaultOptions(options) | ||
| 392 | + .toolCallingManager(getToolCallingManager()) | ||
| 393 | + .build(); | ||
| 370 | } | 394 | } |
| 371 | 395 | ||
| 372 | /** | 396 | /** |
| @@ -397,17 +421,18 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -397,17 +421,18 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 397 | } | 421 | } |
| 398 | 422 | ||
| 399 | /** | 423 | /** |
| 400 | - * 可参考 {@link ZhiPuAiAutoConfiguration} 的 zhiPuAiChatModel 方法 | 424 | + * 可参考 {@link ZhiPuAiChatAutoConfiguration} 的 zhiPuAiChatModel 方法 |
| 401 | */ | 425 | */ |
| 402 | private ZhiPuAiChatModel buildZhiPuChatModel(String apiKey, String url) { | 426 | private ZhiPuAiChatModel buildZhiPuChatModel(String apiKey, String url) { |
| 403 | ZhiPuAiApi zhiPuAiApi = StrUtil.isEmpty(url) ? new ZhiPuAiApi(apiKey) | 427 | ZhiPuAiApi zhiPuAiApi = StrUtil.isEmpty(url) ? new ZhiPuAiApi(apiKey) |
| 404 | : new ZhiPuAiApi(url, apiKey); | 428 | : new ZhiPuAiApi(url, apiKey); |
| 405 | ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder().model(ZhiPuAiApi.DEFAULT_CHAT_MODEL).temperature(0.7).build(); | 429 | ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder().model(ZhiPuAiApi.DEFAULT_CHAT_MODEL).temperature(0.7).build(); |
| 406 | - return new ZhiPuAiChatModel(zhiPuAiApi, options, getFunctionCallbackResolver(), DEFAULT_RETRY_TEMPLATE); | 430 | + return new ZhiPuAiChatModel(zhiPuAiApi, options, getToolCallingManager(), DEFAULT_RETRY_TEMPLATE, |
| 431 | + getObservationRegistry().getIfAvailable()); | ||
| 407 | } | 432 | } |
| 408 | 433 | ||
| 409 | /** | 434 | /** |
| 410 | - * 可参考 {@link ZhiPuAiAutoConfiguration} 的 zhiPuAiImageModel 方法 | 435 | + * 可参考 {@link ZhiPuAiImageAutoConfiguration} 的 zhiPuAiImageModel 方法 |
| 411 | */ | 436 | */ |
| 412 | private ZhiPuAiImageModel buildZhiPuAiImageModel(String apiKey, String url) { | 437 | private ZhiPuAiImageModel buildZhiPuAiImageModel(String apiKey, String url) { |
| 413 | ZhiPuAiImageApi zhiPuAiApi = StrUtil.isEmpty(url) ? new ZhiPuAiImageApi(apiKey) | 438 | ZhiPuAiImageApi zhiPuAiApi = StrUtil.isEmpty(url) ? new ZhiPuAiImageApi(apiKey) |
| @@ -416,23 +441,30 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -416,23 +441,30 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 416 | } | 441 | } |
| 417 | 442 | ||
| 418 | /** | 443 | /** |
| 419 | - * 可参考 {@link MiniMaxAutoConfiguration} 的 miniMaxChatModel 方法 | 444 | + * 可参考 {@link MiniMaxChatAutoConfiguration} 的 miniMaxChatModel 方法 |
| 420 | */ | 445 | */ |
| 421 | private MiniMaxChatModel buildMiniMaxChatModel(String apiKey, String url) { | 446 | private MiniMaxChatModel buildMiniMaxChatModel(String apiKey, String url) { |
| 422 | MiniMaxApi miniMaxApi = StrUtil.isEmpty(url) ? new MiniMaxApi(apiKey) | 447 | MiniMaxApi miniMaxApi = StrUtil.isEmpty(url) ? new MiniMaxApi(apiKey) |
| 423 | : new MiniMaxApi(url, apiKey); | 448 | : new MiniMaxApi(url, apiKey); |
| 424 | MiniMaxChatOptions options = MiniMaxChatOptions.builder().model(MiniMaxApi.DEFAULT_CHAT_MODEL).temperature(0.7).build(); | 449 | MiniMaxChatOptions options = MiniMaxChatOptions.builder().model(MiniMaxApi.DEFAULT_CHAT_MODEL).temperature(0.7).build(); |
| 425 | - return new MiniMaxChatModel(miniMaxApi, options, getFunctionCallbackResolver(), DEFAULT_RETRY_TEMPLATE); | 450 | + return new MiniMaxChatModel(miniMaxApi, options, getToolCallingManager(), DEFAULT_RETRY_TEMPLATE); |
| 426 | } | 451 | } |
| 427 | 452 | ||
| 428 | /** | 453 | /** |
| 429 | - * 可参考 {@link MoonshotAutoConfiguration} 的 moonshotChatModel 方法 | 454 | + * 可参考 {@link MoonshotChatAutoConfiguration} 的 moonshotChatModel 方法 |
| 430 | */ | 455 | */ |
| 431 | private MoonshotChatModel buildMoonshotChatModel(String apiKey, String url) { | 456 | private MoonshotChatModel buildMoonshotChatModel(String apiKey, String url) { |
| 432 | - MoonshotApi moonshotApi = StrUtil.isEmpty(url)? new MoonshotApi(apiKey) | ||
| 433 | - : new MoonshotApi(url, apiKey); | 457 | + MoonshotApi.Builder moonshotApiBuilder = MoonshotApi.builder() |
| 458 | + .apiKey(apiKey); | ||
| 459 | + if (StrUtil.isNotEmpty(url)) { | ||
| 460 | + moonshotApiBuilder.baseUrl(url); | ||
| 461 | + } | ||
| 434 | MoonshotChatOptions options = MoonshotChatOptions.builder().model(MoonshotApi.DEFAULT_CHAT_MODEL).build(); | 462 | MoonshotChatOptions options = MoonshotChatOptions.builder().model(MoonshotApi.DEFAULT_CHAT_MODEL).build(); |
| 435 | - return new MoonshotChatModel(moonshotApi, options, getFunctionCallbackResolver(), DEFAULT_RETRY_TEMPLATE); | 463 | + return MoonshotChatModel.builder() |
| 464 | + .moonshotApi(moonshotApiBuilder.build()) | ||
| 465 | + .defaultOptions(options) | ||
| 466 | + .toolCallingManager(getToolCallingManager()) | ||
| 467 | + .build(); | ||
| 436 | } | 468 | } |
| 437 | 469 | ||
| 438 | /** | 470 | /** |
| @@ -456,33 +488,32 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -456,33 +488,32 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 456 | } | 488 | } |
| 457 | 489 | ||
| 458 | /** | 490 | /** |
| 459 | - * 可参考 {@link OpenAiAutoConfiguration} 的 openAiChatModel 方法 | 491 | + * 可参考 {@link OpenAiChatAutoConfiguration} 的 openAiChatModel 方法 |
| 460 | */ | 492 | */ |
| 461 | private static OpenAiChatModel buildOpenAiChatModel(String openAiToken, String url) { | 493 | private static OpenAiChatModel buildOpenAiChatModel(String openAiToken, String url) { |
| 462 | url = StrUtil.blankToDefault(url, OpenAiApiConstants.DEFAULT_BASE_URL); | 494 | url = StrUtil.blankToDefault(url, OpenAiApiConstants.DEFAULT_BASE_URL); |
| 463 | OpenAiApi openAiApi = OpenAiApi.builder().baseUrl(url).apiKey(openAiToken).build(); | 495 | OpenAiApi openAiApi = OpenAiApi.builder().baseUrl(url).apiKey(openAiToken).build(); |
| 464 | - return OpenAiChatModel.builder().openAiApi(openAiApi).toolCallingManager(getToolCallingManager()).build(); | 496 | + return OpenAiChatModel.builder() |
| 497 | + .openAiApi(openAiApi) | ||
| 498 | + .toolCallingManager(getToolCallingManager()) | ||
| 499 | + .build(); | ||
| 465 | } | 500 | } |
| 466 | 501 | ||
| 467 | - // TODO @芋艿:手头暂时没密钥,使用建议再测试下 | ||
| 468 | /** | 502 | /** |
| 469 | - * 可参考 {@link AzureOpenAiAutoConfiguration} | 503 | + * 可参考 {@link AzureOpenAiChatAutoConfiguration} |
| 470 | */ | 504 | */ |
| 471 | private static AzureOpenAiChatModel buildAzureOpenAiChatModel(String apiKey, String url) { | 505 | private static AzureOpenAiChatModel buildAzureOpenAiChatModel(String apiKey, String url) { |
| 472 | - AzureOpenAiAutoConfiguration azureOpenAiAutoConfiguration = new AzureOpenAiAutoConfiguration(); | ||
| 473 | - // 创建 OpenAIClient 对象 | ||
| 474 | - AzureOpenAiConnectionProperties connectionProperties = new AzureOpenAiConnectionProperties(); | ||
| 475 | - connectionProperties.setApiKey(apiKey); | ||
| 476 | - connectionProperties.setEndpoint(url); | ||
| 477 | - OpenAIClientBuilder openAIClient = azureOpenAiAutoConfiguration.openAIClientBuilder(connectionProperties, null); | ||
| 478 | - // 获取 AzureOpenAiChatProperties 对象 | ||
| 479 | - AzureOpenAiChatProperties chatProperties = SpringUtil.getBean(AzureOpenAiChatProperties.class); | ||
| 480 | - return azureOpenAiAutoConfiguration.azureOpenAiChatModel(openAIClient, chatProperties, | ||
| 481 | - getToolCallingManager(), null, null); | 506 | + // TODO @芋艿:使用前,请测试,暂时没密钥!!! |
| 507 | + OpenAIClientBuilder openAIClientBuilder = new OpenAIClientBuilder() | ||
| 508 | + .endpoint(url).credential(new KeyCredential(apiKey)); | ||
| 509 | + return AzureOpenAiChatModel.builder() | ||
| 510 | + .openAIClientBuilder(openAIClientBuilder) | ||
| 511 | + .toolCallingManager(getToolCallingManager()) | ||
| 512 | + .build(); | ||
| 482 | } | 513 | } |
| 483 | 514 | ||
| 484 | /** | 515 | /** |
| 485 | - * 可参考 {@link OpenAiAutoConfiguration} 的 openAiImageModel 方法 | 516 | + * 可参考 {@link OpenAiImageAutoConfiguration} 的 openAiImageModel 方法 |
| 486 | */ | 517 | */ |
| 487 | private OpenAiImageModel buildOpenAiImageModel(String openAiToken, String url) { | 518 | private OpenAiImageModel buildOpenAiImageModel(String openAiToken, String url) { |
| 488 | url = StrUtil.blankToDefault(url, OpenAiApiConstants.DEFAULT_BASE_URL); | 519 | url = StrUtil.blankToDefault(url, OpenAiApiConstants.DEFAULT_BASE_URL); |
| @@ -500,11 +531,14 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -500,11 +531,14 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 500 | } | 531 | } |
| 501 | 532 | ||
| 502 | /** | 533 | /** |
| 503 | - * 可参考 {@link OllamaAutoConfiguration} 的 ollamaApi 方法 | 534 | + * 可参考 {@link OllamaChatAutoConfiguration} 的 ollamaChatModel 方法 |
| 504 | */ | 535 | */ |
| 505 | private static OllamaChatModel buildOllamaChatModel(String url) { | 536 | private static OllamaChatModel buildOllamaChatModel(String url) { |
| 506 | - OllamaApi ollamaApi = new OllamaApi(url); | ||
| 507 | - return OllamaChatModel.builder().ollamaApi(ollamaApi).toolCallingManager(getToolCallingManager()).build(); | 537 | + OllamaApi ollamaApi = OllamaApi.builder().baseUrl(url).build(); |
| 538 | + return OllamaChatModel.builder() | ||
| 539 | + .ollamaApi(ollamaApi) | ||
| 540 | + .toolCallingManager(getToolCallingManager()) | ||
| 541 | + .build(); | ||
| 508 | } | 542 | } |
| 509 | 543 | ||
| 510 | /** | 544 | /** |
| @@ -519,16 +553,16 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -519,16 +553,16 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 519 | // ========== 各种创建 EmbeddingModel 的方法 ========== | 553 | // ========== 各种创建 EmbeddingModel 的方法 ========== |
| 520 | 554 | ||
| 521 | /** | 555 | /** |
| 522 | - * 可参考 {@link DashScopeAutoConfiguration} 的 dashscopeEmbeddingModel 方法 | 556 | + * 可参考 {@link DashScopeEmbeddingAutoConfiguration} 的 dashscopeEmbeddingModel 方法 |
| 523 | */ | 557 | */ |
| 524 | private DashScopeEmbeddingModel buildTongYiEmbeddingModel(String apiKey, String model) { | 558 | private DashScopeEmbeddingModel buildTongYiEmbeddingModel(String apiKey, String model) { |
| 525 | - DashScopeApi dashScopeApi = new DashScopeApi(apiKey); | 559 | + DashScopeApi dashScopeApi = DashScopeApi.builder().apiKey(apiKey).build(); |
| 526 | DashScopeEmbeddingOptions dashScopeEmbeddingOptions = DashScopeEmbeddingOptions.builder().withModel(model).build(); | 560 | DashScopeEmbeddingOptions dashScopeEmbeddingOptions = DashScopeEmbeddingOptions.builder().withModel(model).build(); |
| 527 | return new DashScopeEmbeddingModel(dashScopeApi, MetadataMode.EMBED, dashScopeEmbeddingOptions); | 561 | return new DashScopeEmbeddingModel(dashScopeApi, MetadataMode.EMBED, dashScopeEmbeddingOptions); |
| 528 | } | 562 | } |
| 529 | 563 | ||
| 530 | /** | 564 | /** |
| 531 | - * 可参考 {@link ZhiPuAiAutoConfiguration} 的 zhiPuAiEmbeddingModel 方法 | 565 | + * 可参考 {@link ZhiPuAiEmbeddingAutoConfiguration} 的 zhiPuAiEmbeddingModel 方法 |
| 532 | */ | 566 | */ |
| 533 | private ZhiPuAiEmbeddingModel buildZhiPuEmbeddingModel(String apiKey, String url, String model) { | 567 | private ZhiPuAiEmbeddingModel buildZhiPuEmbeddingModel(String apiKey, String url, String model) { |
| 534 | ZhiPuAiApi zhiPuAiApi = StrUtil.isEmpty(url) ? new ZhiPuAiApi(apiKey) | 568 | ZhiPuAiApi zhiPuAiApi = StrUtil.isEmpty(url) ? new ZhiPuAiApi(apiKey) |
| @@ -538,7 +572,7 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -538,7 +572,7 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 538 | } | 572 | } |
| 539 | 573 | ||
| 540 | /** | 574 | /** |
| 541 | - * 可参考 {@link MiniMaxAutoConfiguration} 的 miniMaxEmbeddingModel 方法 | 575 | + * 可参考 {@link MiniMaxEmbeddingAutoConfiguration} 的 miniMaxEmbeddingModel 方法 |
| 542 | */ | 576 | */ |
| 543 | private EmbeddingModel buildMiniMaxEmbeddingModel(String apiKey, String url, String model) { | 577 | private EmbeddingModel buildMiniMaxEmbeddingModel(String apiKey, String url, String model) { |
| 544 | MiniMaxApi miniMaxApi = StrUtil.isEmpty(url)? new MiniMaxApi(apiKey) | 578 | MiniMaxApi miniMaxApi = StrUtil.isEmpty(url)? new MiniMaxApi(apiKey) |
| @@ -548,7 +582,7 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -548,7 +582,7 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 548 | } | 582 | } |
| 549 | 583 | ||
| 550 | /** | 584 | /** |
| 551 | - * 可参考 {@link QianFanAutoConfiguration} 的 qianFanEmbeddingModel 方法 | 585 | + * 可参考 {@link QianFanEmbeddingAutoConfiguration} 的 qianFanEmbeddingModel 方法 |
| 552 | */ | 586 | */ |
| 553 | private QianFanEmbeddingModel buildYiYanEmbeddingModel(String key, String model) { | 587 | private QianFanEmbeddingModel buildYiYanEmbeddingModel(String key, String model) { |
| 554 | List<String> keys = StrUtil.split(key, '|'); | 588 | List<String> keys = StrUtil.split(key, '|'); |
| @@ -561,13 +595,16 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -561,13 +595,16 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 561 | } | 595 | } |
| 562 | 596 | ||
| 563 | private OllamaEmbeddingModel buildOllamaEmbeddingModel(String url, String model) { | 597 | private OllamaEmbeddingModel buildOllamaEmbeddingModel(String url, String model) { |
| 564 | - OllamaApi ollamaApi = new OllamaApi(url); | 598 | + OllamaApi ollamaApi = OllamaApi.builder().baseUrl(url).build(); |
| 565 | OllamaOptions ollamaOptions = OllamaOptions.builder().model(model).build(); | 599 | OllamaOptions ollamaOptions = OllamaOptions.builder().model(model).build(); |
| 566 | - return OllamaEmbeddingModel.builder().ollamaApi(ollamaApi).defaultOptions(ollamaOptions).build(); | 600 | + return OllamaEmbeddingModel.builder() |
| 601 | + .ollamaApi(ollamaApi) | ||
| 602 | + .defaultOptions(ollamaOptions) | ||
| 603 | + .build(); | ||
| 567 | } | 604 | } |
| 568 | 605 | ||
| 569 | /** | 606 | /** |
| 570 | - * 可参考 {@link OpenAiAutoConfiguration} 的 openAiEmbeddingModel 方法 | 607 | + * 可参考 {@link OpenAiEmbeddingAutoConfiguration} 的 openAiEmbeddingModel 方法 |
| 571 | */ | 608 | */ |
| 572 | private OpenAiEmbeddingModel buildOpenAiEmbeddingModel(String openAiToken, String url, String model) { | 609 | private OpenAiEmbeddingModel buildOpenAiEmbeddingModel(String openAiToken, String url, String model) { |
| 573 | url = StrUtil.blankToDefault(url, OpenAiApiConstants.DEFAULT_BASE_URL); | 610 | url = StrUtil.blankToDefault(url, OpenAiApiConstants.DEFAULT_BASE_URL); |
| @@ -576,21 +613,19 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -576,21 +613,19 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 576 | return new OpenAiEmbeddingModel(openAiApi, MetadataMode.EMBED, openAiEmbeddingProperties); | 613 | return new OpenAiEmbeddingModel(openAiApi, MetadataMode.EMBED, openAiEmbeddingProperties); |
| 577 | } | 614 | } |
| 578 | 615 | ||
| 579 | - // TODO @芋艿:手头暂时没密钥,使用建议再测试下 | ||
| 580 | /** | 616 | /** |
| 581 | - * 可参考 {@link AzureOpenAiAutoConfiguration} 的 azureOpenAiEmbeddingModel 方法 | 617 | + * 可参考 {@link AzureOpenAiEmbeddingAutoConfiguration} 的 azureOpenAiEmbeddingModel 方法 |
| 582 | */ | 618 | */ |
| 583 | private AzureOpenAiEmbeddingModel buildAzureOpenAiEmbeddingModel(String apiKey, String url, String model) { | 619 | private AzureOpenAiEmbeddingModel buildAzureOpenAiEmbeddingModel(String apiKey, String url, String model) { |
| 584 | - AzureOpenAiAutoConfiguration azureOpenAiAutoConfiguration = new AzureOpenAiAutoConfiguration(); | ||
| 585 | - // 创建 OpenAIClient 对象 | ||
| 586 | - AzureOpenAiConnectionProperties connectionProperties = new AzureOpenAiConnectionProperties(); | ||
| 587 | - connectionProperties.setApiKey(apiKey); | ||
| 588 | - connectionProperties.setEndpoint(url); | ||
| 589 | - OpenAIClientBuilder openAIClient = azureOpenAiAutoConfiguration.openAIClientBuilder(connectionProperties, null); | 620 | + // TODO @芋艿:手头暂时没密钥,使用建议再测试下 |
| 621 | + AzureOpenAiEmbeddingAutoConfiguration azureOpenAiAutoConfiguration = new AzureOpenAiEmbeddingAutoConfiguration(); | ||
| 622 | + // 创建 OpenAIClientBuilder 对象 | ||
| 623 | + OpenAIClientBuilder openAIClientBuilder = new OpenAIClientBuilder() | ||
| 624 | + .endpoint(url).credential(new KeyCredential(apiKey)); | ||
| 590 | // 获取 AzureOpenAiChatProperties 对象 | 625 | // 获取 AzureOpenAiChatProperties 对象 |
| 591 | AzureOpenAiEmbeddingProperties embeddingProperties = SpringUtil.getBean(AzureOpenAiEmbeddingProperties.class); | 626 | AzureOpenAiEmbeddingProperties embeddingProperties = SpringUtil.getBean(AzureOpenAiEmbeddingProperties.class); |
| 592 | - return azureOpenAiAutoConfiguration.azureOpenAiEmbeddingModel(openAIClient, embeddingProperties, | ||
| 593 | - null, null); | 627 | + return azureOpenAiAutoConfiguration.azureOpenAiEmbeddingModel(openAIClientBuilder, embeddingProperties, |
| 628 | + getObservationRegistry(), getEmbeddingModelObservationConvention()); | ||
| 594 | } | 629 | } |
| 595 | 630 | ||
| 596 | // ========== 各种创建 VectorStore 的方法 ========== | 631 | // ========== 各种创建 VectorStore 的方法 ========== |
| @@ -655,12 +690,12 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -655,12 +690,12 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 655 | Map<String, Class<?>> metadataFields) { | 690 | Map<String, Class<?>> metadataFields) { |
| 656 | // 创建 JedisPooled 对象 | 691 | // 创建 JedisPooled 对象 |
| 657 | RedisProperties redisProperties = SpringUtils.getBean(RedisProperties.class); | 692 | RedisProperties redisProperties = SpringUtils.getBean(RedisProperties.class); |
| 658 | - JedisPooled jedisPooled = new JedisPooled(redisProperties.getHost(), redisProperties.getPort()); | 693 | + JedisPooled jedisPooled = new JedisPooled(redisProperties.getHost(), redisProperties.getPort(), |
| 694 | + redisProperties.getUsername(), redisProperties.getPassword()); | ||
| 659 | // 创建 RedisVectorStoreProperties 对象 | 695 | // 创建 RedisVectorStoreProperties 对象 |
| 660 | - RedisVectorStoreAutoConfiguration configuration = new RedisVectorStoreAutoConfiguration(); | ||
| 661 | RedisVectorStoreProperties properties = SpringUtil.getBean(RedisVectorStoreProperties.class); | 696 | RedisVectorStoreProperties properties = SpringUtil.getBean(RedisVectorStoreProperties.class); |
| 662 | RedisVectorStore redisVectorStore = RedisVectorStore.builder(jedisPooled, embeddingModel) | 697 | RedisVectorStore redisVectorStore = RedisVectorStore.builder(jedisPooled, embeddingModel) |
| 663 | - .indexName(properties.getIndex()).prefix(properties.getPrefix()) | 698 | + .indexName(properties.getIndexName()).prefix(properties.getPrefix()) |
| 664 | .initializeSchema(properties.isInitializeSchema()) | 699 | .initializeSchema(properties.isInitializeSchema()) |
| 665 | .metadataFields(convertList(metadataFields.entrySet(), entry -> { | 700 | .metadataFields(convertList(metadataFields.entrySet(), entry -> { |
| 666 | String fieldName = entry.getKey(); | 701 | String fieldName = entry.getKey(); |
| @@ -730,10 +765,12 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -730,10 +765,12 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 730 | 765 | ||
| 731 | private static ObjectProvider<VectorStoreObservationConvention> getCustomObservationConvention() { | 766 | private static ObjectProvider<VectorStoreObservationConvention> getCustomObservationConvention() { |
| 732 | return new ObjectProvider<>() { | 767 | return new ObjectProvider<>() { |
| 768 | + | ||
| 733 | @Override | 769 | @Override |
| 734 | public VectorStoreObservationConvention getObject() throws BeansException { | 770 | public VectorStoreObservationConvention getObject() throws BeansException { |
| 735 | return new DefaultVectorStoreObservationConvention(); | 771 | return new DefaultVectorStoreObservationConvention(); |
| 736 | } | 772 | } |
| 773 | + | ||
| 737 | }; | 774 | }; |
| 738 | } | 775 | } |
| 739 | 776 | ||
| @@ -745,8 +782,15 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -745,8 +782,15 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 745 | return SpringUtil.getBean(ToolCallingManager.class); | 782 | return SpringUtil.getBean(ToolCallingManager.class); |
| 746 | } | 783 | } |
| 747 | 784 | ||
| 748 | - private static FunctionCallbackResolver getFunctionCallbackResolver() { | ||
| 749 | - return SpringUtil.getBean(FunctionCallbackResolver.class); | 785 | + private static ObjectProvider<EmbeddingModelObservationConvention> getEmbeddingModelObservationConvention() { |
| 786 | + return new ObjectProvider<>() { | ||
| 787 | + | ||
| 788 | + @Override | ||
| 789 | + public EmbeddingModelObservationConvention getObject() throws BeansException { | ||
| 790 | + return SpringUtil.getBean(EmbeddingModelObservationConvention.class); | ||
| 791 | + } | ||
| 792 | + | ||
| 793 | + }; | ||
| 750 | } | 794 | } |
| 751 | 795 | ||
| 752 | } | 796 | } |
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/deepseek/DeepSeekChatModel.java deleted
| 1 | -package cn.iocoder.yudao.module.ai.framework.ai.core.model.deepseek; | ||
| 2 | - | ||
| 3 | -import lombok.RequiredArgsConstructor; | ||
| 4 | -import lombok.extern.slf4j.Slf4j; | ||
| 5 | -import org.springframework.ai.chat.model.ChatModel; | ||
| 6 | -import org.springframework.ai.chat.model.ChatResponse; | ||
| 7 | -import org.springframework.ai.chat.prompt.ChatOptions; | ||
| 8 | -import org.springframework.ai.chat.prompt.Prompt; | ||
| 9 | -import org.springframework.ai.openai.OpenAiChatModel; | ||
| 10 | -import reactor.core.publisher.Flux; | ||
| 11 | - | ||
| 12 | -/** | ||
| 13 | - * DeepSeek {@link ChatModel} 实现类 | ||
| 14 | - * | ||
| 15 | - * @author fansili | ||
| 16 | - */ | ||
| 17 | -@Slf4j | ||
| 18 | -@RequiredArgsConstructor | ||
| 19 | -public class DeepSeekChatModel implements ChatModel { | ||
| 20 | - | ||
| 21 | - public static final String BASE_URL = "https://api.deepseek.com"; | ||
| 22 | - | ||
| 23 | - public static final String MODEL_DEFAULT = "deepseek-chat"; | ||
| 24 | - | ||
| 25 | - /** | ||
| 26 | - * 兼容 OpenAI 接口,进行复用 | ||
| 27 | - */ | ||
| 28 | - private final OpenAiChatModel openAiChatModel; | ||
| 29 | - | ||
| 30 | - @Override | ||
| 31 | - public ChatResponse call(Prompt prompt) { | ||
| 32 | - return openAiChatModel.call(prompt); | ||
| 33 | - } | ||
| 34 | - | ||
| 35 | - @Override | ||
| 36 | - public Flux<ChatResponse> stream(Prompt prompt) { | ||
| 37 | - return openAiChatModel.stream(prompt); | ||
| 38 | - } | ||
| 39 | - | ||
| 40 | - @Override | ||
| 41 | - public ChatOptions getDefaultOptions() { | ||
| 42 | - return openAiChatModel.getDefaultOptions(); | ||
| 43 | - } | ||
| 44 | - | ||
| 45 | -} |
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java
| @@ -89,7 +89,7 @@ public class SiliconFlowImageModel implements ImageModel { | @@ -89,7 +89,7 @@ public class SiliconFlowImageModel implements ImageModel { | ||
| 89 | var observationContext = ImageModelObservationContext.builder() | 89 | var observationContext = ImageModelObservationContext.builder() |
| 90 | .imagePrompt(imagePrompt) | 90 | .imagePrompt(imagePrompt) |
| 91 | .provider(SiliconFlowApiConstants.PROVIDER_NAME) | 91 | .provider(SiliconFlowApiConstants.PROVIDER_NAME) |
| 92 | - .requestOptions(imagePrompt.getOptions()) | 92 | + .imagePrompt(imagePrompt) |
| 93 | .build(); | 93 | .build(); |
| 94 | 94 | ||
| 95 | return ImageModelObservationDocumentation.IMAGE_MODEL_OPERATION | 95 | return ImageModelObservationDocumentation.IMAGE_MODEL_OPERATION |
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java
| @@ -9,9 +9,6 @@ import cn.hutool.core.util.ObjUtil; | @@ -9,9 +9,6 @@ import cn.hutool.core.util.ObjUtil; | ||
| 9 | import cn.hutool.core.util.StrUtil; | 9 | import cn.hutool.core.util.StrUtil; |
| 10 | import cn.hutool.extra.spring.SpringUtil; | 10 | import cn.hutool.extra.spring.SpringUtil; |
| 11 | import cn.hutool.http.HttpUtil; | 11 | import cn.hutool.http.HttpUtil; |
| 12 | -import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum; | ||
| 13 | -import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi; | ||
| 14 | -import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowImageOptions; | ||
| 15 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 12 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 16 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 13 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 17 | import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDrawReqVO; | 14 | import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDrawReqVO; |
| @@ -24,17 +21,20 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; | @@ -24,17 +21,20 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; | ||
| 24 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; | 21 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; |
| 25 | import cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper; | 22 | import cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper; |
| 26 | import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum; | 23 | import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum; |
| 24 | +import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum; | ||
| 25 | +import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi; | ||
| 26 | +import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowImageOptions; | ||
| 27 | import cn.iocoder.yudao.module.ai.service.model.AiModelService; | 27 | import cn.iocoder.yudao.module.ai.service.model.AiModelService; |
| 28 | import cn.iocoder.yudao.module.infra.api.file.FileApi; | 28 | import cn.iocoder.yudao.module.infra.api.file.FileApi; |
| 29 | import com.alibaba.cloud.ai.dashscope.image.DashScopeImageOptions; | 29 | import com.alibaba.cloud.ai.dashscope.image.DashScopeImageOptions; |
| 30 | import jakarta.annotation.Resource; | 30 | import jakarta.annotation.Resource; |
| 31 | import lombok.extern.slf4j.Slf4j; | 31 | import lombok.extern.slf4j.Slf4j; |
| 32 | +import org.springaicommunity.qianfan.QianFanImageOptions; | ||
| 32 | import org.springframework.ai.image.ImageModel; | 33 | import org.springframework.ai.image.ImageModel; |
| 33 | import org.springframework.ai.image.ImageOptions; | 34 | import org.springframework.ai.image.ImageOptions; |
| 34 | import org.springframework.ai.image.ImagePrompt; | 35 | import org.springframework.ai.image.ImagePrompt; |
| 35 | import org.springframework.ai.image.ImageResponse; | 36 | import org.springframework.ai.image.ImageResponse; |
| 36 | import org.springframework.ai.openai.OpenAiImageOptions; | 37 | import org.springframework.ai.openai.OpenAiImageOptions; |
| 37 | -import org.springframework.ai.qianfan.QianFanImageOptions; | ||
| 38 | import org.springframework.ai.stabilityai.api.StabilityAiImageOptions; | 38 | import org.springframework.ai.stabilityai.api.StabilityAiImageOptions; |
| 39 | import org.springframework.ai.zhipuai.ZhiPuAiImageOptions; | 39 | import org.springframework.ai.zhipuai.ZhiPuAiImageOptions; |
| 40 | import org.springframework.scheduling.annotation.Async; | 40 | import org.springframework.scheduling.annotation.Async; |
| @@ -140,10 +140,10 @@ public class AiImageServiceImpl implements AiImageService { | @@ -140,10 +140,10 @@ public class AiImageServiceImpl implements AiImageService { | ||
| 140 | private static ImageOptions buildImageOptions(AiImageDrawReqVO draw, AiModelDO model) { | 140 | private static ImageOptions buildImageOptions(AiImageDrawReqVO draw, AiModelDO model) { |
| 141 | if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.OPENAI.getPlatform())) { | 141 | if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.OPENAI.getPlatform())) { |
| 142 | // https://platform.openai.com/docs/api-reference/images/create | 142 | // https://platform.openai.com/docs/api-reference/images/create |
| 143 | - return OpenAiImageOptions.builder().withModel(model.getModel()) | ||
| 144 | - .withHeight(draw.getHeight()).withWidth(draw.getWidth()) | ||
| 145 | - .withStyle(MapUtil.getStr(draw.getOptions(), "style")) // 风格 | ||
| 146 | - .withResponseFormat("b64_json") | 143 | + return OpenAiImageOptions.builder().model(model.getModel()) |
| 144 | + .height(draw.getHeight()).width(draw.getWidth()) | ||
| 145 | + .style(MapUtil.getStr(draw.getOptions(), "style")) // 风格 | ||
| 146 | + .responseFormat("b64_json") | ||
| 147 | .build(); | 147 | .build(); |
| 148 | } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.SILICON_FLOW.getPlatform())) { | 148 | } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.SILICON_FLOW.getPlatform())) { |
| 149 | // https://docs.siliconflow.cn/cn/api-reference/images/images-generations | 149 | // https://docs.siliconflow.cn/cn/api-reference/images/images-generations |
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/tool/UserProfileQueryToolFunction.java
| @@ -7,6 +7,8 @@ import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; | @@ -7,6 +7,8 @@ import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; | ||
| 7 | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | 7 | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| 8 | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; | 8 | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; |
| 9 | import com.fasterxml.jackson.annotation.JsonClassDescription; | 9 | import com.fasterxml.jackson.annotation.JsonClassDescription; |
| 10 | +import com.fasterxml.jackson.annotation.JsonProperty; | ||
| 11 | +import com.fasterxml.jackson.annotation.JsonPropertyDescription; | ||
| 10 | import jakarta.annotation.Resource; | 12 | import jakarta.annotation.Resource; |
| 11 | import lombok.AllArgsConstructor; | 13 | import lombok.AllArgsConstructor; |
| 12 | import lombok.Data; | 14 | import lombok.Data; |
| @@ -17,7 +19,7 @@ import org.springframework.stereotype.Component; | @@ -17,7 +19,7 @@ import org.springframework.stereotype.Component; | ||
| 17 | import java.util.function.BiFunction; | 19 | import java.util.function.BiFunction; |
| 18 | 20 | ||
| 19 | /** | 21 | /** |
| 20 | - * 工具:当前用户信息查询 | 22 | + * 工具:用户信息查询 |
| 21 | * | 23 | * |
| 22 | * 同时,也是展示 ToolContext 上下文的使用 | 24 | * 同时,也是展示 ToolContext 上下文的使用 |
| 23 | * | 25 | * |
| @@ -31,8 +33,17 @@ public class UserProfileQueryToolFunction | @@ -31,8 +33,17 @@ public class UserProfileQueryToolFunction | ||
| 31 | private AdminUserApi adminUserApi; | 33 | private AdminUserApi adminUserApi; |
| 32 | 34 | ||
| 33 | @Data | 35 | @Data |
| 34 | - @JsonClassDescription("当前用户信息查询") | ||
| 35 | - public static class Request { } | 36 | + @JsonClassDescription("用户信息查询") |
| 37 | + public static class Request { | ||
| 38 | + | ||
| 39 | + /** | ||
| 40 | + * 用户编号 | ||
| 41 | + */ | ||
| 42 | + @JsonProperty(value = "id") | ||
| 43 | + @JsonPropertyDescription("用户编号,例如说:1。如果查询自己,则 id 为空") | ||
| 44 | + private Long id; | ||
| 45 | + | ||
| 46 | + } | ||
| 36 | 47 | ||
| 37 | @Data | 48 | @Data |
| 38 | @AllArgsConstructor | 49 | @AllArgsConstructor |
| @@ -61,13 +72,19 @@ public class UserProfileQueryToolFunction | @@ -61,13 +72,19 @@ public class UserProfileQueryToolFunction | ||
| 61 | 72 | ||
| 62 | @Override | 73 | @Override |
| 63 | public Response apply(Request request, ToolContext toolContext) { | 74 | public Response apply(Request request, ToolContext toolContext) { |
| 64 | - LoginUser loginUser = (LoginUser) toolContext.getContext().get(AiUtils.TOOL_CONTEXT_LOGIN_USER); | ||
| 65 | Long tenantId = (Long) toolContext.getContext().get(AiUtils.TOOL_CONTEXT_TENANT_ID); | 75 | Long tenantId = (Long) toolContext.getContext().get(AiUtils.TOOL_CONTEXT_TENANT_ID); |
| 66 | - if (loginUser == null | tenantId == null) { | ||
| 67 | - return null; | 76 | + if (tenantId == null) { |
| 77 | + return new Response(); | ||
| 78 | + } | ||
| 79 | + if (request.getId() == null) { | ||
| 80 | + LoginUser loginUser = (LoginUser) toolContext.getContext().get(AiUtils.TOOL_CONTEXT_LOGIN_USER); | ||
| 81 | + if (loginUser == null) { | ||
| 82 | + return new Response(); | ||
| 83 | + } | ||
| 84 | + request.setId(loginUser.getId()); | ||
| 68 | } | 85 | } |
| 69 | return TenantUtils.execute(tenantId, () -> { | 86 | return TenantUtils.execute(tenantId, () -> { |
| 70 | - AdminUserRespDTO user = adminUserApi.getUser(loginUser.getId()); | 87 | + AdminUserRespDTO user = adminUserApi.getUser(request.getId()); |
| 71 | return BeanUtils.toBean(user, Response.class); | 88 | return BeanUtils.toBean(user, Response.class); |
| 72 | }); | 89 | }); |
| 73 | } | 90 | } |
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java
| @@ -2,18 +2,18 @@ package cn.iocoder.yudao.module.ai.util; | @@ -2,18 +2,18 @@ package cn.iocoder.yudao.module.ai.util; | ||
| 2 | 2 | ||
| 3 | import cn.hutool.core.util.ObjUtil; | 3 | import cn.hutool.core.util.ObjUtil; |
| 4 | import cn.hutool.core.util.StrUtil; | 4 | import cn.hutool.core.util.StrUtil; |
| 5 | -import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum; | ||
| 6 | import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; | 5 | import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; |
| 7 | import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; | 6 | import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; |
| 7 | +import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum; | ||
| 8 | import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions; | 8 | import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions; |
| 9 | +import org.springaicommunity.moonshot.MoonshotChatOptions; | ||
| 10 | +import org.springaicommunity.qianfan.QianFanChatOptions; | ||
| 9 | import org.springframework.ai.azure.openai.AzureOpenAiChatOptions; | 11 | import org.springframework.ai.azure.openai.AzureOpenAiChatOptions; |
| 10 | import org.springframework.ai.chat.messages.*; | 12 | import org.springframework.ai.chat.messages.*; |
| 11 | import org.springframework.ai.chat.prompt.ChatOptions; | 13 | import org.springframework.ai.chat.prompt.ChatOptions; |
| 12 | import org.springframework.ai.minimax.MiniMaxChatOptions; | 14 | import org.springframework.ai.minimax.MiniMaxChatOptions; |
| 13 | -import org.springframework.ai.moonshot.MoonshotChatOptions; | ||
| 14 | import org.springframework.ai.ollama.api.OllamaOptions; | 15 | import org.springframework.ai.ollama.api.OllamaOptions; |
| 15 | import org.springframework.ai.openai.OpenAiChatOptions; | 16 | import org.springframework.ai.openai.OpenAiChatOptions; |
| 16 | -import org.springframework.ai.qianfan.QianFanChatOptions; | ||
| 17 | import org.springframework.ai.zhipuai.ZhiPuAiChatOptions; | 17 | import org.springframework.ai.zhipuai.ZhiPuAiChatOptions; |
| 18 | 18 | ||
| 19 | import java.util.Collections; | 19 | import java.util.Collections; |
| @@ -43,18 +43,18 @@ public class AiUtils { | @@ -43,18 +43,18 @@ public class AiUtils { | ||
| 43 | switch (platform) { | 43 | switch (platform) { |
| 44 | case TONG_YI: | 44 | case TONG_YI: |
| 45 | return DashScopeChatOptions.builder().withModel(model).withTemperature(temperature).withMaxToken(maxTokens) | 45 | return DashScopeChatOptions.builder().withModel(model).withTemperature(temperature).withMaxToken(maxTokens) |
| 46 | - .withFunctions(toolNames).withToolContext(toolContext).build(); | 46 | + .withToolNames(toolNames).withToolContext(toolContext).build(); |
| 47 | case YI_YAN: | 47 | case YI_YAN: |
| 48 | return QianFanChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); | 48 | return QianFanChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); |
| 49 | case ZHI_PU: | 49 | case ZHI_PU: |
| 50 | return ZhiPuAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) | 50 | return ZhiPuAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) |
| 51 | - .functions(toolNames).toolContext(toolContext).build(); | 51 | + .toolNames(toolNames).toolContext(toolContext).build(); |
| 52 | case MINI_MAX: | 52 | case MINI_MAX: |
| 53 | return MiniMaxChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) | 53 | return MiniMaxChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) |
| 54 | - .functions(toolNames).toolContext(toolContext).build(); | 54 | + .toolNames(toolNames).toolContext(toolContext).build(); |
| 55 | case MOONSHOT: | 55 | case MOONSHOT: |
| 56 | return MoonshotChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) | 56 | return MoonshotChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) |
| 57 | - .functions(toolNames).toolContext(toolContext).build(); | 57 | + .toolNames(toolNames).toolContext(toolContext).build(); |
| 58 | case OPENAI: | 58 | case OPENAI: |
| 59 | case DEEP_SEEK: // 复用 OpenAI 客户端 | 59 | case DEEP_SEEK: // 复用 OpenAI 客户端 |
| 60 | case DOU_BAO: // 复用 OpenAI 客户端 | 60 | case DOU_BAO: // 复用 OpenAI 客户端 |
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/AzureOpenAIChatModelTests.java
| @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; | @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; | ||
| 2 | 2 | ||
| 3 | import com.azure.ai.openai.OpenAIClientBuilder; | 3 | import com.azure.ai.openai.OpenAIClientBuilder; |
| 4 | import com.azure.core.credential.AzureKeyCredential; | 4 | import com.azure.core.credential.AzureKeyCredential; |
| 5 | -import com.azure.core.util.ClientOptions; | ||
| 6 | import org.junit.jupiter.api.Disabled; | 5 | import org.junit.jupiter.api.Disabled; |
| 7 | import org.junit.jupiter.api.Test; | 6 | import org.junit.jupiter.api.Test; |
| 8 | import org.springframework.ai.azure.openai.AzureOpenAiChatModel; | 7 | import org.springframework.ai.azure.openai.AzureOpenAiChatModel; |
| @@ -17,7 +16,7 @@ import reactor.core.publisher.Flux; | @@ -17,7 +16,7 @@ import reactor.core.publisher.Flux; | ||
| 17 | import java.util.ArrayList; | 16 | import java.util.ArrayList; |
| 18 | import java.util.List; | 17 | import java.util.List; |
| 19 | 18 | ||
| 20 | -import static org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiChatProperties.DEFAULT_DEPLOYMENT_NAME; | 19 | +import static org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiChatProperties.DEFAULT_DEPLOYMENT_NAME; |
| 21 | 20 | ||
| 22 | /** | 21 | /** |
| 23 | * {@link AzureOpenAiChatModel} 集成测试 | 22 | * {@link AzureOpenAiChatModel} 集成测试 |
| @@ -29,10 +28,13 @@ public class AzureOpenAIChatModelTests { | @@ -29,10 +28,13 @@ public class AzureOpenAIChatModelTests { | ||
| 29 | // TODO @芋艿:晚点在调整 | 28 | // TODO @芋艿:晚点在调整 |
| 30 | private final OpenAIClientBuilder openAiApi = new OpenAIClientBuilder() | 29 | private final OpenAIClientBuilder openAiApi = new OpenAIClientBuilder() |
| 31 | .endpoint("https://eastusprejade.openai.azure.com") | 30 | .endpoint("https://eastusprejade.openai.azure.com") |
| 32 | - .credential(new AzureKeyCredential("xxx")) | ||
| 33 | - .clientOptions((new ClientOptions()).setApplicationId("spring-ai")); | ||
| 34 | - private final AzureOpenAiChatModel chatModel = new AzureOpenAiChatModel(openAiApi, | ||
| 35 | - AzureOpenAiChatOptions.builder().deploymentName(DEFAULT_DEPLOYMENT_NAME).build()); | 31 | + .credential(new AzureKeyCredential("xxx")); |
| 32 | + private final AzureOpenAiChatModel chatModel = AzureOpenAiChatModel.builder() | ||
| 33 | + .openAIClientBuilder(openAiApi) | ||
| 34 | + .defaultOptions(AzureOpenAiChatOptions.builder() | ||
| 35 | + .deploymentName(DEFAULT_DEPLOYMENT_NAME) | ||
| 36 | + .build()) | ||
| 37 | + .build(); | ||
| 36 | 38 | ||
| 37 | @Test | 39 | @Test |
| 38 | @Disabled | 40 | @Disabled |
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/BaiChuanChatModelTests.java
| 1 | package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; | 1 | package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel; | 3 | import cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel; |
| 4 | -import cn.iocoder.yudao.module.ai.framework.ai.core.model.deepseek.DeepSeekChatModel; | ||
| 5 | import org.junit.jupiter.api.Disabled; | 4 | import org.junit.jupiter.api.Disabled; |
| 6 | import org.junit.jupiter.api.Test; | 5 | import org.junit.jupiter.api.Test; |
| 7 | import org.springframework.ai.chat.messages.Message; | 6 | import org.springframework.ai.chat.messages.Message; |
| @@ -35,7 +34,7 @@ public class BaiChuanChatModelTests { | @@ -35,7 +34,7 @@ public class BaiChuanChatModelTests { | ||
| 35 | .build()) | 34 | .build()) |
| 36 | .build(); | 35 | .build(); |
| 37 | 36 | ||
| 38 | - private final DeepSeekChatModel chatModel = new DeepSeekChatModel(openAiChatModel); | 37 | + private final BaiChuanChatModel chatModel = new BaiChuanChatModel(openAiChatModel); |
| 39 | 38 | ||
| 40 | @Test | 39 | @Test |
| 41 | @Disabled | 40 | @Disabled |
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DeepSeekChatModelTests.java
| 1 | package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; | 1 | package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.module.ai.framework.ai.core.model.deepseek.DeepSeekChatModel; | ||
| 4 | import org.junit.jupiter.api.Disabled; | 3 | import org.junit.jupiter.api.Disabled; |
| 5 | import org.junit.jupiter.api.Test; | 4 | import org.junit.jupiter.api.Test; |
| 6 | import org.springframework.ai.chat.messages.Message; | 5 | import org.springframework.ai.chat.messages.Message; |
| @@ -8,9 +7,9 @@ import org.springframework.ai.chat.messages.SystemMessage; | @@ -8,9 +7,9 @@ import org.springframework.ai.chat.messages.SystemMessage; | ||
| 8 | import org.springframework.ai.chat.messages.UserMessage; | 7 | import org.springframework.ai.chat.messages.UserMessage; |
| 9 | import org.springframework.ai.chat.model.ChatResponse; | 8 | import org.springframework.ai.chat.model.ChatResponse; |
| 10 | import org.springframework.ai.chat.prompt.Prompt; | 9 | import org.springframework.ai.chat.prompt.Prompt; |
| 11 | -import org.springframework.ai.openai.OpenAiChatModel; | ||
| 12 | -import org.springframework.ai.openai.OpenAiChatOptions; | ||
| 13 | -import org.springframework.ai.openai.api.OpenAiApi; | 10 | +import org.springframework.ai.deepseek.DeepSeekChatModel; |
| 11 | +import org.springframework.ai.deepseek.DeepSeekChatOptions; | ||
| 12 | +import org.springframework.ai.deepseek.api.DeepSeekApi; | ||
| 14 | import reactor.core.publisher.Flux; | 13 | import reactor.core.publisher.Flux; |
| 15 | 14 | ||
| 16 | import java.util.ArrayList; | 15 | import java.util.ArrayList; |
| @@ -23,19 +22,16 @@ import java.util.List; | @@ -23,19 +22,16 @@ import java.util.List; | ||
| 23 | */ | 22 | */ |
| 24 | public class DeepSeekChatModelTests { | 23 | public class DeepSeekChatModelTests { |
| 25 | 24 | ||
| 26 | - private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder() | ||
| 27 | - .openAiApi(OpenAiApi.builder() | ||
| 28 | - .baseUrl(DeepSeekChatModel.BASE_URL) | ||
| 29 | - .apiKey("sk-e52047409b144d97b791a6a46a2d") // apiKey | 25 | + private final DeepSeekChatModel chatModel = DeepSeekChatModel.builder() |
| 26 | + .deepSeekApi(DeepSeekApi.builder() | ||
| 27 | + .apiKey("sk-eaf4172a057344dd9bc64b1f806b6axx") // apiKey | ||
| 30 | .build()) | 28 | .build()) |
| 31 | - .defaultOptions(OpenAiChatOptions.builder() | 29 | + .defaultOptions(DeepSeekChatOptions.builder() |
| 32 | .model("deepseek-chat") // 模型 | 30 | .model("deepseek-chat") // 模型 |
| 33 | .temperature(0.7) | 31 | .temperature(0.7) |
| 34 | .build()) | 32 | .build()) |
| 35 | .build(); | 33 | .build(); |
| 36 | 34 | ||
| 37 | - private final DeepSeekChatModel chatModel = new DeepSeekChatModel(openAiChatModel); | ||
| 38 | - | ||
| 39 | @Test | 35 | @Test |
| 40 | @Disabled | 36 | @Disabled |
| 41 | public void testCall() { | 37 | public void testCall() { |
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/LlamaChatModelTests.java
| 1 | package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; | 1 | package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; |
| 2 | 2 | ||
| 3 | -import org.junit.jupiter.api.Disabled; | ||
| 4 | -import org.junit.jupiter.api.Test; | ||
| 5 | -import org.springframework.ai.chat.messages.Message; | ||
| 6 | -import org.springframework.ai.chat.messages.SystemMessage; | ||
| 7 | -import org.springframework.ai.chat.messages.UserMessage; | ||
| 8 | -import org.springframework.ai.chat.model.ChatResponse; | ||
| 9 | -import org.springframework.ai.chat.prompt.Prompt; | ||
| 10 | import org.springframework.ai.ollama.OllamaChatModel; | 3 | import org.springframework.ai.ollama.OllamaChatModel; |
| 11 | -import org.springframework.ai.ollama.api.OllamaApi; | ||
| 12 | -import org.springframework.ai.ollama.api.OllamaModel; | ||
| 13 | -import org.springframework.ai.ollama.api.OllamaOptions; | ||
| 14 | -import reactor.core.publisher.Flux; | ||
| 15 | - | ||
| 16 | -import java.util.ArrayList; | ||
| 17 | -import java.util.List; | ||
| 18 | 4 | ||
| 19 | /** | 5 | /** |
| 20 | * {@link OllamaChatModel} 集成测试 | 6 | * {@link OllamaChatModel} 集成测试 |
| @@ -23,43 +9,43 @@ import java.util.List; | @@ -23,43 +9,43 @@ import java.util.List; | ||
| 23 | */ | 9 | */ |
| 24 | public class LlamaChatModelTests { | 10 | public class LlamaChatModelTests { |
| 25 | 11 | ||
| 26 | - private final OllamaChatModel chatModel = OllamaChatModel.builder() | ||
| 27 | - .ollamaApi(new OllamaApi("http://127.0.0.1:11434")) // Ollama 服务地址 | ||
| 28 | - .defaultOptions(OllamaOptions.builder() | ||
| 29 | - .model(OllamaModel.LLAMA3.getName()) // 模型 | ||
| 30 | - .build()) | ||
| 31 | - .build(); | ||
| 32 | - | ||
| 33 | - @Test | ||
| 34 | - @Disabled | ||
| 35 | - public void testCall() { | ||
| 36 | - // 准备参数 | ||
| 37 | - List<Message> messages = new ArrayList<>(); | ||
| 38 | - messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); | ||
| 39 | - messages.add(new UserMessage("1 + 1 = ?")); | ||
| 40 | - | ||
| 41 | - // 调用 | ||
| 42 | - ChatResponse response = chatModel.call(new Prompt(messages)); | ||
| 43 | - // 打印结果 | ||
| 44 | - System.out.println(response); | ||
| 45 | - System.out.println(response.getResult().getOutput()); | ||
| 46 | - } | ||
| 47 | - | ||
| 48 | - @Test | ||
| 49 | - @Disabled | ||
| 50 | - public void testStream() { | ||
| 51 | - // 准备参数 | ||
| 52 | - List<Message> messages = new ArrayList<>(); | ||
| 53 | - messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); | ||
| 54 | - messages.add(new UserMessage("1 + 1 = ?")); | ||
| 55 | - | ||
| 56 | - // 调用 | ||
| 57 | - Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages)); | ||
| 58 | - // 打印结果 | ||
| 59 | - flux.doOnNext(response -> { | ||
| 60 | -// System.out.println(response); | ||
| 61 | - System.out.println(response.getResult().getOutput()); | ||
| 62 | - }).then().block(); | ||
| 63 | - } | 12 | +// private final OllamaChatModel chatModel = OllamaChatModel.builder() |
| 13 | +// .ollamaApi(new OllamaApi("http://127.0.0.1:11434")) // Ollama 服务地址 | ||
| 14 | +// .defaultOptions(OllamaOptions.builder() | ||
| 15 | +// .model(OllamaModel.LLAMA3.getName()) // 模型 | ||
| 16 | +// .build()) | ||
| 17 | +// .build(); | ||
| 18 | +// | ||
| 19 | +// @Test | ||
| 20 | +// @Disabled | ||
| 21 | +// public void testCall() { | ||
| 22 | +// // 准备参数 | ||
| 23 | +// List<Message> messages = new ArrayList<>(); | ||
| 24 | +// messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); | ||
| 25 | +// messages.add(new UserMessage("1 + 1 = ?")); | ||
| 26 | +// | ||
| 27 | +// // 调用 | ||
| 28 | +// ChatResponse response = chatModel.call(new Prompt(messages)); | ||
| 29 | +// // 打印结果 | ||
| 30 | +// System.out.println(response); | ||
| 31 | +// System.out.println(response.getResult().getOutput()); | ||
| 32 | +// } | ||
| 33 | +// | ||
| 34 | +// @Test | ||
| 35 | +// @Disabled | ||
| 36 | +// public void testStream() { | ||
| 37 | +// // 准备参数 | ||
| 38 | +// List<Message> messages = new ArrayList<>(); | ||
| 39 | +// messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。")); | ||
| 40 | +// messages.add(new UserMessage("1 + 1 = ?")); | ||
| 41 | +// | ||
| 42 | +// // 调用 | ||
| 43 | +// Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages)); | ||
| 44 | +// // 打印结果 | ||
| 45 | +// flux.doOnNext(response -> { | ||
| 46 | +//// System.out.println(response); | ||
| 47 | +// System.out.println(response.getResult().getOutput()); | ||
| 48 | +// }).then().block(); | ||
| 49 | +// } | ||
| 64 | 50 | ||
| 65 | } | 51 | } |
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/MoonshotChatModelTests.java
| @@ -2,14 +2,14 @@ package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; | @@ -2,14 +2,14 @@ package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; | ||
| 2 | 2 | ||
| 3 | import org.junit.jupiter.api.Disabled; | 3 | import org.junit.jupiter.api.Disabled; |
| 4 | import org.junit.jupiter.api.Test; | 4 | import org.junit.jupiter.api.Test; |
| 5 | +import org.springaicommunity.moonshot.MoonshotChatModel; | ||
| 6 | +import org.springaicommunity.moonshot.MoonshotChatOptions; | ||
| 7 | +import org.springaicommunity.moonshot.api.MoonshotApi; | ||
| 5 | import org.springframework.ai.chat.messages.Message; | 8 | import org.springframework.ai.chat.messages.Message; |
| 6 | import org.springframework.ai.chat.messages.SystemMessage; | 9 | import org.springframework.ai.chat.messages.SystemMessage; |
| 7 | import org.springframework.ai.chat.messages.UserMessage; | 10 | import org.springframework.ai.chat.messages.UserMessage; |
| 8 | import org.springframework.ai.chat.model.ChatResponse; | 11 | import org.springframework.ai.chat.model.ChatResponse; |
| 9 | import org.springframework.ai.chat.prompt.Prompt; | 12 | import org.springframework.ai.chat.prompt.Prompt; |
| 10 | -import org.springframework.ai.moonshot.MoonshotChatModel; | ||
| 11 | -import org.springframework.ai.moonshot.MoonshotChatOptions; | ||
| 12 | -import org.springframework.ai.moonshot.api.MoonshotApi; | ||
| 13 | import reactor.core.publisher.Flux; | 13 | import reactor.core.publisher.Flux; |
| 14 | 14 | ||
| 15 | import java.util.ArrayList; | 15 | import java.util.ArrayList; |
| @@ -22,11 +22,15 @@ import java.util.List; | @@ -22,11 +22,15 @@ import java.util.List; | ||
| 22 | */ | 22 | */ |
| 23 | public class MoonshotChatModelTests { | 23 | public class MoonshotChatModelTests { |
| 24 | 24 | ||
| 25 | - private final MoonshotChatModel chatModel = new MoonshotChatModel( | ||
| 26 | - new MoonshotApi("sk-aHYYV1SARscItye5QQRRNbXij4fy65Ee7pNZlC9gsSQnUKXA"), // 密钥 | ||
| 27 | - MoonshotChatOptions.builder() | ||
| 28 | - .model("moonshot-v1-8k") // 模型 | ||
| 29 | - .build()); | 25 | + private final MoonshotChatModel chatModel = MoonshotChatModel.builder() |
| 26 | + .moonshotApi(MoonshotApi.builder() | ||
| 27 | + .apiKey("sk-aHYYV1SARscItye5QQRRNbXij4fy65Ee7pNZlC9gsSQnUKXA") // 密钥 | ||
| 28 | + .build()) | ||
| 29 | + .defaultOptions(MoonshotChatOptions.builder() | ||
| 30 | + .model("kimi-k2-0711-preview") // 模型 | ||
| 31 | + .build()) | ||
| 32 | + .build(); | ||
| 33 | + | ||
| 30 | @Test | 34 | @Test |
| 31 | @Disabled | 35 | @Disabled |
| 32 | public void testCall() { | 36 | public void testCall() { |
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OllamaChatModelTests.java
| @@ -23,7 +23,9 @@ import java.util.List; | @@ -23,7 +23,9 @@ import java.util.List; | ||
| 23 | public class OllamaChatModelTests { | 23 | public class OllamaChatModelTests { |
| 24 | 24 | ||
| 25 | private final OllamaChatModel chatModel = OllamaChatModel.builder() | 25 | private final OllamaChatModel chatModel = OllamaChatModel.builder() |
| 26 | - .ollamaApi(new OllamaApi("http://127.0.0.1:11434")) // Ollama 服务地址 | 26 | + .ollamaApi(OllamaApi.builder() |
| 27 | + .baseUrl("http://127.0.0.1:11434") // Ollama 服务地址 | ||
| 28 | + .build()) | ||
| 27 | .defaultOptions(OllamaOptions.builder() | 29 | .defaultOptions(OllamaOptions.builder() |
| 28 | // .model("qwen") // 模型(https://ollama.com/library/qwen) | 30 | // .model("qwen") // 模型(https://ollama.com/library/qwen) |
| 29 | .model("deepseek-r1") // 模型(https://ollama.com/library/deepseek-r1) | 31 | .model("deepseek-r1") // 模型(https://ollama.com/library/deepseek-r1) |
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OpenAIChatModelTests.java
| @@ -25,10 +25,10 @@ public class OpenAIChatModelTests { | @@ -25,10 +25,10 @@ public class OpenAIChatModelTests { | ||
| 25 | private final OpenAiChatModel chatModel = OpenAiChatModel.builder() | 25 | private final OpenAiChatModel chatModel = OpenAiChatModel.builder() |
| 26 | .openAiApi(OpenAiApi.builder() | 26 | .openAiApi(OpenAiApi.builder() |
| 27 | .baseUrl("https://api.holdai.top") | 27 | .baseUrl("https://api.holdai.top") |
| 28 | - .apiKey("sk-aN6nWn3fILjrgLFT0fC4Aa60B72e4253826c77B29dC94f17") // apiKey | 28 | + .apiKey("sk-PytRecQlmjEteoa2RRN6cGnwslo72UUPLQVNEMS6K9yjbmpD") // apiKey |
| 29 | .build()) | 29 | .build()) |
| 30 | .defaultOptions(OpenAiChatOptions.builder() | 30 | .defaultOptions(OpenAiChatOptions.builder() |
| 31 | - .model(OpenAiApi.ChatModel.GPT_4_O) // 模型 | 31 | + .model(OpenAiApi.ChatModel.GPT_4_1_NANO) // 模型 |
| 32 | .temperature(0.7) | 32 | .temperature(0.7) |
| 33 | .build()) | 33 | .build()) |
| 34 | .build(); | 34 | .build(); |
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/TongYiChatModelTests.java
| @@ -22,14 +22,17 @@ import java.util.List; | @@ -22,14 +22,17 @@ import java.util.List; | ||
| 22 | */ | 22 | */ |
| 23 | public class TongYiChatModelTests { | 23 | public class TongYiChatModelTests { |
| 24 | 24 | ||
| 25 | - private final DashScopeChatModel chatModel = new DashScopeChatModel( | ||
| 26 | - new DashScopeApi("sk-7d903764249848cfa912733146da12d1"), | ||
| 27 | - DashScopeChatOptions.builder() | 25 | + private final DashScopeChatModel chatModel = DashScopeChatModel.builder() |
| 26 | + .dashScopeApi(DashScopeApi.builder() | ||
| 27 | + .apiKey("sk-47aa124781be4bfb95244cc62f63f7d0") | ||
| 28 | + .build()) | ||
| 29 | + .defaultOptions( DashScopeChatOptions.builder() | ||
| 28 | .withModel("qwen1.5-72b-chat") // 模型 | 30 | .withModel("qwen1.5-72b-chat") // 模型 |
| 29 | // .withModel("deepseek-r1") // 模型(deepseek-r1) | 31 | // .withModel("deepseek-r1") // 模型(deepseek-r1) |
| 30 | // .withModel("deepseek-v3") // 模型(deepseek-v3) | 32 | // .withModel("deepseek-v3") // 模型(deepseek-v3) |
| 31 | // .withModel("deepseek-r1-distill-qwen-1.5b") // 模型(deepseek-r1-distill-qwen-1.5b) | 33 | // .withModel("deepseek-r1-distill-qwen-1.5b") // 模型(deepseek-r1-distill-qwen-1.5b) |
| 32 | - .build()); | 34 | + .build()) |
| 35 | + .build(); | ||
| 33 | 36 | ||
| 34 | @Test | 37 | @Test |
| 35 | @Disabled | 38 | @Disabled |
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/YiYanChatModelTests.java
| @@ -2,13 +2,13 @@ package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; | @@ -2,13 +2,13 @@ package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat; | ||
| 2 | 2 | ||
| 3 | import org.junit.jupiter.api.Disabled; | 3 | import org.junit.jupiter.api.Disabled; |
| 4 | import org.junit.jupiter.api.Test; | 4 | import org.junit.jupiter.api.Test; |
| 5 | +import org.springaicommunity.qianfan.QianFanChatModel; | ||
| 6 | +import org.springaicommunity.qianfan.QianFanChatOptions; | ||
| 7 | +import org.springaicommunity.qianfan.api.QianFanApi; | ||
| 5 | import org.springframework.ai.chat.messages.Message; | 8 | import org.springframework.ai.chat.messages.Message; |
| 6 | import org.springframework.ai.chat.messages.UserMessage; | 9 | import org.springframework.ai.chat.messages.UserMessage; |
| 7 | import org.springframework.ai.chat.model.ChatResponse; | 10 | import org.springframework.ai.chat.model.ChatResponse; |
| 8 | import org.springframework.ai.chat.prompt.Prompt; | 11 | import org.springframework.ai.chat.prompt.Prompt; |
| 9 | -import org.springframework.ai.qianfan.QianFanChatModel; | ||
| 10 | -import org.springframework.ai.qianfan.QianFanChatOptions; | ||
| 11 | -import org.springframework.ai.qianfan.api.QianFanApi; | ||
| 12 | import reactor.core.publisher.Flux; | 12 | import reactor.core.publisher.Flux; |
| 13 | 13 | ||
| 14 | import java.util.ArrayList; | 14 | import java.util.ArrayList; |
| @@ -23,9 +23,9 @@ import java.util.List; | @@ -23,9 +23,9 @@ import java.util.List; | ||
| 23 | public class YiYanChatModelTests { | 23 | public class YiYanChatModelTests { |
| 24 | 24 | ||
| 25 | private final QianFanChatModel chatModel = new QianFanChatModel( | 25 | private final QianFanChatModel chatModel = new QianFanChatModel( |
| 26 | - new QianFanApi("qS8k8dYr2nXunagK4SSU8Xjj", "pHGbx51ql2f0hOyabQvSZezahVC3hh3e"), // 密钥 | 26 | + new QianFanApi("DGnyzREuaY7av7c38bOM9Ji2", "9aR8myflEOPDrEeLhoXv0FdqANOAyIZW"), // 密钥 |
| 27 | QianFanChatOptions.builder() | 27 | QianFanChatOptions.builder() |
| 28 | - .model(QianFanApi.ChatModel.ERNIE_4_0_8K_Preview.getValue()) | 28 | + .model("ERNIE-4.5-8K-Preview") |
| 29 | .build() | 29 | .build() |
| 30 | ); | 30 | ); |
| 31 | 31 |
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/OpenAiImageModelTests.java
| @@ -18,7 +18,7 @@ public class OpenAiImageModelTests { | @@ -18,7 +18,7 @@ public class OpenAiImageModelTests { | ||
| 18 | 18 | ||
| 19 | private final OpenAiImageModel imageModel = new OpenAiImageModel(OpenAiImageApi.builder() | 19 | private final OpenAiImageModel imageModel = new OpenAiImageModel(OpenAiImageApi.builder() |
| 20 | .baseUrl("https://api.holdai.top") // apiKey | 20 | .baseUrl("https://api.holdai.top") // apiKey |
| 21 | - .apiKey("sk-aN6nWn3fILjrgLFT0fC4Aa60B72e4253826c77B29dC94f17") | 21 | + .apiKey("sk-PytRecQlmjEteoa2RRN6cGnwslo72UUPLQVNEMS6K9yjbmpD") |
| 22 | .build()); | 22 | .build()); |
| 23 | 23 | ||
| 24 | @Test | 24 | @Test |
| @@ -26,8 +26,8 @@ public class OpenAiImageModelTests { | @@ -26,8 +26,8 @@ public class OpenAiImageModelTests { | ||
| 26 | public void testCall() { | 26 | public void testCall() { |
| 27 | // 准备参数 | 27 | // 准备参数 |
| 28 | ImageOptions options = OpenAiImageOptions.builder() | 28 | ImageOptions options = OpenAiImageOptions.builder() |
| 29 | - .withModel(OpenAiImageApi.ImageModel.DALL_E_2.getValue()) // 这个模型比较便宜 | ||
| 30 | - .withHeight(256).withWidth(256) | 29 | + .model(OpenAiImageApi.ImageModel.DALL_E_2.getValue()) // 这个模型比较便宜 |
| 30 | + .height(256).width(256) | ||
| 31 | .build(); | 31 | .build(); |
| 32 | ImagePrompt prompt = new ImagePrompt("中国长城!", options); | 32 | ImagePrompt prompt = new ImagePrompt("中国长城!", options); |
| 33 | 33 |
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/QianFanImageTests.java
| @@ -2,11 +2,11 @@ package cn.iocoder.yudao.module.ai.framework.ai.core.model.image; | @@ -2,11 +2,11 @@ package cn.iocoder.yudao.module.ai.framework.ai.core.model.image; | ||
| 2 | 2 | ||
| 3 | import org.junit.jupiter.api.Disabled; | 3 | import org.junit.jupiter.api.Disabled; |
| 4 | import org.junit.jupiter.api.Test; | 4 | import org.junit.jupiter.api.Test; |
| 5 | +import org.springaicommunity.qianfan.QianFanImageModel; | ||
| 6 | +import org.springaicommunity.qianfan.QianFanImageOptions; | ||
| 7 | +import org.springaicommunity.qianfan.api.QianFanImageApi; | ||
| 5 | import org.springframework.ai.image.ImagePrompt; | 8 | import org.springframework.ai.image.ImagePrompt; |
| 6 | import org.springframework.ai.image.ImageResponse; | 9 | import org.springframework.ai.image.ImageResponse; |
| 7 | -import org.springframework.ai.qianfan.QianFanImageModel; | ||
| 8 | -import org.springframework.ai.qianfan.QianFanImageOptions; | ||
| 9 | -import org.springframework.ai.qianfan.api.QianFanImageApi; | ||
| 10 | 10 | ||
| 11 | import static cn.iocoder.yudao.module.ai.framework.ai.core.model.image.StabilityAiImageModelTests.viewImage; | 11 | import static cn.iocoder.yudao.module.ai.framework.ai.core.model.image.StabilityAiImageModelTests.viewImage; |
| 12 | 12 |
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/StabilityAiImageModelTests.java
| @@ -31,8 +31,8 @@ public class StabilityAiImageModelTests { | @@ -31,8 +31,8 @@ public class StabilityAiImageModelTests { | ||
| 31 | public void testCall() { | 31 | public void testCall() { |
| 32 | // 准备参数 | 32 | // 准备参数 |
| 33 | ImageOptions options = OpenAiImageOptions.builder() | 33 | ImageOptions options = OpenAiImageOptions.builder() |
| 34 | - .withModel("stable-diffusion-v1-6") | ||
| 35 | - .withHeight(320).withWidth(320) | 34 | + .model("stable-diffusion-v1-6") |
| 35 | + .height(320).width(320) | ||
| 36 | .build(); | 36 | .build(); |
| 37 | ImagePrompt prompt = new ImagePrompt("great wall", options); | 37 | ImagePrompt prompt = new ImagePrompt("great wall", options); |
| 38 | 38 |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionRespVO.java
| 1 | package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression; | 1 | package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression; |
| 2 | 2 | ||
| 3 | -import com.alibaba.excel.annotation.ExcelProperty; | 3 | +import cn.idev.excel.annotation.ExcelProperty; |
| 4 | import io.swagger.v3.oas.annotations.media.Schema; | 4 | import io.swagger.v3.oas.annotations.media.Schema; |
| 5 | import lombok.Data; | 5 | import lombok.Data; |
| 6 | 6 | ||
| @@ -27,4 +27,4 @@ public class BpmProcessExpressionRespVO { | @@ -27,4 +27,4 @@ public class BpmProcessExpressionRespVO { | ||
| 27 | @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | 27 | @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) |
| 28 | private LocalDateTime createTime; | 28 | private LocalDateTime createTime; |
| 29 | 29 | ||
| 30 | -} | ||
| 31 | \ No newline at end of file | 30 | \ No newline at end of file |
| 31 | +} |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java
| @@ -71,6 +71,9 @@ public class BpmSimpleModelNodeVO { | @@ -71,6 +71,9 @@ public class BpmSimpleModelNodeVO { | ||
| 71 | @Schema(description = "是否填写审批意见", example = "false") | 71 | @Schema(description = "是否填写审批意见", example = "false") |
| 72 | private Boolean reasonRequire; | 72 | private Boolean reasonRequire; |
| 73 | 73 | ||
| 74 | + @Schema(description = "跳过表达式", example = "{true}") | ||
| 75 | + private String skipExpression; // 用于审批节点 | ||
| 76 | + | ||
| 74 | /** | 77 | /** |
| 75 | * 审批节点拒绝处理 | 78 | * 审批节点拒绝处理 |
| 76 | */ | 79 | */ |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailRespVO.java
| @@ -73,7 +73,7 @@ public class BpmApprovalDetailRespVO { | @@ -73,7 +73,7 @@ public class BpmApprovalDetailRespVO { | ||
| 73 | private List<UserSimpleBaseVO> candidateUsers; // 只包含未生成 ApprovalTaskInfo 的用户列表 | 73 | private List<UserSimpleBaseVO> candidateUsers; // 只包含未生成 ApprovalTaskInfo 的用户列表 |
| 74 | 74 | ||
| 75 | @Schema(description = "流程编号", example = "8761d8e0-0922-11f0-bd37-00ff1db677bf") | 75 | @Schema(description = "流程编号", example = "8761d8e0-0922-11f0-bd37-00ff1db677bf") |
| 76 | - private String processInstanceId; // 当且仅当,该节点是子流程节点时,才会有值(CallActivity 的 processInstanceId 字段) | 76 | + private String processInstanceId; // 当且仅当,该节点是子流程节点时,才会有值(CallActivity 的 calledProcessInstanceId 字段) |
| 77 | 77 | ||
| 78 | } | 78 | } |
| 79 | 79 |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
| @@ -40,7 +40,7 @@ public interface ErrorCodeConstants { | @@ -40,7 +40,7 @@ public interface ErrorCodeConstants { | ||
| 40 | ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS = new ErrorCode(1_009_004_004, "任务({})的候选人({})不存在"); | 40 | ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS = new ErrorCode(1_009_004_004, "任务({})的候选人({})不存在"); |
| 41 | ErrorCode PROCESS_INSTANCE_START_USER_CAN_START = new ErrorCode(1_009_004_005, "发起流程失败,你没有权限发起该流程"); | 41 | ErrorCode PROCESS_INSTANCE_START_USER_CAN_START = new ErrorCode(1_009_004_005, "发起流程失败,你没有权限发起该流程"); |
| 42 | ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_005, "流程取消失败,该流程不允许取消"); | 42 | ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_005, "流程取消失败,该流程不允许取消"); |
| 43 | - ErrorCode PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR = new ErrorCode(1_009_004_006, "流程 Http 触发器请求调用失败"); | 43 | + ErrorCode PROCESS_INSTANCE_HTTP_CALL_ERROR = new ErrorCode(1_009_004_006, "流程 Http 请求调用失败"); |
| 44 | ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, "下一个任务({})的审批人未配置"); | 44 | ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, "下一个任务({})的审批人未配置"); |
| 45 | ErrorCode PROCESS_INSTANCE_CANCEL_CHILD_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_008, "子流程取消失败,子流程不允许取消"); | 45 | ErrorCode PROCESS_INSTANCE_CANCEL_CHILD_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_008, "子流程取消失败,子流程不允许取消"); |
| 46 | 46 | ||
| @@ -58,7 +58,6 @@ public interface ErrorCodeConstants { | @@ -58,7 +58,6 @@ public interface ErrorCodeConstants { | ||
| 58 | ErrorCode TASK_SIGN_DELETE_NO_PARENT = new ErrorCode(1_009_005_012, "任务减签失败,被减签的任务必须是通过加签生成的任务"); | 58 | ErrorCode TASK_SIGN_DELETE_NO_PARENT = new ErrorCode(1_009_005_012, "任务减签失败,被减签的任务必须是通过加签生成的任务"); |
| 59 | ErrorCode TASK_TRANSFER_FAIL_USER_REPEAT = new ErrorCode(1_009_005_013, "任务转办失败,转办人和当前审批人为同一人"); | 59 | ErrorCode TASK_TRANSFER_FAIL_USER_REPEAT = new ErrorCode(1_009_005_013, "任务转办失败,转办人和当前审批人为同一人"); |
| 60 | ErrorCode TASK_TRANSFER_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_014, "任务转办失败,转办人不存在"); | 60 | ErrorCode TASK_TRANSFER_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_014, "任务转办失败,转办人不存在"); |
| 61 | - ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!"); | ||
| 62 | ErrorCode TASK_SIGNATURE_NOT_EXISTS = new ErrorCode(1_009_005_015, "签名不能为空!"); | 61 | ErrorCode TASK_SIGNATURE_NOT_EXISTS = new ErrorCode(1_009_005_015, "签名不能为空!"); |
| 63 | ErrorCode TASK_REASON_REQUIRE = new ErrorCode(1_009_005_016, "审批意见不能为空!"); | 62 | ErrorCode TASK_REASON_REQUIRE = new ErrorCode(1_009_005_016, "审批意见不能为空!"); |
| 64 | 63 | ||
| @@ -74,6 +73,7 @@ public interface ErrorCodeConstants { | @@ -74,6 +73,7 @@ public interface ErrorCodeConstants { | ||
| 74 | ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(1_009_012_000, "流程分类不存在"); | 73 | ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(1_009_012_000, "流程分类不存在"); |
| 75 | ErrorCode CATEGORY_NAME_DUPLICATE = new ErrorCode(1_009_012_001, "流程分类名字【{}】重复"); | 74 | ErrorCode CATEGORY_NAME_DUPLICATE = new ErrorCode(1_009_012_001, "流程分类名字【{}】重复"); |
| 76 | ErrorCode CATEGORY_CODE_DUPLICATE = new ErrorCode(1_009_012_002, "流程分类编码【{}】重复"); | 75 | ErrorCode CATEGORY_CODE_DUPLICATE = new ErrorCode(1_009_012_002, "流程分类编码【{}】重复"); |
| 76 | + ErrorCode CATEGORY_DELETE_FAIL_MODEL_USED = new ErrorCode(1_009_012_003, "删除失败,流程分类【{}】已被流程模型使用,请先删除对应的流程模型"); | ||
| 77 | 77 | ||
| 78 | // ========== BPM 流程监听器 1-009-013-000 ========== | 78 | // ========== BPM 流程监听器 1-009-013-000 ========== |
| 79 | ErrorCode PROCESS_LISTENER_NOT_EXISTS = new ErrorCode(1_009_013_000, "流程监听器不存在"); | 79 | ErrorCode PROCESS_LISTENER_NOT_EXISTS = new ErrorCode(1_009_013_000, "流程监听器不存在"); |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java
| @@ -35,7 +35,7 @@ public enum BpmSimpleModelNodeTypeEnum implements ArrayValuable<Integer> { | @@ -35,7 +35,7 @@ public enum BpmSimpleModelNodeTypeEnum implements ArrayValuable<Integer> { | ||
| 35 | // 50 ~ 条件分支 | 35 | // 50 ~ 条件分支 |
| 36 | CONDITION_NODE(50, "条件", "sequenceFlow"), // 用于构建流转条件的表达式 | 36 | CONDITION_NODE(50, "条件", "sequenceFlow"), // 用于构建流转条件的表达式 |
| 37 | CONDITION_BRANCH_NODE(51, "条件分支", "exclusiveGateway"), | 37 | CONDITION_BRANCH_NODE(51, "条件分支", "exclusiveGateway"), |
| 38 | - PARALLEL_BRANCH_NODE(52, "并行分支", "parallelGateway"), | 38 | + PARALLEL_BRANCH_NODE(52, "并行分支", "inclusiveGateway"), // 并行分支使用包容网关实现,条件表达式结果设置为 true |
| 39 | INCLUSIVE_BRANCH_NODE(53, "包容分支", "inclusiveGateway"), | 39 | INCLUSIVE_BRANCH_NODE(53, "包容分支", "inclusiveGateway"), |
| 40 | ROUTER_BRANCH_NODE(54, "路由分支", "exclusiveGateway") | 40 | ROUTER_BRANCH_NODE(54, "路由分支", "exclusiveGateway") |
| 41 | ; | 41 | ; |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmTaskStatusEnum.java
| @@ -14,6 +14,7 @@ import lombok.Getter; | @@ -14,6 +14,7 @@ import lombok.Getter; | ||
| 14 | @AllArgsConstructor | 14 | @AllArgsConstructor |
| 15 | public enum BpmTaskStatusEnum { | 15 | public enum BpmTaskStatusEnum { |
| 16 | 16 | ||
| 17 | + SKIP(-2, "跳过"), | ||
| 17 | NOT_START(-1, "未开始"), | 18 | NOT_START(-1, "未开始"), |
| 18 | RUNNING(1, "审批中"), | 19 | RUNNING(1, "审批中"), |
| 19 | APPROVE(2, "审批通过"), | 20 | APPROVE(2, "审批通过"), |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java
| @@ -18,10 +18,7 @@ import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | @@ -18,10 +18,7 @@ import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | ||
| 18 | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; | 18 | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; |
| 19 | import com.google.common.annotations.VisibleForTesting; | 19 | import com.google.common.annotations.VisibleForTesting; |
| 20 | import lombok.extern.slf4j.Slf4j; | 20 | import lombok.extern.slf4j.Slf4j; |
| 21 | -import org.flowable.bpmn.model.BpmnModel; | ||
| 22 | -import org.flowable.bpmn.model.CallActivity; | ||
| 23 | -import org.flowable.bpmn.model.FlowElement; | ||
| 24 | -import org.flowable.bpmn.model.UserTask; | 21 | +import org.flowable.bpmn.model.*; |
| 25 | import org.flowable.engine.delegate.DelegateExecution; | 22 | import org.flowable.engine.delegate.DelegateExecution; |
| 26 | import org.flowable.engine.runtime.ProcessInstance; | 23 | import org.flowable.engine.runtime.ProcessInstance; |
| 27 | 24 | ||
| @@ -132,7 +129,7 @@ public class BpmTaskCandidateInvoker { | @@ -132,7 +129,7 @@ public class BpmTaskCandidateInvoker { | ||
| 132 | Long startUserId, String processDefinitionId, Map<String, Object> processVariables) { | 129 | Long startUserId, String processDefinitionId, Map<String, Object> processVariables) { |
| 133 | // 如果是 CallActivity 子流程,不进行计算候选人 | 130 | // 如果是 CallActivity 子流程,不进行计算候选人 |
| 134 | FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, activityId); | 131 | FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, activityId); |
| 135 | - if (flowElement instanceof CallActivity) { | 132 | + if (flowElement instanceof CallActivity || flowElement instanceof SubProcess) { |
| 136 | return new HashSet<>(); | 133 | return new HashSet<>(); |
| 137 | } | 134 | } |
| 138 | // 审批类型非人工审核时,不进行计算候选人。原因是:后续会自动通过、不通过 | 135 | // 审批类型非人工审核时,不进行计算候选人。原因是:后续会自动通过、不通过 |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java
| @@ -24,6 +24,7 @@ import static java.util.Collections.emptySet; | @@ -24,6 +24,7 @@ import static java.util.Collections.emptySet; | ||
| 24 | * @author 芋道源码 | 24 | * @author 芋道源码 |
| 25 | */ | 25 | */ |
| 26 | @Component | 26 | @Component |
| 27 | +@Deprecated // 仅仅是表达式的示例,建议使用 BpmTaskCandidateStartUserDeptLeaderStrategy 替代 | ||
| 27 | public class BpmTaskAssignLeaderExpression { | 28 | public class BpmTaskAssignLeaderExpression { |
| 28 | 29 | ||
| 29 | @Resource | 30 | @Resource |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java
| @@ -16,6 +16,7 @@ import java.util.Set; | @@ -16,6 +16,7 @@ import java.util.Set; | ||
| 16 | * @author 芋道源码 | 16 | * @author 芋道源码 |
| 17 | */ | 17 | */ |
| 18 | @Component | 18 | @Component |
| 19 | +@Deprecated // 仅仅是表达式的示例,建议使用 BpmTaskCandidateStartUserStrategy 替代 | ||
| 19 | public class BpmTaskAssignStartUserExpression { | 20 | public class BpmTaskAssignStartUserExpression { |
| 20 | 21 | ||
| 21 | @Resource | 22 | @Resource |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategy.java
| @@ -8,6 +8,7 @@ import com.google.common.collect.Sets; | @@ -8,6 +8,7 @@ import com.google.common.collect.Sets; | ||
| 8 | import lombok.extern.slf4j.Slf4j; | 8 | import lombok.extern.slf4j.Slf4j; |
| 9 | import org.flowable.bpmn.model.BpmnModel; | 9 | import org.flowable.bpmn.model.BpmnModel; |
| 10 | import org.flowable.common.engine.api.FlowableException; | 10 | import org.flowable.common.engine.api.FlowableException; |
| 11 | +import org.flowable.common.engine.impl.javax.el.PropertyNotFoundException; | ||
| 11 | import org.flowable.engine.delegate.DelegateExecution; | 12 | import org.flowable.engine.delegate.DelegateExecution; |
| 12 | import org.springframework.stereotype.Component; | 13 | import org.springframework.stereotype.Component; |
| 13 | 14 | ||
| @@ -48,10 +49,12 @@ public class BpmTaskCandidateExpressionStrategy implements BpmTaskCandidateStrat | @@ -48,10 +49,12 @@ public class BpmTaskCandidateExpressionStrategy implements BpmTaskCandidateStrat | ||
| 48 | Object result = FlowableUtils.getExpressionValue(variables, param); | 49 | Object result = FlowableUtils.getExpressionValue(variables, param); |
| 49 | return CollectionUtils.toLinkedHashSet(Long.class, result); | 50 | return CollectionUtils.toLinkedHashSet(Long.class, result); |
| 50 | } catch (FlowableException ex) { | 51 | } catch (FlowableException ex) { |
| 51 | - // 预测未运行的节点时候,表达式如果包含 execution 或者不存在的流程变量会抛异常, | ||
| 52 | - log.warn("[calculateUsersByActivity][表达式({}) 变量({}) 解析报错", param, variables, ex); | ||
| 53 | - // 不能预测候选人,返回空列表, 避免流程无法进行 | ||
| 54 | - return Sets.newHashSet(); | 52 | + // 预测未运行的节点时候,表达式如果包含 execution 或者不存在的流程变量会抛异常,此时忽略该异常!相当于说,不做流程预测!!! |
| 53 | + if (ex.getCause() != null && ex.getCause() instanceof PropertyNotFoundException) { | ||
| 54 | + return Sets.newHashSet(); | ||
| 55 | + } | ||
| 56 | + log.error("[calculateUsersByActivity][表达式({}) 变量({}) 解析报错", param, variables, ex); | ||
| 57 | + throw ex; | ||
| 55 | } | 58 | } |
| 56 | } | 59 | } |
| 57 | 60 |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java
| 1 | package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener; | 1 | package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; | ||
| 3 | import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; | 4 | import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; |
| 4 | import com.google.common.collect.ImmutableSet; | 5 | import com.google.common.collect.ImmutableSet; |
| 5 | import jakarta.annotation.Resource; | 6 | import jakarta.annotation.Resource; |
| @@ -37,18 +38,26 @@ public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEvent | @@ -37,18 +38,26 @@ public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEvent | ||
| 37 | 38 | ||
| 38 | @Override | 39 | @Override |
| 39 | protected void processCreated(FlowableEngineEntityEvent event) { | 40 | protected void processCreated(FlowableEngineEntityEvent event) { |
| 40 | - processInstanceService.processProcessInstanceCreated((ProcessInstance)event.getEntity()); | 41 | + ProcessInstance processInstance = (ProcessInstance) event.getEntity(); |
| 42 | + FlowableUtils.execute(processInstance.getTenantId(), | ||
| 43 | + () -> processInstanceService.processProcessInstanceCreated(processInstance)); | ||
| 41 | } | 44 | } |
| 42 | 45 | ||
| 43 | @Override | 46 | @Override |
| 44 | protected void processCompleted(FlowableEngineEntityEvent event) { | 47 | protected void processCompleted(FlowableEngineEntityEvent event) { |
| 45 | - processInstanceService.processProcessInstanceCompleted((ProcessInstance)event.getEntity()); | 48 | + ProcessInstance processInstance = (ProcessInstance) event.getEntity(); |
| 49 | + FlowableUtils.execute(processInstance.getTenantId(), | ||
| 50 | + () -> processInstanceService.processProcessInstanceCompleted(processInstance)); | ||
| 46 | } | 51 | } |
| 47 | 52 | ||
| 48 | - @Override // 特殊情况:当跳转到 EndEvent 流程实例未结束, 会执行 deleteProcessInstance 方法 | 53 | + @Override |
| 49 | protected void processCancelled(FlowableCancelledEvent event) { | 54 | protected void processCancelled(FlowableCancelledEvent event) { |
| 55 | + // 特殊情况:当跳转到 EndEvent 流程实例未结束, 会执行 deleteProcessInstance 方法 | ||
| 50 | ProcessInstance processInstance = processInstanceService.getProcessInstance(event.getProcessInstanceId()); | 56 | ProcessInstance processInstance = processInstanceService.getProcessInstance(event.getProcessInstanceId()); |
| 51 | - processInstanceService.processProcessInstanceCompleted(processInstance); | 57 | + if (processInstance != null) { |
| 58 | + FlowableUtils.execute(processInstance.getTenantId(), | ||
| 59 | + () -> processInstanceService.processProcessInstanceCompleted(processInstance)); | ||
| 60 | + } | ||
| 52 | } | 61 | } |
| 53 | 62 | ||
| 54 | } | 63 | } |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java
| @@ -3,10 +3,12 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener; | @@ -3,10 +3,12 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener; | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | import cn.hutool.core.util.ObjectUtil; | 4 | import cn.hutool.core.util.ObjectUtil; |
| 5 | import cn.hutool.core.util.StrUtil; | 5 | import cn.hutool.core.util.StrUtil; |
| 6 | +import cn.hutool.json.JSONObject; | ||
| 6 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | 7 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; |
| 7 | import cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventTypeEnum; | 8 | import cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventTypeEnum; |
| 8 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; | 9 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; |
| 9 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; | 10 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; |
| 11 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; | ||
| 10 | import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService; | 12 | import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService; |
| 11 | import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService; | 13 | import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService; |
| 12 | import com.google.common.collect.ImmutableSet; | 14 | import com.google.common.collect.ImmutableSet; |
| @@ -58,17 +60,20 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { | @@ -58,17 +60,20 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { | ||
| 58 | 60 | ||
| 59 | @Override | 61 | @Override |
| 60 | protected void taskCreated(FlowableEngineEntityEvent event) { | 62 | protected void taskCreated(FlowableEngineEntityEvent event) { |
| 61 | - taskService.processTaskCreated((Task) event.getEntity()); | 63 | + Task entity = (Task) event.getEntity(); |
| 64 | + FlowableUtils.execute(entity.getTenantId(), () -> taskService.processTaskCreated(entity)); | ||
| 62 | } | 65 | } |
| 63 | 66 | ||
| 64 | @Override | 67 | @Override |
| 65 | protected void taskAssigned(FlowableEngineEntityEvent event) { | 68 | protected void taskAssigned(FlowableEngineEntityEvent event) { |
| 66 | - taskService.processTaskAssigned((Task) event.getEntity()); | 69 | + Task entity = (Task) event.getEntity(); |
| 70 | + FlowableUtils.execute(entity.getTenantId(), () -> taskService.processTaskAssigned(entity)); | ||
| 67 | } | 71 | } |
| 68 | 72 | ||
| 69 | @Override | 73 | @Override |
| 70 | protected void taskCompleted(FlowableEngineEntityEvent event) { | 74 | protected void taskCompleted(FlowableEngineEntityEvent event) { |
| 71 | - taskService.processTaskCompleted((Task) event.getEntity()); | 75 | + Task entity = (Task) event.getEntity(); |
| 76 | + FlowableUtils.execute(entity.getTenantId(), () -> taskService.processTaskCompleted(entity)); | ||
| 72 | } | 77 | } |
| 73 | 78 | ||
| 74 | @Override | 79 | @Override |
| @@ -94,6 +99,23 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { | @@ -94,6 +99,23 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { | ||
| 94 | String processDefinitionId = event.getProcessDefinitionId(); | 99 | String processDefinitionId = event.getProcessDefinitionId(); |
| 95 | BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processDefinitionId); | 100 | BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processDefinitionId); |
| 96 | Job entity = (Job) event.getEntity(); | 101 | Job entity = (Job) event.getEntity(); |
| 102 | + // 特殊 from https://t.zsxq.com/h6oWr :当 elementId 为空时,尝试从 JobHandlerConfiguration 中解析 JSON 获取 | ||
| 103 | + String elementId = entity.getElementId(); | ||
| 104 | + if (elementId == null && entity.getJobHandlerConfiguration() != null) { | ||
| 105 | + try { | ||
| 106 | + String handlerConfig = entity.getJobHandlerConfiguration(); | ||
| 107 | + if (handlerConfig.startsWith("{") && handlerConfig.contains("activityId")) { | ||
| 108 | + elementId = new JSONObject(handlerConfig).getStr("activityId"); | ||
| 109 | + } | ||
| 110 | + } catch (Exception e) { | ||
| 111 | + log.error("[timerFired][解析 entity({}) 失败]", entity, e); | ||
| 112 | + return; | ||
| 113 | + } | ||
| 114 | + } | ||
| 115 | + if (elementId == null) { | ||
| 116 | + log.error("[timerFired][解析 entity({}) elementId 为空,跳过处理]", entity); | ||
| 117 | + return; | ||
| 118 | + } | ||
| 97 | FlowElement element = BpmnModelUtils.getFlowElementById(bpmnModel, entity.getElementId()); | 119 | FlowElement element = BpmnModelUtils.getFlowElementById(bpmnModel, entity.getElementId()); |
| 98 | if (!(element instanceof BoundaryEvent)) { | 120 | if (!(element instanceof BoundaryEvent)) { |
| 99 | return; | 121 | return; |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java
| @@ -6,15 +6,15 @@ import cn.iocoder.yudao.framework.common.core.KeyValue; | @@ -6,15 +6,15 @@ import cn.iocoder.yudao.framework.common.core.KeyValue; | ||
| 6 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 6 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 7 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | 7 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 8 | import cn.iocoder.yudao.framework.common.util.spring.SpringUtils; | 8 | import cn.iocoder.yudao.framework.common.util.spring.SpringUtils; |
| 9 | +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; | ||
| 10 | +import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent; | ||
| 9 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; | 11 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; |
| 10 | import cn.iocoder.yudao.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum; | 12 | import cn.iocoder.yudao.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum; |
| 11 | import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; | 13 | import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; |
| 12 | import com.fasterxml.jackson.core.type.TypeReference; | 14 | import com.fasterxml.jackson.core.type.TypeReference; |
| 13 | import lombok.extern.slf4j.Slf4j; | 15 | import lombok.extern.slf4j.Slf4j; |
| 14 | import org.flowable.engine.runtime.ProcessInstance; | 16 | import org.flowable.engine.runtime.ProcessInstance; |
| 15 | -import org.springframework.http.HttpEntity; | ||
| 16 | -import org.springframework.http.HttpMethod; | ||
| 17 | -import org.springframework.http.ResponseEntity; | 17 | +import org.springframework.http.*; |
| 18 | import org.springframework.util.LinkedMultiValueMap; | 18 | import org.springframework.util.LinkedMultiValueMap; |
| 19 | import org.springframework.util.MultiValueMap; | 19 | import org.springframework.util.MultiValueMap; |
| 20 | import org.springframework.web.client.RestClientException; | 20 | import org.springframework.web.client.RestClientException; |
| @@ -26,7 +26,7 @@ import java.util.Map; | @@ -26,7 +26,7 @@ import java.util.Map; | ||
| 26 | 26 | ||
| 27 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 27 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 28 | import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; | 28 | import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; |
| 29 | -import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR; | 29 | +import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_HTTP_CALL_ERROR; |
| 30 | 30 | ||
| 31 | /** | 31 | /** |
| 32 | * 工作流发起 HTTP 请求工具类 | 32 | * 工作流发起 HTTP 请求工具类 |
| @@ -42,7 +42,6 @@ public class BpmHttpRequestUtils { | @@ -42,7 +42,6 @@ public class BpmHttpRequestUtils { | ||
| 42 | List<BpmSimpleModelNodeVO.HttpRequestParam> bodyParams, | 42 | List<BpmSimpleModelNodeVO.HttpRequestParam> bodyParams, |
| 43 | Boolean handleResponse, | 43 | Boolean handleResponse, |
| 44 | List<KeyValue<String, String>> response) { | 44 | List<KeyValue<String, String>> response) { |
| 45 | - RestTemplate restTemplate = SpringUtils.getBean(RestTemplate.class); | ||
| 46 | BpmProcessInstanceService processInstanceService = SpringUtils.getBean(BpmProcessInstanceService.class); | 45 | BpmProcessInstanceService processInstanceService = SpringUtils.getBean(BpmProcessInstanceService.class); |
| 47 | 46 | ||
| 48 | // 1.1 设置请求头 | 47 | // 1.1 设置请求头 |
| @@ -51,6 +50,7 @@ public class BpmHttpRequestUtils { | @@ -51,6 +50,7 @@ public class BpmHttpRequestUtils { | ||
| 51 | MultiValueMap<String, String> body = buildHttpBody(processInstance, bodyParams); | 50 | MultiValueMap<String, String> body = buildHttpBody(processInstance, bodyParams); |
| 52 | 51 | ||
| 53 | // 2. 发起请求 | 52 | // 2. 发起请求 |
| 53 | + RestTemplate restTemplate = SpringUtils.getBean(RestTemplate.class); | ||
| 54 | ResponseEntity<String> responseEntity = sendHttpRequest(url, headers, body, restTemplate); | 54 | ResponseEntity<String> responseEntity = sendHttpRequest(url, headers, body, restTemplate); |
| 55 | 55 | ||
| 56 | // 3. 处理返回 | 56 | // 3. 处理返回 |
| @@ -78,27 +78,55 @@ public class BpmHttpRequestUtils { | @@ -78,27 +78,55 @@ public class BpmHttpRequestUtils { | ||
| 78 | } | 78 | } |
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | + public static void executeBpmHttpRequest(BpmProcessInstanceStatusEvent event, | ||
| 82 | + String url) { | ||
| 83 | + // 1.1 设置请求头 | ||
| 84 | + HttpHeaders headers = new HttpHeaders(); | ||
| 85 | + headers.setContentType(MediaType.APPLICATION_JSON); | ||
| 86 | + if (TenantContextHolder.getTenantId() != null) { | ||
| 87 | + headers.add(HEADER_TENANT_ID, String.valueOf(TenantContextHolder.getTenantId())); | ||
| 88 | + } else { | ||
| 89 | + BpmProcessInstanceService processInstanceService = SpringUtils.getBean(BpmProcessInstanceService.class); | ||
| 90 | + ProcessInstance processInstance = processInstanceService.getProcessInstance(event.getId()); | ||
| 91 | + if (processInstance != null) { | ||
| 92 | + headers.add(HEADER_TENANT_ID, String.valueOf(TenantContextHolder.getTenantId())); | ||
| 93 | + } | ||
| 94 | + } | ||
| 95 | + // 1.2 设置请求体 | ||
| 96 | +// MultiValueMap<String, String> body = new LinkedMultiValueMap<>(); | ||
| 97 | +// body.add("id", event.getId()); | ||
| 98 | +// body.add("processDefinitionKey", event.getProcessDefinitionKey()); | ||
| 99 | +// body.add("status", event.getStatus().toString()); | ||
| 100 | +// if (StrUtil.isNotEmpty(event.getBusinessKey())) { | ||
| 101 | +// body.add("businessKey", event.getBusinessKey()); | ||
| 102 | +// } | ||
| 103 | + | ||
| 104 | + // 2. 发起请求 | ||
| 105 | + RestTemplate restTemplate = SpringUtils.getBean(RestTemplate.class); | ||
| 106 | + sendHttpRequest(url, headers, event, restTemplate); | ||
| 107 | + } | ||
| 108 | + | ||
| 81 | public static ResponseEntity<String> sendHttpRequest(String url, | 109 | public static ResponseEntity<String> sendHttpRequest(String url, |
| 82 | MultiValueMap<String, String> headers, | 110 | MultiValueMap<String, String> headers, |
| 83 | - MultiValueMap<String, String> body, | 111 | + Object body, |
| 84 | RestTemplate restTemplate) { | 112 | RestTemplate restTemplate) { |
| 85 | - HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(body, headers); | 113 | + HttpEntity<Object> requestEntity = new HttpEntity<>(body, headers); |
| 86 | ResponseEntity<String> responseEntity; | 114 | ResponseEntity<String> responseEntity; |
| 87 | try { | 115 | try { |
| 88 | responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); | 116 | responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); |
| 89 | - log.info("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},响应结果:{}]", headers, body, responseEntity); | 117 | + log.info("[sendHttpRequest][HTTP 请求,请求头:{},请求体:{},响应结果:{}]", headers, body, responseEntity); |
| 90 | } catch (RestClientException e) { | 118 | } catch (RestClientException e) { |
| 91 | - log.error("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},请求出错:{}]", headers, body, e.getMessage()); | ||
| 92 | - throw exception(PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR); | 119 | + log.error("[sendHttpRequest][HTTP 请求,请求头:{},请求体:{},请求出错:{}]", headers, body, e.getMessage()); |
| 120 | + throw exception(PROCESS_INSTANCE_HTTP_CALL_ERROR); | ||
| 93 | } | 121 | } |
| 94 | return responseEntity; | 122 | return responseEntity; |
| 95 | } | 123 | } |
| 96 | 124 | ||
| 97 | public static MultiValueMap<String, String> buildHttpHeaders(ProcessInstance processInstance, | 125 | public static MultiValueMap<String, String> buildHttpHeaders(ProcessInstance processInstance, |
| 98 | List<BpmSimpleModelNodeVO.HttpRequestParam> headerSettings) { | 126 | List<BpmSimpleModelNodeVO.HttpRequestParam> headerSettings) { |
| 99 | - Map<String, Object> processVariables = processInstance.getProcessVariables(); | ||
| 100 | MultiValueMap<String, String> headers = new LinkedMultiValueMap<>(); | 127 | MultiValueMap<String, String> headers = new LinkedMultiValueMap<>(); |
| 101 | headers.add(HEADER_TENANT_ID, processInstance.getTenantId()); | 128 | headers.add(HEADER_TENANT_ID, processInstance.getTenantId()); |
| 129 | + Map<String, Object> processVariables = processInstance.getProcessVariables(); | ||
| 102 | addHttpRequestParam(headers, headerSettings, processVariables); | 130 | addHttpRequestParam(headers, headerSettings, processVariables); |
| 103 | return headers; | 131 | return headers; |
| 104 | } | 132 | } |
| @@ -108,7 +136,9 @@ public class BpmHttpRequestUtils { | @@ -108,7 +136,9 @@ public class BpmHttpRequestUtils { | ||
| 108 | Map<String, Object> processVariables = processInstance.getProcessVariables(); | 136 | Map<String, Object> processVariables = processInstance.getProcessVariables(); |
| 109 | MultiValueMap<String, String> body = new LinkedMultiValueMap<>(); | 137 | MultiValueMap<String, String> body = new LinkedMultiValueMap<>(); |
| 110 | addHttpRequestParam(body, bodySettings, processVariables); | 138 | addHttpRequestParam(body, bodySettings, processVariables); |
| 111 | - body.add("processInstanceId", processInstance.getId()); | 139 | + if (!body.containsKey("processInstanceId")) { // 避免重复添加 |
| 140 | + body.add("processInstanceId", processInstance.getId()); | ||
| 141 | + } | ||
| 112 | return body; | 142 | return body; |
| 113 | } | 143 | } |
| 114 | 144 |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
| @@ -478,7 +478,11 @@ public class BpmnModelUtils { | @@ -478,7 +478,11 @@ public class BpmnModelUtils { | ||
| 478 | */ | 478 | */ |
| 479 | public static FlowElement getFlowElementById(BpmnModel model, String flowElementId) { | 479 | public static FlowElement getFlowElementById(BpmnModel model, String flowElementId) { |
| 480 | Process process = model.getMainProcess(); | 480 | Process process = model.getMainProcess(); |
| 481 | - return process.getFlowElement(flowElementId); | 481 | + FlowElement flowElement = process.getFlowElement(flowElementId); |
| 482 | + if (flowElement != null) { | ||
| 483 | + return flowElement; | ||
| 484 | + } | ||
| 485 | + return model.getFlowElement(flowElementId); | ||
| 482 | } | 486 | } |
| 483 | 487 | ||
| 484 | /** | 488 | /** |
| @@ -796,9 +800,10 @@ public class BpmnModelUtils { | @@ -796,9 +800,10 @@ public class BpmnModelUtils { | ||
| 796 | || currentElement instanceof EndEvent | 800 | || currentElement instanceof EndEvent |
| 797 | || currentElement instanceof UserTask | 801 | || currentElement instanceof UserTask |
| 798 | || currentElement instanceof ServiceTask) { | 802 | || currentElement instanceof ServiceTask) { |
| 799 | - // 添加元素 | 803 | + // 添加节点 |
| 800 | FlowNode flowNode = (FlowNode) currentElement; | 804 | FlowNode flowNode = (FlowNode) currentElement; |
| 801 | resultElements.add(flowNode); | 805 | resultElements.add(flowNode); |
| 806 | + | ||
| 802 | // 遍历子节点 | 807 | // 遍历子节点 |
| 803 | flowNode.getOutgoingFlows().forEach( | 808 | flowNode.getOutgoingFlows().forEach( |
| 804 | nextElement -> simulateNextFlowElements(nextElement.getTargetFlowElement(), variables, resultElements, visitElements)); | 809 | nextElement -> simulateNextFlowElements(nextElement.getTargetFlowElement(), variables, resultElements, visitElements)); |
| @@ -832,6 +837,31 @@ public class BpmnModelUtils { | @@ -832,6 +837,31 @@ public class BpmnModelUtils { | ||
| 832 | } | 837 | } |
| 833 | 838 | ||
| 834 | /** | 839 | /** |
| 840 | + * 判断是否跳过此节点 | ||
| 841 | + * | ||
| 842 | + * @param flowNode 节点 | ||
| 843 | + * @param variables 流程变量 | ||
| 844 | + */ | ||
| 845 | + public static boolean isSkipNode(FlowElement flowNode, Map<String, Object> variables) { | ||
| 846 | + // 1. 检查节点是否有跳过表达式(支持多种任务节点类型) | ||
| 847 | + String skipExpression = null; | ||
| 848 | + if (flowNode instanceof UserTask) { | ||
| 849 | + skipExpression = ((UserTask) flowNode).getSkipExpression(); | ||
| 850 | + } else if (flowNode instanceof ServiceTask) { | ||
| 851 | + skipExpression = ((ServiceTask) flowNode).getSkipExpression(); | ||
| 852 | + } else if (flowNode instanceof ScriptTask) { | ||
| 853 | + skipExpression = ((ScriptTask) flowNode).getSkipExpression(); | ||
| 854 | + } | ||
| 855 | + | ||
| 856 | + if (StrUtil.isEmpty(skipExpression)) { | ||
| 857 | + return false; | ||
| 858 | + } | ||
| 859 | + | ||
| 860 | + // 2. 计算跳过表达式的值 | ||
| 861 | + return evalConditionExpress(variables, skipExpression); | ||
| 862 | + } | ||
| 863 | + | ||
| 864 | + /** | ||
| 835 | * 根据当前节点,获取下一个节点 | 865 | * 根据当前节点,获取下一个节点 |
| 836 | * | 866 | * |
| 837 | * @param currentElement 当前节点 | 867 | * @param currentElement 当前节点 |
| @@ -993,7 +1023,7 @@ public class BpmnModelUtils { | @@ -993,7 +1023,7 @@ public class BpmnModelUtils { | ||
| 993 | * @return 是否满足条件 | 1023 | * @return 是否满足条件 |
| 994 | */ | 1024 | */ |
| 995 | public static boolean evalConditionExpress(Map<String, Object> variables, String expression) { | 1025 | public static boolean evalConditionExpress(Map<String, Object> variables, String expression) { |
| 996 | - if (expression == null) { | 1026 | + if (StrUtil.isEmpty(expression)) { |
| 997 | return Boolean.FALSE; | 1027 | return Boolean.FALSE; |
| 998 | } | 1028 | } |
| 999 | // 如果 variables 为空,则创建一个的原因?可能 expression 的计算,不依赖于 variables | 1029 | // 如果 variables 为空,则创建一个的原因?可能 expression 的计算,不依赖于 variables |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java
| @@ -4,7 +4,6 @@ import cn.hutool.core.collection.CollUtil; | @@ -4,7 +4,6 @@ import cn.hutool.core.collection.CollUtil; | ||
| 4 | import cn.hutool.core.lang.Assert; | 4 | import cn.hutool.core.lang.Assert; |
| 5 | import cn.hutool.core.map.MapUtil; | 5 | import cn.hutool.core.map.MapUtil; |
| 6 | import cn.hutool.core.util.*; | 6 | import cn.hutool.core.util.*; |
| 7 | -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | ||
| 8 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | 7 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 9 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; | 8 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; |
| 10 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.ConditionGroups; | 9 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.ConditionGroups; |
| @@ -17,13 +16,14 @@ import cn.iocoder.yudao.module.bpm.service.task.listener.BpmCallActivityListener | @@ -17,13 +16,14 @@ import cn.iocoder.yudao.module.bpm.service.task.listener.BpmCallActivityListener | ||
| 17 | import cn.iocoder.yudao.module.bpm.service.task.listener.BpmUserTaskListener; | 16 | import cn.iocoder.yudao.module.bpm.service.task.listener.BpmUserTaskListener; |
| 18 | import org.flowable.bpmn.BpmnAutoLayout; | 17 | import org.flowable.bpmn.BpmnAutoLayout; |
| 19 | import org.flowable.bpmn.constants.BpmnXMLConstants; | 18 | import org.flowable.bpmn.constants.BpmnXMLConstants; |
| 20 | -import org.flowable.bpmn.model.Process; | ||
| 21 | import org.flowable.bpmn.model.*; | 19 | import org.flowable.bpmn.model.*; |
| 20 | +import org.flowable.bpmn.model.Process; | ||
| 22 | import org.flowable.engine.delegate.ExecutionListener; | 21 | import org.flowable.engine.delegate.ExecutionListener; |
| 23 | import org.flowable.engine.delegate.TaskListener; | 22 | import org.flowable.engine.delegate.TaskListener; |
| 24 | 23 | ||
| 25 | import java.util.*; | 24 | import java.util.*; |
| 26 | 25 | ||
| 26 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | ||
| 27 | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; | 27 | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; |
| 28 | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; | 28 | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; |
| 29 | import static java.util.Arrays.asList; | 29 | import static java.util.Arrays.asList; |
| @@ -239,13 +239,13 @@ public class SimpleModelUtils { | @@ -239,13 +239,13 @@ public class SimpleModelUtils { | ||
| 239 | // 3.1 分支有后续节点。即分支 1: A->B->C->D 的情况 | 239 | // 3.1 分支有后续节点。即分支 1: A->B->C->D 的情况 |
| 240 | if (isValidNode(conditionChildNode)) { | 240 | if (isValidNode(conditionChildNode)) { |
| 241 | // 3.1.1 建立与后续的节点的连线。例如说,建立 A->B 的连线 | 241 | // 3.1.1 建立与后续的节点的连线。例如说,建立 A->B 的连线 |
| 242 | - SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), conditionChildNode.getId(), item); | 242 | + SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), conditionChildNode.getId(), nodeType, item); |
| 243 | process.addFlowElement(sequenceFlow); | 243 | process.addFlowElement(sequenceFlow); |
| 244 | // 3.1.2 递归调用后续节点连线。例如说,建立 B->C->D 的连线 | 244 | // 3.1.2 递归调用后续节点连线。例如说,建立 B->C->D 的连线 |
| 245 | traverseNodeToBuildSequenceFlow(process, conditionChildNode, branchEndNodeId); | 245 | traverseNodeToBuildSequenceFlow(process, conditionChildNode, branchEndNodeId); |
| 246 | } else { | 246 | } else { |
| 247 | // 3.2 分支没有后续节点。例如说,建立 A->D 的连线 | 247 | // 3.2 分支没有后续节点。例如说,建立 A->D 的连线 |
| 248 | - SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), branchEndNodeId, item); | 248 | + SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), branchEndNodeId, nodeType, item); |
| 249 | process.addFlowElement(sequenceFlow); | 249 | process.addFlowElement(sequenceFlow); |
| 250 | } | 250 | } |
| 251 | } | 251 | } |
| @@ -464,6 +464,10 @@ public class SimpleModelUtils { | @@ -464,6 +464,10 @@ public class SimpleModelUtils { | ||
| 464 | addReasonRequire(node.getReasonRequire(), userTask); | 464 | addReasonRequire(node.getReasonRequire(), userTask); |
| 465 | // 节点类型 | 465 | // 节点类型 |
| 466 | addNodeType(node.getType(), userTask); | 466 | addNodeType(node.getType(), userTask); |
| 467 | + // 添加跳过表达式 | ||
| 468 | + if (StrUtil.isNotEmpty(node.getSkipExpression())) { | ||
| 469 | + userTask.setSkipExpression(node.getSkipExpression()); | ||
| 470 | + } | ||
| 467 | return userTask; | 471 | return userTask; |
| 468 | } | 472 | } |
| 469 | 473 | ||
| @@ -591,17 +595,23 @@ public class SimpleModelUtils { | @@ -591,17 +595,23 @@ public class SimpleModelUtils { | ||
| 591 | 595 | ||
| 592 | private static class ParallelBranchNodeConvert implements NodeConvert { | 596 | private static class ParallelBranchNodeConvert implements NodeConvert { |
| 593 | 597 | ||
| 598 | + /** | ||
| 599 | + * 并行分支使用包容网关。需要设置所有出口条件表达式的值为 true 。原因是,解决 https://t.zsxq.com/m6GXh 反馈问题 | ||
| 600 | + * | ||
| 601 | + * @see {@link ConditionNodeConvert#buildSequenceFlow} | ||
| 602 | + */ | ||
| 594 | @Override | 603 | @Override |
| 595 | - public List<ParallelGateway> convertList(BpmSimpleModelNodeVO node) { | ||
| 596 | - ParallelGateway parallelGateway = new ParallelGateway(); | ||
| 597 | - parallelGateway.setId(node.getId()); | 604 | + public List<InclusiveGateway> convertList(BpmSimpleModelNodeVO node) { |
| 605 | + | ||
| 606 | + InclusiveGateway inclusiveGateway = new InclusiveGateway(); | ||
| 607 | + inclusiveGateway.setId(node.getId()); | ||
| 598 | // TODO @jason:setName | 608 | // TODO @jason:setName |
| 599 | 609 | ||
| 600 | - // 并行聚合网关由程序创建,前端不需要传入 | ||
| 601 | - ParallelGateway joinParallelGateway = new ParallelGateway(); | 610 | + // 合并网关 由程序创建,前端不需要传入 |
| 611 | + InclusiveGateway joinParallelGateway = new InclusiveGateway(); | ||
| 602 | joinParallelGateway.setId(buildGatewayJoinId(node.getId())); | 612 | joinParallelGateway.setId(buildGatewayJoinId(node.getId())); |
| 603 | // TODO @jason:setName | 613 | // TODO @jason:setName |
| 604 | - return CollUtil.newArrayList(parallelGateway, joinParallelGateway); | 614 | + return CollUtil.newArrayList(inclusiveGateway, joinParallelGateway); |
| 605 | } | 615 | } |
| 606 | 616 | ||
| 607 | @Override | 617 | @Override |
| @@ -652,8 +662,14 @@ public class SimpleModelUtils { | @@ -652,8 +662,14 @@ public class SimpleModelUtils { | ||
| 652 | } | 662 | } |
| 653 | 663 | ||
| 654 | public static SequenceFlow buildSequenceFlow(String sourceId, String targetId, | 664 | public static SequenceFlow buildSequenceFlow(String sourceId, String targetId, |
| 655 | - BpmSimpleModelNodeVO node) { | ||
| 656 | - String conditionExpression = buildConditionExpression(node.getConditionSetting()); | 665 | + BpmSimpleModelNodeTypeEnum nodeType, BpmSimpleModelNodeVO node) { |
| 666 | + String conditionExpression; | ||
| 667 | + // 并行分支,使用包容网关实现,强制设置条件表达式为 true | ||
| 668 | + if (BpmSimpleModelNodeTypeEnum.PARALLEL_BRANCH_NODE == nodeType) { | ||
| 669 | + conditionExpression ="${true}"; | ||
| 670 | + } else { | ||
| 671 | + conditionExpression = buildConditionExpression(node.getConditionSetting()); | ||
| 672 | + } | ||
| 657 | return buildBpmnSequenceFlow(sourceId, targetId, node.getId(), node.getName(), conditionExpression); | 673 | return buildBpmnSequenceFlow(sourceId, targetId, node.getId(), node.getName(), conditionExpression); |
| 658 | } | 674 | } |
| 659 | } | 675 | } |
| @@ -662,7 +678,6 @@ public class SimpleModelUtils { | @@ -662,7 +678,6 @@ public class SimpleModelUtils { | ||
| 662 | * 构造条件表达式 | 678 | * 构造条件表达式 |
| 663 | */ | 679 | */ |
| 664 | public static String buildConditionExpression(BpmSimpleModelNodeVO.ConditionSetting conditionSetting) { | 680 | public static String buildConditionExpression(BpmSimpleModelNodeVO.ConditionSetting conditionSetting) { |
| 665 | - // 并行网关不需要设置条件 | ||
| 666 | if (conditionSetting == null) { | 681 | if (conditionSetting == null) { |
| 667 | return null; | 682 | return null; |
| 668 | } | 683 | } |
| @@ -684,15 +699,18 @@ public class SimpleModelUtils { | @@ -684,15 +699,18 @@ public class SimpleModelUtils { | ||
| 684 | if (conditionGroups == null || CollUtil.isEmpty(conditionGroups.getConditions())) { | 699 | if (conditionGroups == null || CollUtil.isEmpty(conditionGroups.getConditions())) { |
| 685 | return null; | 700 | return null; |
| 686 | } | 701 | } |
| 687 | - List<String> strConditionGroups = CollectionUtils.convertList(conditionGroups.getConditions(), item -> { | 702 | + List<String> strConditionGroups = convertList(conditionGroups.getConditions(), item -> { |
| 688 | if (CollUtil.isEmpty(item.getRules())) { | 703 | if (CollUtil.isEmpty(item.getRules())) { |
| 689 | return ""; | 704 | return ""; |
| 690 | } | 705 | } |
| 691 | // 构造规则表达式 | 706 | // 构造规则表达式 |
| 692 | - List<String> list = CollectionUtils.convertList(item.getRules(), (rule) -> { | 707 | + List<String> list = convertList(item.getRules(), (rule) -> { |
| 693 | String rightSide = NumberUtil.isNumber(rule.getRightSide()) ? rule.getRightSide() | 708 | String rightSide = NumberUtil.isNumber(rule.getRightSide()) ? rule.getRightSide() |
| 694 | : "\"" + rule.getRightSide() + "\""; // 如果非数值类型加引号 | 709 | : "\"" + rule.getRightSide() + "\""; // 如果非数值类型加引号 |
| 695 | - return String.format(" %s %s var:convertByType(%s,%s)", rule.getLeftSide(), rule.getOpCode(), rule.getLeftSide(), rightSide); | 710 | + return String.format(" vars:getOrDefault(%s, null) %s var:convertByType(%s,%s) ", |
| 711 | + rule.getLeftSide(), // 左侧:读取变量 | ||
| 712 | + rule.getOpCode(), // 中间:操作符,比较 | ||
| 713 | + rule.getLeftSide(), rightSide); // 右侧:转换变量,VariableConvertByTypeExpressionFunction | ||
| 696 | }); | 714 | }); |
| 697 | // 构造条件组的表达式 | 715 | // 构造条件组的表达式 |
| 698 | Boolean and = item.getAnd(); | 716 | Boolean and = item.getAnd(); |
| @@ -954,7 +972,7 @@ public class SimpleModelUtils { | @@ -954,7 +972,7 @@ public class SimpleModelUtils { | ||
| 954 | || nodeType == BpmSimpleModelNodeTypeEnum.COPY_NODE | 972 | || nodeType == BpmSimpleModelNodeTypeEnum.COPY_NODE |
| 955 | || nodeType == BpmSimpleModelNodeTypeEnum.CHILD_PROCESS | 973 | || nodeType == BpmSimpleModelNodeTypeEnum.CHILD_PROCESS |
| 956 | || nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) { | 974 | || nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) { |
| 957 | - // 添加元素 | 975 | + // 添加此节点 |
| 958 | resultNodes.add(currentNode); | 976 | resultNodes.add(currentNode); |
| 959 | } | 977 | } |
| 960 | 978 | ||
| @@ -1000,6 +1018,16 @@ public class SimpleModelUtils { | @@ -1000,6 +1018,16 @@ public class SimpleModelUtils { | ||
| 1000 | simulateNextNode(currentNode.getChildNode(), variables, resultNodes); | 1018 | simulateNextNode(currentNode.getChildNode(), variables, resultNodes); |
| 1001 | } | 1019 | } |
| 1002 | 1020 | ||
| 1021 | + /** | ||
| 1022 | + * 根据跳过表达式,判断是否跳过此节点 | ||
| 1023 | + */ | ||
| 1024 | + public static boolean isSkipNode(BpmSimpleModelNodeVO currentNode, Map<String, Object> variables) { | ||
| 1025 | + if (StrUtil.isEmpty(currentNode.getSkipExpression())) { | ||
| 1026 | + return false; | ||
| 1027 | + } | ||
| 1028 | + return BpmnModelUtils.evalConditionExpress(variables, currentNode.getSkipExpression()); | ||
| 1029 | + } | ||
| 1030 | + | ||
| 1003 | public static boolean evalConditionExpress(Map<String, Object> variables, BpmSimpleModelNodeVO.ConditionSetting conditionSetting) { | 1031 | public static boolean evalConditionExpress(Map<String, Object> variables, BpmSimpleModelNodeVO.ConditionSetting conditionSetting) { |
| 1004 | return BpmnModelUtils.evalConditionExpress(variables, buildConditionExpression(conditionSetting)); | 1032 | return BpmnModelUtils.evalConditionExpress(variables, buildConditionExpression(conditionSetting)); |
| 1005 | } | 1033 | } |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmCategoryServiceImpl.java
| @@ -34,6 +34,9 @@ public class BpmCategoryServiceImpl implements BpmCategoryService { | @@ -34,6 +34,9 @@ public class BpmCategoryServiceImpl implements BpmCategoryService { | ||
| 34 | @Resource | 34 | @Resource |
| 35 | private BpmCategoryMapper bpmCategoryMapper; | 35 | private BpmCategoryMapper bpmCategoryMapper; |
| 36 | 36 | ||
| 37 | + @Resource | ||
| 38 | + private BpmModelService modelService; | ||
| 39 | + | ||
| 37 | @Override | 40 | @Override |
| 38 | public Long createCategory(BpmCategorySaveReqVO createReqVO) { | 41 | public Long createCategory(BpmCategorySaveReqVO createReqVO) { |
| 39 | // 校验唯一 | 42 | // 校验唯一 |
| @@ -77,15 +80,22 @@ public class BpmCategoryServiceImpl implements BpmCategoryService { | @@ -77,15 +80,22 @@ public class BpmCategoryServiceImpl implements BpmCategoryService { | ||
| 77 | @Override | 80 | @Override |
| 78 | public void deleteCategory(Long id) { | 81 | public void deleteCategory(Long id) { |
| 79 | // 校验存在 | 82 | // 校验存在 |
| 80 | - validateCategoryExists(id); | 83 | + BpmCategoryDO category = validateCategoryExists(id); |
| 84 | + // 校验是否被流程模型使用 | ||
| 85 | + Long count = modelService.getModelCountByCategory(category.getCode()); | ||
| 86 | + if (count > 0) { | ||
| 87 | + throw exception(CATEGORY_DELETE_FAIL_MODEL_USED, category.getName()); | ||
| 88 | + } | ||
| 81 | // 删除 | 89 | // 删除 |
| 82 | bpmCategoryMapper.deleteById(id); | 90 | bpmCategoryMapper.deleteById(id); |
| 83 | } | 91 | } |
| 84 | 92 | ||
| 85 | - private void validateCategoryExists(Long id) { | ||
| 86 | - if (bpmCategoryMapper.selectById(id) == null) { | 93 | + private BpmCategoryDO validateCategoryExists(Long id) { |
| 94 | + BpmCategoryDO category = bpmCategoryMapper.selectById(id); | ||
| 95 | + if (category == null) { | ||
| 87 | throw exception(CATEGORY_NOT_EXISTS); | 96 | throw exception(CATEGORY_NOT_EXISTS); |
| 88 | } | 97 | } |
| 98 | + return category; | ||
| 89 | } | 99 | } |
| 90 | 100 | ||
| 91 | @Override | 101 | @Override |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java
| @@ -25,6 +25,14 @@ public interface BpmModelService { | @@ -25,6 +25,14 @@ public interface BpmModelService { | ||
| 25 | List<Model> getModelList(String name); | 25 | List<Model> getModelList(String name); |
| 26 | 26 | ||
| 27 | /** | 27 | /** |
| 28 | + * 根据分类编码获得流程模型数量 | ||
| 29 | + * | ||
| 30 | + * @param category 分类编码 | ||
| 31 | + * @return 流程模型数量 | ||
| 32 | + */ | ||
| 33 | + Long getModelCountByCategory(String category); | ||
| 34 | + | ||
| 35 | + /** | ||
| 28 | * 创建流程模型 | 36 | * 创建流程模型 |
| 29 | * | 37 | * |
| 30 | * @param modelVO 创建信息 | 38 | * @param modelVO 创建信息 |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
| @@ -89,6 +89,14 @@ public class BpmModelServiceImpl implements BpmModelService { | @@ -89,6 +89,14 @@ public class BpmModelServiceImpl implements BpmModelService { | ||
| 89 | } | 89 | } |
| 90 | 90 | ||
| 91 | @Override | 91 | @Override |
| 92 | + public Long getModelCountByCategory(String category) { | ||
| 93 | + return repositoryService.createModelQuery() | ||
| 94 | + .modelCategory(category) | ||
| 95 | + .modelTenantId(FlowableUtils.getTenantId()) | ||
| 96 | + .count(); | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + @Override | ||
| 92 | @Transactional(rollbackFor = Exception.class) | 100 | @Transactional(rollbackFor = Exception.class) |
| 93 | public String createModel(@Valid BpmModelSaveReqVO createReqVO) { | 101 | public String createModel(@Valid BpmModelSaveReqVO createReqVO) { |
| 94 | if (!ValidationUtils.isXmlNCName(createReqVO.getKey())) { | 102 | if (!ValidationUtils.isXmlNCName(createReqVO.getKey())) { |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
| @@ -4,11 +4,9 @@ import cn.hutool.core.collection.CollUtil; | @@ -4,11 +4,9 @@ import cn.hutool.core.collection.CollUtil; | ||
| 4 | import cn.hutool.core.collection.ListUtil; | 4 | import cn.hutool.core.collection.ListUtil; |
| 5 | import cn.hutool.core.date.DateUtil; | 5 | import cn.hutool.core.date.DateUtil; |
| 6 | import cn.hutool.core.lang.Assert; | 6 | import cn.hutool.core.lang.Assert; |
| 7 | -import cn.hutool.core.util.ArrayUtil; | ||
| 8 | -import cn.hutool.core.util.ObjUtil; | ||
| 9 | -import cn.hutool.core.util.ObjectUtil; | ||
| 10 | -import cn.hutool.core.util.StrUtil; | 7 | +import cn.hutool.core.util.*; |
| 11 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 8 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 9 | +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | ||
| 12 | import cn.iocoder.yudao.framework.common.util.date.DateUtils; | 10 | import cn.iocoder.yudao.framework.common.util.date.DateUtils; |
| 13 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | 11 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 14 | import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; | 12 | import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; |
| @@ -61,12 +59,15 @@ import org.flowable.task.api.history.HistoricTaskInstance; | @@ -61,12 +59,15 @@ import org.flowable.task.api.history.HistoricTaskInstance; | ||
| 61 | import org.springframework.context.annotation.Lazy; | 59 | import org.springframework.context.annotation.Lazy; |
| 62 | import org.springframework.stereotype.Service; | 60 | import org.springframework.stereotype.Service; |
| 63 | import org.springframework.transaction.annotation.Transactional; | 61 | import org.springframework.transaction.annotation.Transactional; |
| 62 | +import org.springframework.transaction.support.TransactionSynchronization; | ||
| 63 | +import org.springframework.transaction.support.TransactionSynchronizationManager; | ||
| 64 | import org.springframework.validation.annotation.Validated; | 64 | import org.springframework.validation.annotation.Validated; |
| 65 | 65 | ||
| 66 | import java.util.*; | 66 | import java.util.*; |
| 67 | 67 | ||
| 68 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 68 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 69 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; | 69 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; |
| 70 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | ||
| 70 | import static cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ActivityNode; | 71 | import static cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ActivityNode; |
| 71 | import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; | 72 | import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; |
| 72 | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID; | 73 | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID; |
| @@ -264,7 +265,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService | @@ -264,7 +265,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService | ||
| 264 | // 3. 获取下一个将要执行的节点集合 | 265 | // 3. 获取下一个将要执行的节点集合 |
| 265 | FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey()); | 266 | FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey()); |
| 266 | List<FlowNode> nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, processVariables); | 267 | List<FlowNode> nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, processVariables); |
| 267 | - List<ActivityNode> nextActivityNodes = convertList(nextFlowNodes, node -> new ActivityNode().setId(node.getId()) | 268 | + // 仅仅获取 UserTask 节点 TODO add from jason:如果网关节点和网关节点相连,获取下个 UserTask. 貌似有点不准。 |
| 269 | + List<FlowNode> nextUserTaskList = CollectionUtils.filterList(nextFlowNodes, node -> node instanceof UserTask); | ||
| 270 | + List<ActivityNode> nextActivityNodes = convertList(nextUserTaskList, node -> new ActivityNode().setId(node.getId()) | ||
| 268 | .setName(node.getName()).setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) | 271 | .setName(node.getName()).setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) |
| 269 | .setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) | 272 | .setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) |
| 270 | .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(node)) | 273 | .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(node)) |
| @@ -395,7 +398,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService | @@ -395,7 +398,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService | ||
| 395 | ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType() | 398 | ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType() |
| 396 | : ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”的识别 | 399 | : ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”的识别 |
| 397 | BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) | 400 | BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) |
| 398 | - .setStatus(FlowableUtils.getTaskStatus(task)) | 401 | + .setStatus(getEndActivityNodeStatus(task)) |
| 399 | .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) | 402 | .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) |
| 400 | .setStartTime(DateUtils.of(task.getCreateTime())).setEndTime(DateUtils.of(task.getEndTime())) | 403 | .setStartTime(DateUtils.of(task.getCreateTime())).setEndTime(DateUtils.of(task.getEndTime())) |
| 401 | .setTasks(singletonList(BpmProcessInstanceConvert.INSTANCE.buildApprovalTaskInfo(task))); | 404 | .setTasks(singletonList(BpmProcessInstanceConvert.INSTANCE.buildApprovalTaskInfo(task))); |
| @@ -449,7 +452,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService | @@ -449,7 +452,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService | ||
| 449 | .setNodeType(BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType()).setStatus(processInstanceStatus) | 452 | .setNodeType(BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType()).setStatus(processInstanceStatus) |
| 450 | .setStartTime(DateUtils.of(activity.getStartTime())) | 453 | .setStartTime(DateUtils.of(activity.getStartTime())) |
| 451 | .setEndTime(DateUtils.of(activity.getEndTime())) | 454 | .setEndTime(DateUtils.of(activity.getEndTime())) |
| 452 | - .setProcessInstanceId(activity.getProcessInstanceId()); | 455 | + .setProcessInstanceId(activity.getCalledProcessInstanceId()); |
| 453 | approvalNodes.add(callActivity); | 456 | approvalNodes.add(callActivity); |
| 454 | } | 457 | } |
| 455 | }); | 458 | }); |
| @@ -460,6 +463,18 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService | @@ -460,6 +463,18 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService | ||
| 460 | } | 463 | } |
| 461 | 464 | ||
| 462 | /** | 465 | /** |
| 466 | + * 获取结束节点的状态 | ||
| 467 | + */ | ||
| 468 | + private Integer getEndActivityNodeStatus(HistoricTaskInstance task) { | ||
| 469 | + Integer status = FlowableUtils.getTaskStatus(task); | ||
| 470 | + if (status != null) { | ||
| 471 | + return status; | ||
| 472 | + } | ||
| 473 | + // 结束节点未获取到状态,为跳过状态。可见 bpmn 或者 simple 的 skipExpression | ||
| 474 | + return BpmTaskStatusEnum.SKIP.getStatus(); | ||
| 475 | + } | ||
| 476 | + | ||
| 477 | + /** | ||
| 463 | * 获得【进行中】的活动节点们 | 478 | * 获得【进行中】的活动节点们 |
| 464 | */ | 479 | */ |
| 465 | private List<ActivityNode> getRunApproveNodeList(Long startUserId, | 480 | private List<ActivityNode> getRunApproveNodeList(Long startUserId, |
| @@ -521,7 +536,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService | @@ -521,7 +536,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService | ||
| 521 | activityNode.setCandidateUserIds(CollUtil.sub(candidateUserIds, index + 1, candidateUserIds.size())); | 536 | activityNode.setCandidateUserIds(CollUtil.sub(candidateUserIds, index + 1, candidateUserIds.size())); |
| 522 | } | 537 | } |
| 523 | if (BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType().equals(activityNode.getNodeType())) { | 538 | if (BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType().equals(activityNode.getNodeType())) { |
| 524 | - activityNode.setProcessInstanceId(firstActivity.getProcessInstanceId()); | 539 | + activityNode.setProcessInstanceId(firstActivity.getCalledProcessInstanceId()); |
| 525 | } | 540 | } |
| 526 | return activityNode; | 541 | return activityNode; |
| 527 | }); | 542 | }); |
| @@ -562,10 +577,14 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService | @@ -562,10 +577,14 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService | ||
| 562 | if (runActivityIds.contains(node.getId())) { | 577 | if (runActivityIds.contains(node.getId())) { |
| 563 | return null; | 578 | return null; |
| 564 | } | 579 | } |
| 565 | - | 580 | + Integer status = BpmTaskStatusEnum.NOT_START.getStatus(); |
| 581 | + // 如果节点被跳过。设置状态为跳过 | ||
| 582 | + if (SimpleModelUtils.isSkipNode(node, processVariables)) { | ||
| 583 | + status = BpmTaskStatusEnum.SKIP.getStatus(); | ||
| 584 | + } | ||
| 566 | ActivityNode activityNode = new ActivityNode().setId(node.getId()).setName(node.getName()) | 585 | ActivityNode activityNode = new ActivityNode().setId(node.getId()).setName(node.getName()) |
| 567 | .setNodeType(node.getType()).setCandidateStrategy(node.getCandidateStrategy()) | 586 | .setNodeType(node.getType()).setCandidateStrategy(node.getCandidateStrategy()) |
| 568 | - .setStatus(BpmTaskStatusEnum.NOT_START.getStatus()); | 587 | + .setStatus(status); |
| 569 | 588 | ||
| 570 | // 1. 开始节点/审批节点 | 589 | // 1. 开始节点/审批节点 |
| 571 | if (ObjectUtils.equalsAny(node.getType(), | 590 | if (ObjectUtils.equalsAny(node.getType(), |
| @@ -605,8 +624,13 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService | @@ -605,8 +624,13 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService | ||
| 605 | if (runActivityIds.contains(node.getId())) { | 624 | if (runActivityIds.contains(node.getId())) { |
| 606 | return null; | 625 | return null; |
| 607 | } | 626 | } |
| 627 | + Integer status = BpmTaskStatusEnum.NOT_START.getStatus(); | ||
| 628 | + // 如果节点被跳过,状态设置为跳过 | ||
| 629 | + if(BpmnModelUtils.isSkipNode(node, processVariables)){ | ||
| 630 | + status = BpmTaskStatusEnum.SKIP.getStatus(); | ||
| 631 | + } | ||
| 608 | ActivityNode activityNode = new ActivityNode().setId(node.getId()) | 632 | ActivityNode activityNode = new ActivityNode().setId(node.getId()) |
| 609 | - .setStatus(BpmTaskStatusEnum.NOT_START.getStatus()); | 633 | + .setStatus(status); |
| 610 | 634 | ||
| 611 | // 1. 开始节点 | 635 | // 1. 开始节点 |
| 612 | if (node instanceof StartEvent) { | 636 | if (node instanceof StartEvent) { |
| @@ -771,17 +795,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService | @@ -771,17 +795,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService | ||
| 771 | processInstanceBuilder.predefineProcessInstanceId(processIdRedisDAO.generate(processIdRule)); | 795 | processInstanceBuilder.predefineProcessInstanceId(processIdRedisDAO.generate(processIdRule)); |
| 772 | } | 796 | } |
| 773 | // 3.2 流程名称 | 797 | // 3.2 流程名称 |
| 774 | - BpmModelMetaInfoVO.TitleSetting titleSetting = processDefinitionInfo.getTitleSetting(); | ||
| 775 | - if (titleSetting != null && Boolean.TRUE.equals(titleSetting.getEnable())) { | ||
| 776 | - AdminUserRespDTO user = adminUserApi.getUser(userId); | ||
| 777 | - Map<String, Object> cloneVariables = new HashMap<>(variables); | ||
| 778 | - cloneVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, user.getNickname()); | ||
| 779 | - cloneVariables.put(BpmnVariableConstants.PROCESS_START_TIME, DateUtil.now()); | ||
| 780 | - cloneVariables.put(BpmnVariableConstants.PROCESS_DEFINITION_NAME, definition.getName().trim()); | ||
| 781 | - processInstanceBuilder.name(StrUtil.format(titleSetting.getTitle(), cloneVariables)); | ||
| 782 | - } else { | ||
| 783 | - processInstanceBuilder.name(definition.getName().trim()); | ||
| 784 | - } | 798 | + processInstanceBuilder.name(generateProcessInstanceName(userId, definition, processDefinitionInfo, variables)); |
| 785 | // 3.3 发起流程实例 | 799 | // 3.3 发起流程实例 |
| 786 | ProcessInstance instance = processInstanceBuilder.start(); | 800 | ProcessInstance instance = processInstanceBuilder.start(); |
| 787 | return instance.getId(); | 801 | return instance.getId(); |
| @@ -817,6 +831,25 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService | @@ -817,6 +831,25 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService | ||
| 817 | }); | 831 | }); |
| 818 | } | 832 | } |
| 819 | 833 | ||
| 834 | + private String generateProcessInstanceName(Long userId, | ||
| 835 | + ProcessDefinition definition, | ||
| 836 | + BpmProcessDefinitionInfoDO definitionInfo, | ||
| 837 | + Map<String, Object> variables) { | ||
| 838 | + if (definition == null || definitionInfo == null) { | ||
| 839 | + return null; | ||
| 840 | + } | ||
| 841 | + BpmModelMetaInfoVO.TitleSetting titleSetting = definitionInfo.getTitleSetting(); | ||
| 842 | + if (titleSetting == null || !BooleanUtil.isTrue(titleSetting.getEnable())) { | ||
| 843 | + return definition.getName(); | ||
| 844 | + } | ||
| 845 | + AdminUserRespDTO user = adminUserApi.getUser(userId); | ||
| 846 | + Map<String, Object> cloneVariables = new HashMap<>(variables); | ||
| 847 | + cloneVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, user.getNickname()); | ||
| 848 | + cloneVariables.put(BpmnVariableConstants.PROCESS_START_TIME, DateUtil.now()); | ||
| 849 | + cloneVariables.put(BpmnVariableConstants.PROCESS_DEFINITION_NAME, definition.getName().trim()); | ||
| 850 | + return StrUtil.format(definitionInfo.getTitleSetting().getTitle(), cloneVariables); | ||
| 851 | + } | ||
| 852 | + | ||
| 820 | @Override | 853 | @Override |
| 821 | public void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { | 854 | public void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { |
| 822 | // 1.1 校验流程实例存在 | 855 | // 1.1 校验流程实例存在 |
| @@ -833,7 +866,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService | @@ -833,7 +866,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService | ||
| 833 | .getProcessDefinitionInfo(instance.getProcessDefinitionId()); | 866 | .getProcessDefinitionInfo(instance.getProcessDefinitionId()); |
| 834 | Assert.notNull(processDefinitionInfo, "流程定义({})不存在", processDefinitionInfo); | 867 | Assert.notNull(processDefinitionInfo, "流程定义({})不存在", processDefinitionInfo); |
| 835 | if (processDefinitionInfo.getAllowCancelRunningProcess() != null // 防止未配置 AllowCancelRunningProcess , 默认为可取消 | 868 | if (processDefinitionInfo.getAllowCancelRunningProcess() != null // 防止未配置 AllowCancelRunningProcess , 默认为可取消 |
| 836 | - && Boolean.FALSE.equals(processDefinitionInfo.getAllowCancelRunningProcess())) { | 869 | + && BooleanUtil.isFalse(processDefinitionInfo.getAllowCancelRunningProcess())) { |
| 837 | throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW); | 870 | throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW); |
| 838 | } | 871 | } |
| 839 | // 1.4 子流程不允许取消 | 872 | // 1.4 子流程不允许取消 |
| @@ -900,64 +933,77 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService | @@ -900,64 +933,77 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService | ||
| 900 | 933 | ||
| 901 | @Override | 934 | @Override |
| 902 | public void processProcessInstanceCompleted(ProcessInstance instance) { | 935 | public void processProcessInstanceCompleted(ProcessInstance instance) { |
| 903 | - // 注意:需要基于 instance 设置租户编号,避免 Flowable 内部异步时,丢失租户编号 | ||
| 904 | - FlowableUtils.execute(instance.getTenantId(), () -> { | ||
| 905 | - // 1.1 获取当前状态 | ||
| 906 | - Integer status = (Integer) instance.getProcessVariables() | ||
| 907 | - .get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); | ||
| 908 | - String reason = (String) instance.getProcessVariables() | ||
| 909 | - .get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON); | ||
| 910 | - // 1.2 当流程状态还是审批状态中,说明审批通过了,则变更下它的状态 | ||
| 911 | - // 为什么这么处理?因为流程完成,并且完成了,说明审批通过了 | ||
| 912 | - if (Objects.equals(status, BpmProcessInstanceStatusEnum.RUNNING.getStatus())) { | ||
| 913 | - status = BpmProcessInstanceStatusEnum.APPROVE.getStatus(); | ||
| 914 | - runtimeService.setVariable(instance.getId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, | ||
| 915 | - status); | ||
| 916 | - } | 936 | + // 1.1 获取当前状态 |
| 937 | + Integer status = (Integer) instance.getProcessVariables() | ||
| 938 | + .get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); | ||
| 939 | + String reason = (String) instance.getProcessVariables() | ||
| 940 | + .get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON); | ||
| 941 | + // 1.2 当流程状态还是审批状态中,说明审批通过了,则变更下它的状态 | ||
| 942 | + // 为什么这么处理?因为流程完成,并且完成了,说明审批通过了 | ||
| 943 | + if (Objects.equals(status, BpmProcessInstanceStatusEnum.RUNNING.getStatus())) { | ||
| 944 | + status = BpmProcessInstanceStatusEnum.APPROVE.getStatus(); | ||
| 945 | + runtimeService.setVariable(instance.getId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, | ||
| 946 | + status); | ||
| 947 | + } | ||
| 948 | + | ||
| 949 | + // 2. 发送对应的消息通知 | ||
| 950 | + if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) { | ||
| 951 | + messageService.sendMessageWhenProcessInstanceApprove( | ||
| 952 | + BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance)); | ||
| 953 | + } else if (Objects.equals(status, BpmProcessInstanceStatusEnum.REJECT.getStatus())) { | ||
| 954 | + messageService.sendMessageWhenProcessInstanceReject( | ||
| 955 | + BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(instance, reason)); | ||
| 956 | + } | ||
| 957 | + | ||
| 958 | + // 3. 发送流程实例的状态事件 | ||
| 959 | + processInstanceEventPublisher.sendProcessInstanceResultEvent( | ||
| 960 | + BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, status)); | ||
| 961 | + | ||
| 962 | + // 4. 流程后置通知 | ||
| 963 | + if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) { | ||
| 964 | + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService. | ||
| 965 | + getProcessDefinitionInfo(instance.getProcessDefinitionId()); | ||
| 966 | + if (ObjUtil.isNotNull(processDefinitionInfo) && | ||
| 967 | + ObjUtil.isNotNull(processDefinitionInfo.getProcessAfterTriggerSetting())) { | ||
| 968 | + BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessAfterTriggerSetting(); | ||
| 917 | 969 | ||
| 918 | - // 2. 发送对应的消息通知 | ||
| 919 | - if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) { | ||
| 920 | - messageService.sendMessageWhenProcessInstanceApprove( | ||
| 921 | - BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance)); | ||
| 922 | - } else if (Objects.equals(status, BpmProcessInstanceStatusEnum.REJECT.getStatus())) { | ||
| 923 | - messageService.sendMessageWhenProcessInstanceReject( | ||
| 924 | - BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(instance, reason)); | 970 | + BpmHttpRequestUtils.executeBpmHttpRequest(instance, |
| 971 | + setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse()); | ||
| 925 | } | 972 | } |
| 973 | + } | ||
| 974 | + } | ||
| 926 | 975 | ||
| 927 | - // 3. 发送流程实例的状态事件 | ||
| 928 | - processInstanceEventPublisher.sendProcessInstanceResultEvent( | ||
| 929 | - BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, status)); | 976 | + @Override |
| 977 | + public void processProcessInstanceCreated(ProcessInstance instance) { | ||
| 978 | + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService. | ||
| 979 | + getProcessDefinitionInfo(instance.getProcessDefinitionId()); | ||
| 980 | + ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(instance.getProcessDefinitionId()); | ||
| 981 | + if (processDefinition == null || processDefinitionInfo == null) { | ||
| 982 | + return; | ||
| 983 | + } | ||
| 930 | 984 | ||
| 931 | - // 4. 流程后置通知 | ||
| 932 | - if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) { | ||
| 933 | - BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService. | ||
| 934 | - getProcessDefinitionInfo(instance.getProcessDefinitionId()); | ||
| 935 | - if (ObjUtil.isNotNull(processDefinitionInfo) && | ||
| 936 | - ObjUtil.isNotNull(processDefinitionInfo.getProcessAfterTriggerSetting())) { | ||
| 937 | - BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessAfterTriggerSetting(); | 985 | + // 自定义标题。目的:主要处理子流程的标题无法处理 |
| 986 | + // 注意:必须使用 TransactionSynchronizationManager 事务提交后,否则不生效!!! | ||
| 987 | + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { | ||
| 938 | 988 | ||
| 939 | - BpmHttpRequestUtils.executeBpmHttpRequest(instance, | ||
| 940 | - setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse()); | 989 | + @Override |
| 990 | + public void afterCommit() { | ||
| 991 | + String name = generateProcessInstanceName(Long.valueOf(instance.getStartUserId()), | ||
| 992 | + processDefinition, processDefinitionInfo, instance.getProcessVariables()); | ||
| 993 | + if (ObjUtil.notEqual(instance.getName(), name)) { | ||
| 994 | + runtimeService.setProcessInstanceName(instance.getProcessInstanceId(), name); | ||
| 941 | } | 995 | } |
| 942 | } | 996 | } |
| 943 | - }); | ||
| 944 | - } | ||
| 945 | 997 | ||
| 946 | - @Override | ||
| 947 | - public void processProcessInstanceCreated(ProcessInstance instance) { | ||
| 948 | - // 注意:需要基于 instance 设置租户编号,避免 Flowable 内部异步时,丢失租户编号 | ||
| 949 | - FlowableUtils.execute(instance.getTenantId(), () -> { | ||
| 950 | - // 流程前置通知 | ||
| 951 | - BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService. | ||
| 952 | - getProcessDefinitionInfo(instance.getProcessDefinitionId()); | ||
| 953 | - if (ObjUtil.isNull(processDefinitionInfo) || | ||
| 954 | - ObjUtil.isNull(processDefinitionInfo.getProcessBeforeTriggerSetting())) { | ||
| 955 | - return; | ||
| 956 | - } | ||
| 957 | - BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessBeforeTriggerSetting(); | ||
| 958 | - BpmHttpRequestUtils.executeBpmHttpRequest(instance, | ||
| 959 | - setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse()); | ||
| 960 | }); | 998 | }); |
| 999 | + | ||
| 1000 | + // 流程前置通知 | ||
| 1001 | + if (ObjUtil.isNull(processDefinitionInfo.getProcessBeforeTriggerSetting())) { | ||
| 1002 | + return; | ||
| 1003 | + } | ||
| 1004 | + BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessBeforeTriggerSetting(); | ||
| 1005 | + BpmHttpRequestUtils.executeBpmHttpRequest(instance, | ||
| 1006 | + setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse()); | ||
| 961 | } | 1007 | } |
| 962 | 1008 | ||
| 963 | } | 1009 | } |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
| @@ -230,10 +230,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -230,10 +230,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 230 | if (StrUtil.isNotBlank(pageVO.getName())) { | 230 | if (StrUtil.isNotBlank(pageVO.getName())) { |
| 231 | taskQuery.taskNameLike("%" + pageVO.getName() + "%"); | 231 | taskQuery.taskNameLike("%" + pageVO.getName() + "%"); |
| 232 | } | 232 | } |
| 233 | - if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) { | ||
| 234 | - taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0])); | ||
| 235 | - taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1])); | ||
| 236 | - } | 233 | +// if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) { |
| 234 | +// taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0])); | ||
| 235 | +// taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1])); | ||
| 236 | +// } | ||
| 237 | // 执行查询 | 237 | // 执行查询 |
| 238 | long count = taskQuery.count(); | 238 | long count = taskQuery.count(); |
| 239 | if (count == 0) { | 239 | if (count == 0) { |
| @@ -244,6 +244,12 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -244,6 +244,12 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 244 | // 特殊:强制移除自动完成的“发起人”节点 | 244 | // 特殊:强制移除自动完成的“发起人”节点 |
| 245 | // 补充说明:由于 taskQuery 无法方面的过滤,所以暂时通过内存过滤 | 245 | // 补充说明:由于 taskQuery 无法方面的过滤,所以暂时通过内存过滤 |
| 246 | tasks.removeIf(task -> task.getTaskDefinitionKey().equals(START_USER_NODE_ID)); | 246 | tasks.removeIf(task -> task.getTaskDefinitionKey().equals(START_USER_NODE_ID)); |
| 247 | + // TODO @芋艿:https://t.zsxq.com/MNzqp 【flowable bug】:taskCreatedAfter、taskCreatedBefore 拼接的是 OR | ||
| 248 | + if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) { | ||
| 249 | + tasks.removeIf(task -> task.getCreateTime() == null | ||
| 250 | + || task.getCreateTime().before(DateUtils.of(pageVO.getCreateTime()[0])) | ||
| 251 | + || task.getCreateTime().after(DateUtils.of(pageVO.getCreateTime()[1]))); | ||
| 252 | + } | ||
| 247 | return new PageResult<>(tasks, count); | 253 | return new PageResult<>(tasks, count); |
| 248 | } | 254 | } |
| 249 | 255 | ||
| @@ -259,16 +265,22 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -259,16 +265,22 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 259 | if (StrUtil.isNotEmpty(pageVO.getCategory())) { | 265 | if (StrUtil.isNotEmpty(pageVO.getCategory())) { |
| 260 | taskQuery.taskCategory(pageVO.getCategory()); | 266 | taskQuery.taskCategory(pageVO.getCategory()); |
| 261 | } | 267 | } |
| 262 | - if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) { | ||
| 263 | - taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0])); | ||
| 264 | - taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1])); | ||
| 265 | - } | 268 | +// if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) { |
| 269 | +// taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0])); | ||
| 270 | +// taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1])); | ||
| 271 | +// } | ||
| 266 | // 执行查询 | 272 | // 执行查询 |
| 267 | long count = taskQuery.count(); | 273 | long count = taskQuery.count(); |
| 268 | if (count == 0) { | 274 | if (count == 0) { |
| 269 | return PageResult.empty(); | 275 | return PageResult.empty(); |
| 270 | } | 276 | } |
| 271 | List<HistoricTaskInstance> tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize()); | 277 | List<HistoricTaskInstance> tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize()); |
| 278 | + // TODO @芋艿:https://t.zsxq.com/MNzqp 【flowable bug】:taskCreatedAfter、taskCreatedBefore 拼接的是 OR | ||
| 279 | + if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) { | ||
| 280 | + tasks.removeIf(task -> task.getCreateTime() == null | ||
| 281 | + || task.getCreateTime().before(DateUtils.of(pageVO.getCreateTime()[0])) | ||
| 282 | + || task.getCreateTime().after(DateUtils.of(pageVO.getCreateTime()[1]))); | ||
| 283 | + } | ||
| 272 | return new PageResult<>(tasks, count); | 284 | return new PageResult<>(tasks, count); |
| 273 | } | 285 | } |
| 274 | 286 | ||
| @@ -886,7 +898,9 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -886,7 +898,9 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 886 | if (!returnTaskKeyList.contains(task.getTaskDefinitionKey())) { | 898 | if (!returnTaskKeyList.contains(task.getTaskDefinitionKey())) { |
| 887 | return; | 899 | return; |
| 888 | } | 900 | } |
| 889 | - runExecutionIds.add(task.getExecutionId()); | 901 | + if (task.getExecutionId() != null) { |
| 902 | + runExecutionIds.add(task.getExecutionId()); | ||
| 903 | + } | ||
| 890 | 904 | ||
| 891 | // 判断是否分配给自己任务,因为会签任务,一个节点会有多个任务 | 905 | // 判断是否分配给自己任务,因为会签任务,一个节点会有多个任务 |
| 892 | if (isAssignUserTask(userId, task)) { // 情况一:自己的任务,进行 RETURN 标记 | 906 | if (isAssignUserTask(userId, task)) { // 情况一:自己的任务,进行 RETURN 标记 |
| @@ -933,7 +947,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -933,7 +947,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 933 | BpmCommentTypeEnum.DELEGATE_START.formatComment(currentUser.getNickname(), delegateUser.getNickname(), reqVO.getReason())); | 947 | BpmCommentTypeEnum.DELEGATE_START.formatComment(currentUser.getNickname(), delegateUser.getNickname(), reqVO.getReason())); |
| 934 | 948 | ||
| 935 | // 3.1 设置任务所有人 (owner) 为原任务的处理人 (assignee) | 949 | // 3.1 设置任务所有人 (owner) 为原任务的处理人 (assignee) |
| 936 | - taskService.setOwner(taskId, task.getAssignee()); | 950 | + // 特殊:如果已经被委派(owner 非空),则不需要更新 owner:https://gitee.com/zhijiantianya/yudao-cloud/issues/ICJ153 |
| 951 | + if (StrUtil.isEmpty(task.getOwner())) { | ||
| 952 | + taskService.setOwner(taskId, task.getAssignee()); | ||
| 953 | + } | ||
| 937 | // 3.2 执行委派,将任务委派给 delegateUser | 954 | // 3.2 执行委派,将任务委派给 delegateUser |
| 938 | taskService.delegateTask(taskId, reqVO.getDelegateUserId().toString()); | 955 | taskService.delegateTask(taskId, reqVO.getDelegateUserId().toString()); |
| 939 | // 补充说明:委托不单独设置状态。如果需要,可通过 Task 的 DelegationState 字段,判断是否为 DelegationState.PENDING 委托中 | 956 | // 补充说明:委托不单独设置状态。如果需要,可通过 Task 的 DelegationState 字段,判断是否为 DelegationState.PENDING 委托中 |
| @@ -959,7 +976,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -959,7 +976,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 959 | BpmCommentTypeEnum.TRANSFER.formatComment(currentUser.getNickname(), assigneeUser.getNickname(), reqVO.getReason())); | 976 | BpmCommentTypeEnum.TRANSFER.formatComment(currentUser.getNickname(), assigneeUser.getNickname(), reqVO.getReason())); |
| 960 | 977 | ||
| 961 | // 3.1 设置任务所有人 (owner) 为原任务的处理人 (assignee) | 978 | // 3.1 设置任务所有人 (owner) 为原任务的处理人 (assignee) |
| 962 | - taskService.setOwner(taskId, task.getAssignee()); | 979 | + // 特殊:如果已经被转派(owner 非空),则不需要更新 owner:https://gitee.com/zhijiantianya/yudao-cloud/issues/ICJ153 |
| 980 | + if (StrUtil.isEmpty(task.getOwner())) { | ||
| 981 | + taskService.setOwner(taskId, task.getAssignee()); | ||
| 982 | + } | ||
| 963 | // 3.2 执行转派(审批人),将任务转派给 assigneeUser | 983 | // 3.2 执行转派(审批人),将任务转派给 assigneeUser |
| 964 | // 委托( delegate)和转派(transfer)的差别,就在这块的调用!!!! | 984 | // 委托( delegate)和转派(transfer)的差别,就在这块的调用!!!! |
| 965 | taskService.setAssignee(taskId, reqVO.getAssigneeUserId().toString()); | 985 | taskService.setAssignee(taskId, reqVO.getAssigneeUserId().toString()); |
| @@ -1367,7 +1387,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -1367,7 +1387,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 1367 | PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE, String.class)); | 1387 | PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE, String.class)); |
| 1368 | if (userTaskElement.getId().equals(START_USER_NODE_ID) | 1388 | if (userTaskElement.getId().equals(START_USER_NODE_ID) |
| 1369 | && (skipStartUserNodeFlag == null // 目的:一般是“主流程”,发起人节点,自动通过审核 | 1389 | && (skipStartUserNodeFlag == null // 目的:一般是“主流程”,发起人节点,自动通过审核 |
| 1370 | - || Boolean.TRUE.equals(skipStartUserNodeFlag)) // 目的:一般是“子流程”,发起人节点,按配置自动通过审核 | 1390 | + || BooleanUtil.isTrue(skipStartUserNodeFlag)) // 目的:一般是“子流程”,发起人节点,按配置自动通过审核 |
| 1371 | && ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) { | 1391 | && ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) { |
| 1372 | getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) | 1392 | getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) |
| 1373 | .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason())); | 1393 | .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason())); |
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java
| @@ -85,10 +85,15 @@ public class BpmCallActivityListener implements ExecutionListener { | @@ -85,10 +85,15 @@ public class BpmCallActivityListener implements ExecutionListener { | ||
| 85 | // 2.2 使用表单值,并兜底字符串转 Long 失败时使用主流程发起人 | 85 | // 2.2 使用表单值,并兜底字符串转 Long 失败时使用主流程发起人 |
| 86 | try { | 86 | try { |
| 87 | FlowableUtils.setAuthenticatedUserId(Long.parseLong(formFieldValue)); | 87 | FlowableUtils.setAuthenticatedUserId(Long.parseLong(formFieldValue)); |
| 88 | - } catch (Exception e) { | ||
| 89 | - log.error("[notify][监听器:{},子流程监听器设置流程的发起人字符串转 Long 失败,字符串:{}]", | ||
| 90 | - DELEGATE_EXPRESSION, formFieldValue); | ||
| 91 | - FlowableUtils.setAuthenticatedUserId(Long.parseLong(processInstance.getStartUserId())); | 88 | + } catch (NumberFormatException ex) { |
| 89 | + try { | ||
| 90 | + List<Long> formFieldValues = JsonUtils.parseArray(formFieldValue, Long.class); | ||
| 91 | + FlowableUtils.setAuthenticatedUserId(formFieldValues.get(0)); | ||
| 92 | + } catch (Exception e) { | ||
| 93 | + log.error("[notify][监听器:{},子流程监听器设置流程的发起人字符串转 Long 失败,字符串:{}]", | ||
| 94 | + DELEGATE_EXPRESSION, formFieldValue); | ||
| 95 | + FlowableUtils.setAuthenticatedUserId(Long.parseLong(processInstance.getStartUserId())); | ||
| 96 | + } | ||
| 92 | } | 97 | } |
| 93 | } | 98 | } |
| 94 | } | 99 | } |
yudao-module-bpm/src/test/java/cn/iocoder/yudao/module/bpm/service/category/BpmCategoryServiceImplTest.java
| @@ -8,9 +8,11 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCa | @@ -8,9 +8,11 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.category.BpmCa | ||
| 8 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; | 8 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; |
| 9 | import cn.iocoder.yudao.module.bpm.dal.mysql.category.BpmCategoryMapper; | 9 | import cn.iocoder.yudao.module.bpm.dal.mysql.category.BpmCategoryMapper; |
| 10 | import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryServiceImpl; | 10 | import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryServiceImpl; |
| 11 | +import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService; | ||
| 11 | import jakarta.annotation.Resource; | 12 | import jakarta.annotation.Resource; |
| 12 | import org.junit.jupiter.api.Test; | 13 | import org.junit.jupiter.api.Test; |
| 13 | import org.springframework.context.annotation.Import; | 14 | import org.springframework.context.annotation.Import; |
| 15 | +import org.springframework.test.context.bean.override.mockito.MockitoBean; | ||
| 14 | 16 | ||
| 15 | import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; | 17 | import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; |
| 16 | import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; | 18 | import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; |
| @@ -32,6 +34,9 @@ public class BpmCategoryServiceImplTest extends BaseDbUnitTest { | @@ -32,6 +34,9 @@ public class BpmCategoryServiceImplTest extends BaseDbUnitTest { | ||
| 32 | @Resource | 34 | @Resource |
| 33 | private BpmCategoryServiceImpl categoryService; | 35 | private BpmCategoryServiceImpl categoryService; |
| 34 | 36 | ||
| 37 | + @MockitoBean | ||
| 38 | + private BpmModelService modelService; | ||
| 39 | + | ||
| 35 | @Resource | 40 | @Resource |
| 36 | private BpmCategoryMapper categoryMapper; | 41 | private BpmCategoryMapper categoryMapper; |
| 37 | 42 |
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/vo/business/CrmBusinessRespVO.java
| 1 | package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business; | 1 | package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business; |
| 2 | 2 | ||
| 3 | -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; | ||
| 4 | -import com.alibaba.excel.annotation.ExcelProperty; | 3 | +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; |
| 4 | +import cn.idev.excel.annotation.ExcelProperty; | ||
| 5 | import io.swagger.v3.oas.annotations.media.Schema; | 5 | import io.swagger.v3.oas.annotations.media.Schema; |
| 6 | import lombok.AllArgsConstructor; | 6 | import lombok.AllArgsConstructor; |
| 7 | import lombok.Data; | 7 | import lombok.Data; |
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueRespVO.java
| @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.crm.controller.admin.clue.vo; | @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.crm.controller.admin.clue.vo; | ||
| 3 | import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; | 3 | import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; |
| 4 | import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; | 4 | import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; |
| 5 | import cn.iocoder.yudao.module.infra.enums.DictTypeConstants; | 5 | import cn.iocoder.yudao.module.infra.enums.DictTypeConstants; |
| 6 | -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; | ||
| 7 | -import com.alibaba.excel.annotation.ExcelProperty; | 6 | +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; |
| 7 | +import cn.idev.excel.annotation.ExcelProperty; | ||
| 8 | import io.swagger.v3.oas.annotations.media.Schema; | 8 | import io.swagger.v3.oas.annotations.media.Schema; |
| 9 | import lombok.Data; | 9 | import lombok.Data; |
| 10 | import lombok.ToString; | 10 | import lombok.ToString; |
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/vo/CrmContactRespVO.java
| @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.crm.controller.admin.contact.vo; | @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.crm.controller.admin.contact.vo; | ||
| 3 | import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; | 3 | import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; |
| 4 | import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; | 4 | import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; |
| 5 | import cn.iocoder.yudao.module.infra.enums.DictTypeConstants; | 5 | import cn.iocoder.yudao.module.infra.enums.DictTypeConstants; |
| 6 | -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; | ||
| 7 | -import com.alibaba.excel.annotation.ExcelProperty; | 6 | +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; |
| 7 | +import cn.idev.excel.annotation.ExcelProperty; | ||
| 8 | import io.swagger.v3.oas.annotations.media.Schema; | 8 | import io.swagger.v3.oas.annotations.media.Schema; |
| 9 | import lombok.Data; | 9 | import lombok.Data; |
| 10 | import lombok.ToString; | 10 | import lombok.ToString; |
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractRespVO.java
| 1 | package cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract; | 1 | package cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract; |
| 2 | 2 | ||
| 3 | -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; | ||
| 4 | -import com.alibaba.excel.annotation.ExcelProperty; | 3 | +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; |
| 4 | +import cn.idev.excel.annotation.ExcelProperty; | ||
| 5 | import io.swagger.v3.oas.annotations.media.Schema; | 5 | import io.swagger.v3.oas.annotations.media.Schema; |
| 6 | import lombok.AllArgsConstructor; | 6 | import lombok.AllArgsConstructor; |
| 7 | import lombok.Data; | 7 | import lombok.Data; |
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportExcelVO.java
| @@ -5,7 +5,7 @@ import cn.iocoder.yudao.framework.excel.core.annotations.ExcelColumnSelect; | @@ -5,7 +5,7 @@ import cn.iocoder.yudao.framework.excel.core.annotations.ExcelColumnSelect; | ||
| 5 | import cn.iocoder.yudao.framework.excel.core.convert.AreaConvert; | 5 | import cn.iocoder.yudao.framework.excel.core.convert.AreaConvert; |
| 6 | import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; | 6 | import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; |
| 7 | import cn.iocoder.yudao.module.crm.framework.excel.core.AreaExcelColumnSelectFunction; | 7 | import cn.iocoder.yudao.module.crm.framework.excel.core.AreaExcelColumnSelectFunction; |
| 8 | -import com.alibaba.excel.annotation.ExcelProperty; | 8 | +import cn.idev.excel.annotation.ExcelProperty; |
| 9 | import lombok.AllArgsConstructor; | 9 | import lombok.AllArgsConstructor; |
| 10 | import lombok.Builder; | 10 | import lombok.Builder; |
| 11 | import lombok.Data; | 11 | import lombok.Data; |
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerRespVO.java
| @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer; | @@ -3,8 +3,8 @@ package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer; | ||
| 3 | import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; | 3 | import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; |
| 4 | import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; | 4 | import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; |
| 5 | import cn.iocoder.yudao.module.infra.enums.DictTypeConstants; | 5 | import cn.iocoder.yudao.module.infra.enums.DictTypeConstants; |
| 6 | -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; | ||
| 7 | -import com.alibaba.excel.annotation.ExcelProperty; | 6 | +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; |
| 7 | +import cn.idev.excel.annotation.ExcelProperty; | ||
| 8 | import io.swagger.v3.oas.annotations.media.Schema; | 8 | import io.swagger.v3.oas.annotations.media.Schema; |
| 9 | import lombok.Data; | 9 | import lombok.Data; |
| 10 | 10 |
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/vo/CrmFollowUpRecordRespVO.java
| @@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.crm.controller.admin.followup.vo; | @@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.crm.controller.admin.followup.vo; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; | 3 | import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; |
| 4 | import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessRespVO; | 4 | import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessRespVO; |
| 5 | -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; | ||
| 6 | -import com.alibaba.excel.annotation.ExcelProperty; | 5 | +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; |
| 6 | +import cn.idev.excel.annotation.ExcelProperty; | ||
| 7 | import io.swagger.v3.oas.annotations.media.Schema; | 7 | import io.swagger.v3.oas.annotations.media.Schema; |
| 8 | import lombok.Data; | 8 | import lombok.Data; |
| 9 | 9 | ||
| @@ -61,4 +61,4 @@ public class CrmFollowUpRecordRespVO { | @@ -61,4 +61,4 @@ public class CrmFollowUpRecordRespVO { | ||
| 61 | @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | 61 | @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) |
| 62 | private LocalDateTime createTime; | 62 | private LocalDateTime createTime; |
| 63 | 63 | ||
| 64 | -} | ||
| 65 | \ No newline at end of file | 64 | \ No newline at end of file |
| 65 | +} |
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogRespVO.java
| 1 | package cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo; | 1 | package cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo; |
| 2 | 2 | ||
| 3 | -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; | 3 | +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; |
| 4 | import io.swagger.v3.oas.annotations.media.Schema; | 4 | import io.swagger.v3.oas.annotations.media.Schema; |
| 5 | import lombok.Data; | 5 | import lombok.Data; |
| 6 | 6 |
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/CrmProductController.java
| @@ -14,6 +14,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; | @@ -14,6 +14,7 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO; | ||
| 14 | import cn.iocoder.yudao.module.crm.enums.product.CrmProductStatusEnum; | 14 | import cn.iocoder.yudao.module.crm.enums.product.CrmProductStatusEnum; |
| 15 | import cn.iocoder.yudao.module.crm.service.product.CrmProductService; | 15 | import cn.iocoder.yudao.module.crm.service.product.CrmProductService; |
| 16 | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | 16 | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| 17 | +import com.fhs.core.trans.anno.TransMethodResult; | ||
| 17 | import io.swagger.v3.oas.annotations.Operation; | 18 | import io.swagger.v3.oas.annotations.Operation; |
| 18 | import io.swagger.v3.oas.annotations.Parameter; | 19 | import io.swagger.v3.oas.annotations.Parameter; |
| 19 | import io.swagger.v3.oas.annotations.tags.Tag; | 20 | import io.swagger.v3.oas.annotations.tags.Tag; |
| @@ -70,6 +71,7 @@ public class CrmProductController { | @@ -70,6 +71,7 @@ public class CrmProductController { | ||
| 70 | @Operation(summary = "获得产品") | 71 | @Operation(summary = "获得产品") |
| 71 | @Parameter(name = "id", description = "编号", required = true, example = "1024") | 72 | @Parameter(name = "id", description = "编号", required = true, example = "1024") |
| 72 | @PreAuthorize("@ss.hasPermission('crm:product:query')") | 73 | @PreAuthorize("@ss.hasPermission('crm:product:query')") |
| 74 | + @TransMethodResult | ||
| 73 | public CommonResult<CrmProductRespVO> getProduct(@RequestParam("id") Long id) { | 75 | public CommonResult<CrmProductRespVO> getProduct(@RequestParam("id") Long id) { |
| 74 | CrmProductDO product = productService.getProduct(id); | 76 | CrmProductDO product = productService.getProduct(id); |
| 75 | return success(BeanUtils.toBean(product, CrmProductRespVO.class)); | 77 | return success(BeanUtils.toBean(product, CrmProductRespVO.class)); |
| @@ -86,6 +88,7 @@ public class CrmProductController { | @@ -86,6 +88,7 @@ public class CrmProductController { | ||
| 86 | @GetMapping("/page") | 88 | @GetMapping("/page") |
| 87 | @Operation(summary = "获得产品分页") | 89 | @Operation(summary = "获得产品分页") |
| 88 | @PreAuthorize("@ss.hasPermission('crm:product:query')") | 90 | @PreAuthorize("@ss.hasPermission('crm:product:query')") |
| 91 | + @TransMethodResult | ||
| 89 | public CommonResult<PageResult<CrmProductRespVO>> getProductPage(@Valid CrmProductPageReqVO pageVO) { | 92 | public CommonResult<PageResult<CrmProductRespVO>> getProductPage(@Valid CrmProductPageReqVO pageVO) { |
| 90 | PageResult<CrmProductDO> pageResult = productService.getProductPage(pageVO); | 93 | PageResult<CrmProductDO> pageResult = productService.getProductPage(pageVO); |
| 91 | return success(BeanUtils.toBean(pageResult, CrmProductRespVO.class)); | 94 | return success(BeanUtils.toBean(pageResult, CrmProductRespVO.class)); |
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/category/CrmProductCategoryListReqVO.java
| 1 | package cn.iocoder.yudao.module.crm.controller.admin.product.vo.category; | 1 | package cn.iocoder.yudao.module.crm.controller.admin.product.vo.category; |
| 2 | 2 | ||
| 3 | -import com.alibaba.excel.annotation.ExcelProperty; | 3 | +import cn.idev.excel.annotation.ExcelProperty; |
| 4 | import io.swagger.v3.oas.annotations.media.Schema; | 4 | import io.swagger.v3.oas.annotations.media.Schema; |
| 5 | import lombok.Data; | 5 | import lombok.Data; |
| 6 | 6 |
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/product/vo/product/CrmProductRespVO.java
| @@ -4,8 +4,8 @@ import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; | @@ -4,8 +4,8 @@ import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; | ||
| 4 | import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; | 4 | import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; |
| 5 | import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO; | 5 | import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductCategoryDO; |
| 6 | import cn.iocoder.yudao.module.crm.enums.DictTypeConstants; | 6 | import cn.iocoder.yudao.module.crm.enums.DictTypeConstants; |
| 7 | -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; | ||
| 8 | -import com.alibaba.excel.annotation.ExcelProperty; | 7 | +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; |
| 8 | +import cn.idev.excel.annotation.ExcelProperty; | ||
| 9 | import com.fhs.core.trans.anno.Trans; | 9 | import com.fhs.core.trans.anno.Trans; |
| 10 | import com.fhs.core.trans.constant.TransType; | 10 | import com.fhs.core.trans.constant.TransType; |
| 11 | import com.fhs.core.trans.vo.VO; | 11 | import com.fhs.core.trans.vo.VO; |
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/plan/CrmReceivablePlanRespVO.java
| 1 | package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan; | 1 | package cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.plan; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO; | 3 | import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO; |
| 4 | -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; | ||
| 5 | -import com.alibaba.excel.annotation.ExcelProperty; | 4 | +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; |
| 5 | +import cn.idev.excel.annotation.ExcelProperty; | ||
| 6 | import io.swagger.v3.oas.annotations.media.Schema; | 6 | import io.swagger.v3.oas.annotations.media.Schema; |
| 7 | import lombok.Data; | 7 | import lombok.Data; |
| 8 | 8 |
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/receivable/vo/receivable/CrmReceivableRespVO.java
| @@ -4,8 +4,8 @@ import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; | @@ -4,8 +4,8 @@ import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; | ||
| 4 | import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; | 4 | import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; |
| 5 | import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractRespVO; | 5 | import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractRespVO; |
| 6 | import cn.iocoder.yudao.module.crm.enums.DictTypeConstants; | 6 | import cn.iocoder.yudao.module.crm.enums.DictTypeConstants; |
| 7 | -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; | ||
| 8 | -import com.alibaba.excel.annotation.ExcelProperty; | 7 | +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; |
| 8 | +import cn.idev.excel.annotation.ExcelProperty; | ||
| 9 | import io.swagger.v3.oas.annotations.media.Schema; | 9 | import io.swagger.v3.oas.annotations.media.Schema; |
| 10 | import lombok.Data; | 10 | import lombok.Data; |
| 11 | 11 |
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java
| @@ -142,6 +142,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { | @@ -142,6 +142,7 @@ public class CrmBusinessServiceImpl implements CrmBusinessService { | ||
| 142 | updateBusinessProduct(updateObj.getId(), businessProducts); | 142 | updateBusinessProduct(updateObj.getId(), businessProducts); |
| 143 | 143 | ||
| 144 | // 3. 记录操作日志上下文 | 144 | // 3. 记录操作日志上下文 |
| 145 | + updateReqVO.setOwnerUserId(oldBusiness.getOwnerUserId()); // 避免操作日志出现“删除负责人”的情况 | ||
| 145 | LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldBusiness, CrmBusinessSaveReqVO.class)); | 146 | LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldBusiness, CrmBusinessSaveReqVO.class)); |
| 146 | LogRecordContext.putVariable("businessName", oldBusiness.getName()); | 147 | LogRecordContext.putVariable("businessName", oldBusiness.getName()); |
| 147 | } | 148 | } |
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java
| @@ -92,19 +92,20 @@ public class CrmClueServiceImpl implements CrmClueService { | @@ -92,19 +92,20 @@ public class CrmClueServiceImpl implements CrmClueService { | ||
| 92 | @Transactional(rollbackFor = Exception.class) | 92 | @Transactional(rollbackFor = Exception.class) |
| 93 | @LogRecord(type = CRM_CLUE_TYPE, subType = CRM_CLUE_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", | 93 | @LogRecord(type = CRM_CLUE_TYPE, subType = CRM_CLUE_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", |
| 94 | success = CRM_CLUE_UPDATE_SUCCESS) | 94 | success = CRM_CLUE_UPDATE_SUCCESS) |
| 95 | - @CrmPermission(bizType = CrmBizTypeEnum.CRM_CLUE, bizId = "#updateReq.id", level = CrmPermissionLevelEnum.OWNER) | ||
| 96 | - public void updateClue(CrmClueSaveReqVO updateReq) { | ||
| 97 | - Assert.notNull(updateReq.getId(), "线索编号不能为空"); | 95 | + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CLUE, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.OWNER) |
| 96 | + public void updateClue(CrmClueSaveReqVO updateReqVO) { | ||
| 97 | + Assert.notNull(updateReqVO.getId(), "线索编号不能为空"); | ||
| 98 | // 1.1 校验线索是否存在 | 98 | // 1.1 校验线索是否存在 |
| 99 | - CrmClueDO oldClue = validateClueExists(updateReq.getId()); | 99 | + CrmClueDO oldClue = validateClueExists(updateReqVO.getId()); |
| 100 | // 1.2 校验关联数据 | 100 | // 1.2 校验关联数据 |
| 101 | - validateRelationDataExists(updateReq); | 101 | + validateRelationDataExists(updateReqVO); |
| 102 | 102 | ||
| 103 | // 2. 更新线索 | 103 | // 2. 更新线索 |
| 104 | - CrmClueDO updateObj = BeanUtils.toBean(updateReq, CrmClueDO.class); | 104 | + CrmClueDO updateObj = BeanUtils.toBean(updateReqVO, CrmClueDO.class); |
| 105 | clueMapper.updateById(updateObj); | 105 | clueMapper.updateById(updateObj); |
| 106 | 106 | ||
| 107 | // 3. 记录操作日志上下文 | 107 | // 3. 记录操作日志上下文 |
| 108 | + updateReqVO.setOwnerUserId(oldClue.getOwnerUserId()); // 避免操作日志出现“删除负责人”的情况 | ||
| 108 | LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldClue, CrmCustomerSaveReqVO.class)); | 109 | LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldClue, CrmCustomerSaveReqVO.class)); |
| 109 | LogRecordContext.putVariable("clueName", oldClue.getName()); | 110 | LogRecordContext.putVariable("clueName", oldClue.getName()); |
| 110 | } | 111 | } |
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java
| @@ -114,6 +114,7 @@ public class CrmContactServiceImpl implements CrmContactService { | @@ -114,6 +114,7 @@ public class CrmContactServiceImpl implements CrmContactService { | ||
| 114 | contactMapper.updateById(updateObj); | 114 | contactMapper.updateById(updateObj); |
| 115 | 115 | ||
| 116 | // 3. 记录操作日志 | 116 | // 3. 记录操作日志 |
| 117 | + updateReqVO.setOwnerUserId(oldContact.getOwnerUserId()); // 避免操作日志出现“删除负责人”的情况 | ||
| 117 | LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldContact, CrmContactSaveReqVO.class)); | 118 | LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldContact, CrmContactSaveReqVO.class)); |
| 118 | LogRecordContext.putVariable("contactName", oldContact.getName()); | 119 | LogRecordContext.putVariable("contactName", oldContact.getName()); |
| 119 | } | 120 | } |
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
| @@ -140,9 +140,9 @@ public class CrmContractServiceImpl implements CrmContractService { | @@ -140,9 +140,9 @@ public class CrmContractServiceImpl implements CrmContractService { | ||
| 140 | Assert.notNull(updateReqVO.getId(), "合同编号不能为空"); | 140 | Assert.notNull(updateReqVO.getId(), "合同编号不能为空"); |
| 141 | updateReqVO.setOwnerUserId(null); // 不允许更新的字段 | 141 | updateReqVO.setOwnerUserId(null); // 不允许更新的字段 |
| 142 | // 1.1 校验存在 | 142 | // 1.1 校验存在 |
| 143 | - CrmContractDO contract = validateContractExists(updateReqVO.getId()); | 143 | + CrmContractDO oldContract = validateContractExists(updateReqVO.getId()); |
| 144 | // 1.2 只有草稿、审批中,可以编辑; | 144 | // 1.2 只有草稿、审批中,可以编辑; |
| 145 | - if (!ObjectUtils.equalsAny(contract.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus(), | 145 | + if (!ObjectUtils.equalsAny(oldContract.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus(), |
| 146 | CrmAuditStatusEnum.PROCESS.getStatus())) { | 146 | CrmAuditStatusEnum.PROCESS.getStatus())) { |
| 147 | throw exception(CONTRACT_UPDATE_FAIL_NOT_DRAFT); | 147 | throw exception(CONTRACT_UPDATE_FAIL_NOT_DRAFT); |
| 148 | } | 148 | } |
| @@ -159,8 +159,9 @@ public class CrmContractServiceImpl implements CrmContractService { | @@ -159,8 +159,9 @@ public class CrmContractServiceImpl implements CrmContractService { | ||
| 159 | updateContractProduct(updateReqVO.getId(), contractProducts); | 159 | updateContractProduct(updateReqVO.getId(), contractProducts); |
| 160 | 160 | ||
| 161 | // 3. 记录操作日志上下文 | 161 | // 3. 记录操作日志上下文 |
| 162 | - LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(contract, CrmContractSaveReqVO.class)); | ||
| 163 | - LogRecordContext.putVariable("contractName", contract.getName()); | 162 | + updateReqVO.setOwnerUserId(oldContract.getOwnerUserId()); // 避免操作日志出现“删除负责人”的情况 |
| 163 | + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldContract, CrmContractSaveReqVO.class)); | ||
| 164 | + LogRecordContext.putVariable("contractName", oldContract.getName()); | ||
| 164 | } | 165 | } |
| 165 | 166 | ||
| 166 | private void updateContractProduct(Long id, List<CrmContractProductDO> newList) { | 167 | private void updateContractProduct(Long id, List<CrmContractProductDO> newList) { |
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
| @@ -137,6 +137,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { | @@ -137,6 +137,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { | ||
| 137 | customerMapper.updateById(updateObj); | 137 | customerMapper.updateById(updateObj); |
| 138 | 138 | ||
| 139 | // 3. 记录操作日志上下文 | 139 | // 3. 记录操作日志上下文 |
| 140 | + updateReqVO.setOwnerUserId(oldCustomer.getOwnerUserId()); // 避免操作日志出现“删除负责人”的情况 | ||
| 140 | LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldCustomer, CrmCustomerSaveReqVO.class)); | 141 | LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldCustomer, CrmCustomerSaveReqVO.class)); |
| 141 | LogRecordContext.putVariable("customerName", oldCustomer.getName()); | 142 | LogRecordContext.putVariable("customerName", oldCustomer.getName()); |
| 142 | } | 143 | } |
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/permission/CrmPermissionServiceImpl.java
| @@ -210,12 +210,12 @@ public class CrmPermissionServiceImpl implements CrmPermissionService { | @@ -210,12 +210,12 @@ public class CrmPermissionServiceImpl implements CrmPermissionService { | ||
| 210 | CrmPermissionDO oldPermission = permissionMapper.selectByBizTypeAndBizIdByUserId( | 210 | CrmPermissionDO oldPermission = permissionMapper.selectByBizTypeAndBizIdByUserId( |
| 211 | transferReqBO.getBizType(), transferReqBO.getBizId(), transferReqBO.getUserId()); | 211 | transferReqBO.getBizType(), transferReqBO.getBizId(), transferReqBO.getUserId()); |
| 212 | String bizTypeName = CrmBizTypeEnum.getNameByType(transferReqBO.getBizType()); | 212 | String bizTypeName = CrmBizTypeEnum.getNameByType(transferReqBO.getBizType()); |
| 213 | - if (oldPermission == null // 不是拥有者,并且不是超管 | ||
| 214 | - || (!isOwner(oldPermission.getLevel()) && !CrmPermissionUtils.isCrmAdmin())) { | 213 | + if ((oldPermission == null || !isOwner(oldPermission.getLevel())) |
| 214 | + && !CrmPermissionUtils.isCrmAdmin()) { // 并且不是超管 | ||
| 215 | throw exception(CRM_PERMISSION_DENIED, bizTypeName); | 215 | throw exception(CRM_PERMISSION_DENIED, bizTypeName); |
| 216 | } | 216 | } |
| 217 | // 1.1 校验转移对象是否已经是该负责人 | 217 | // 1.1 校验转移对象是否已经是该负责人 |
| 218 | - if (ObjUtil.equal(transferReqBO.getNewOwnerUserId(), oldPermission.getUserId())) { | 218 | + if (oldPermission != null && ObjUtil.equal(transferReqBO.getNewOwnerUserId(), oldPermission.getUserId())) { |
| 219 | throw exception(CRM_PERMISSION_MODEL_TRANSFER_FAIL_OWNER_USER_EXISTS, bizTypeName); | 219 | throw exception(CRM_PERMISSION_MODEL_TRANSFER_FAIL_OWNER_USER_EXISTS, bizTypeName); |
| 220 | } | 220 | } |
| 221 | // 1.2 校验新负责人是否存在 | 221 | // 1.2 校验新负责人是否存在 |
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivablePlanServiceImpl.java
| @@ -104,6 +104,7 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { | @@ -104,6 +104,7 @@ public class CrmReceivablePlanServiceImpl implements CrmReceivablePlanService { | ||
| 104 | receivablePlanMapper.updateById(updateObj); | 104 | receivablePlanMapper.updateById(updateObj); |
| 105 | 105 | ||
| 106 | // 3. 记录操作日志上下文 | 106 | // 3. 记录操作日志上下文 |
| 107 | + updateReqVO.setOwnerUserId(oldReceivablePlan.getOwnerUserId()); // 避免操作日志出现“删除负责人”的情况 | ||
| 107 | LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldReceivablePlan, CrmReceivablePlanSaveReqVO.class)); | 108 | LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldReceivablePlan, CrmReceivablePlanSaveReqVO.class)); |
| 108 | LogRecordContext.putVariable("receivablePlan", oldReceivablePlan); | 109 | LogRecordContext.putVariable("receivablePlan", oldReceivablePlan); |
| 109 | } | 110 | } |
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/receivable/CrmReceivableServiceImpl.java
| @@ -162,14 +162,14 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { | @@ -162,14 +162,14 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { | ||
| 162 | Assert.notNull(updateReqVO.getId(), "回款编号不能为空"); | 162 | Assert.notNull(updateReqVO.getId(), "回款编号不能为空"); |
| 163 | updateReqVO.setOwnerUserId(null).setCustomerId(null).setContractId(null).setPlanId(null); // 不允许修改的字段 | 163 | updateReqVO.setOwnerUserId(null).setCustomerId(null).setContractId(null).setPlanId(null); // 不允许修改的字段 |
| 164 | // 1.1 校验存在 | 164 | // 1.1 校验存在 |
| 165 | - CrmReceivableDO receivable = validateReceivableExists(updateReqVO.getId()); | ||
| 166 | - updateReqVO.setOwnerUserId(receivable.getOwnerUserId()).setCustomerId(receivable.getCustomerId()) | ||
| 167 | - .setContractId(receivable.getContractId()).setPlanId(receivable.getPlanId()); // 设置已存在的值 | 165 | + CrmReceivableDO oldReceivable = validateReceivableExists(updateReqVO.getId()); |
| 166 | + updateReqVO.setOwnerUserId(oldReceivable.getOwnerUserId()).setCustomerId(oldReceivable.getCustomerId()) | ||
| 167 | + .setContractId(oldReceivable.getContractId()).setPlanId(oldReceivable.getPlanId()); // 设置已存在的值 | ||
| 168 | // 1.2 校验可回款金额超过上限 | 168 | // 1.2 校验可回款金额超过上限 |
| 169 | validateReceivablePriceExceedsLimit(updateReqVO); | 169 | validateReceivablePriceExceedsLimit(updateReqVO); |
| 170 | 170 | ||
| 171 | // 1.3 只有草稿、审批中,可以编辑; | 171 | // 1.3 只有草稿、审批中,可以编辑; |
| 172 | - if (!ObjectUtils.equalsAny(receivable.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus(), | 172 | + if (!ObjectUtils.equalsAny(oldReceivable.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus(), |
| 173 | CrmAuditStatusEnum.PROCESS.getStatus())) { | 173 | CrmAuditStatusEnum.PROCESS.getStatus())) { |
| 174 | throw exception(RECEIVABLE_UPDATE_FAIL_EDITING_PROHIBITED); | 174 | throw exception(RECEIVABLE_UPDATE_FAIL_EDITING_PROHIBITED); |
| 175 | } | 175 | } |
| @@ -179,9 +179,10 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { | @@ -179,9 +179,10 @@ public class CrmReceivableServiceImpl implements CrmReceivableService { | ||
| 179 | receivableMapper.updateById(updateObj); | 179 | receivableMapper.updateById(updateObj); |
| 180 | 180 | ||
| 181 | // 3. 记录操作日志上下文 | 181 | // 3. 记录操作日志上下文 |
| 182 | - LogRecordContext.putVariable("receivable", receivable); | ||
| 183 | - LogRecordContext.putVariable("period", getReceivablePeriod(receivable.getPlanId())); | ||
| 184 | - LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(receivable, CrmReceivableSaveReqVO.class)); | 182 | + updateReqVO.setOwnerUserId(oldReceivable.getOwnerUserId()); // 避免操作日志出现“删除负责人”的情况 |
| 183 | + LogRecordContext.putVariable("oldReceivable", oldReceivable); | ||
| 184 | + LogRecordContext.putVariable("period", getReceivablePeriod(oldReceivable.getPlanId())); | ||
| 185 | + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldReceivable, CrmReceivableSaveReqVO.class)); | ||
| 185 | } | 186 | } |
| 186 | 187 | ||
| 187 | private Integer getReceivablePeriod(Long planId) { | 188 | private Integer getReceivablePeriod(Long planId) { |
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/util/CrmPermissionUtils.java
| @@ -2,11 +2,11 @@ package cn.iocoder.yudao.module.crm.util; | @@ -2,11 +2,11 @@ package cn.iocoder.yudao.module.crm.util; | ||
| 2 | 2 | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | import cn.hutool.extra.spring.SpringUtil; | 4 | import cn.hutool.extra.spring.SpringUtil; |
| 5 | +import cn.iocoder.yudao.framework.common.biz.system.permission.PermissionCommonApi; | ||
| 5 | import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; | 6 | import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; |
| 6 | import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; | 7 | import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; |
| 7 | import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum; | 8 | import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum; |
| 8 | import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; | 9 | import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; |
| 9 | -import cn.iocoder.yudao.module.system.api.permission.PermissionApi; | ||
| 10 | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | 10 | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| 11 | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; | 11 | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; |
| 12 | import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum; | 12 | import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum; |
| @@ -32,7 +32,7 @@ public class CrmPermissionUtils { | @@ -32,7 +32,7 @@ public class CrmPermissionUtils { | ||
| 32 | * @return 是/否 | 32 | * @return 是/否 |
| 33 | */ | 33 | */ |
| 34 | public static boolean isCrmAdmin() { | 34 | public static boolean isCrmAdmin() { |
| 35 | - PermissionApi permissionApi = SpringUtil.getBean(PermissionApi.class); | 35 | + PermissionCommonApi permissionApi = SpringUtil.getBean(PermissionCommonApi.class); |
| 36 | return permissionApi.hasAnyRoles(getLoginUserId(), RoleCodeEnum.CRM_ADMIN.getCode()); | 36 | return permissionApi.hasAnyRoles(getLoginUserId(), RoleCodeEnum.CRM_ADMIN.getCode()); |
| 37 | } | 37 | } |
| 38 | 38 |