From 4cb9af22a2d78585c7dcc50a6613be64a3d82cc7 Mon Sep 17 00:00:00 2001 From: egd <7769519+egberd@user.noreply.gitee.com> Date: Tue, 9 Sep 2025 09:39:48 +0800 Subject: [PATCH 1/4] =?UTF-8?q?fix(web):=20=E7=A1=AE=E4=BF=9D=E5=9C=A8=20B?= =?UTF-8?q?ean=20=E5=88=9B=E5=BB=BA=E5=89=8D=E6=98=A0=E5=B0=84=E5=BA=94?= =?UTF-8?q?=E7=94=A8=E8=AF=B7=E6=B1=82=E5=89=8D=E7=BC=80=20=E5=9C=BA?= =?UTF-8?q?=E6=99=AF=EF=BC=9A=20=E5=BD=93=20app=20=E5=92=8C=20admin=20?= =?UTF-8?q?=E4=B8=8B=E7=9A=84=E6=8E=A5=E5=8F=A3=E5=9C=B0=E5=9D=80=E4=B8=80?= =?UTF-8?q?=E8=87=B4=E6=97=B6=EF=BC=88=E6=AF=94=E5=A6=82=EF=BC=9A/system/u?= =?UTF-8?q?ser/get=EF=BC=89=EF=BC=8C=E9=9C=80=E8=A6=81=E4=BE=9D=E8=B5=96?= =?UTF-8?q?=E5=89=8D=E7=BC=80=EF=BC=88admin-api=20=EF=BD=9C=20app-api?= =?UTF-8?q?=EF=BC=89=E6=9D=A5=E5=8C=BA=E5=88=86=EF=BC=8C=E9=98=B2=E6=AD=A2?= =?UTF-8?q?=20URI=20=E5=86=B2=E7=AA=81=E3=80=82=20=E9=97=AE=E9=A2=98?= =?UTF-8?q?=EF=BC=9A=20-=20=E5=BD=93=20xss.enable=3Dtrue=20=E6=97=B6?= =?UTF-8?q?=EF=BC=8C=E4=BC=9A=E8=A7=A6=E5=8F=91=20TechXssAutoConfiguration?= =?UTF-8?q?=20=E4=B8=AD=20xssJacksonCustomizer=20=E7=9A=84=E6=9E=84?= =?UTF-8?q?=E5=BB=BA=EF=BC=8C=E9=97=B4=E6=8E=A5=E8=A7=A6=E5=8F=91=20PathMa?= =?UTF-8?q?tcher=20=E6=B3=A8=E5=85=A5=EF=BC=8C=E8=B0=83=E7=94=A8=20Delegat?= =?UTF-8?q?ingWebMvcConfiguration.configurePathMatch=E3=80=82=20-=20?= =?UTF-8?q?=E6=AD=A4=E6=97=B6=20RequestMappingHandlerMapping=20=E7=9A=84?= =?UTF-8?q?=20mapping=20=E8=BF=98=E6=9C=AA=E5=8A=A0=E4=B8=8A=E5=89=8D?= =?UTF-8?q?=E7=BC=80=E3=80=82=20-=20=E5=BD=93=20api-encrypt.enable=3Dtrue?= =?UTF-8?q?=20=E6=97=B6=EF=BC=8C=E6=8F=90=E5=89=8D=E6=B3=A8=E5=85=A5?= =?UTF-8?q?=E7=9A=84=20RequestMappingHandlerMapping=20=E6=B2=A1=E6=9C=89?= =?UTF-8?q?=E5=89=8D=E7=BC=80=EF=BC=8C=E5=AF=BC=E8=87=B4=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E5=9C=B0=E5=9D=80=E9=87=8D=E5=A4=8D=E6=8A=A5=E9=94=99=E3=80=82?= =?UTF-8?q?=20=E8=A7=A3=E5=86=B3=EF=BC=9A=20-=20=E4=B8=8D=E4=BE=9D?= =?UTF-8?q?=E8=B5=96=20DelegatingWebMvcConfiguration=20=E7=9A=84=E5=9B=9E?= =?UTF-8?q?=E8=B0=83=E9=A1=BA=E5=BA=8F=E3=80=82=20-=20=E7=A1=AE=E4=BF=9D?= =?UTF-8?q?=E5=8D=B3=E4=BD=BF=E5=85=B6=E4=BB=96=20Bean=20=E6=8F=90?= =?UTF-8?q?=E5=89=8D=E8=A7=A6=E5=8F=91=20Mapping=20=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E4=B9=9F=E8=83=BD=E6=AD=A3=E7=A1=AE=E5=8A=A0?= =?UTF-8?q?=E4=B8=8A=E5=89=8D=E7=BC=80=EF=BC=8C=E9=81=BF=E5=85=8D=20URI=20?= =?UTF-8?q?=E5=86=B2=E7=AA=81=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/config/YudaoWebAutoConfiguration.java | 53 ++++++++++++------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java index 5e033b124..aff4ff173 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java @@ -7,11 +7,13 @@ import cn.iocoder.yudao.framework.web.core.filter.DemoFilter; import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; import cn.iocoder.yudao.framework.web.core.handler.GlobalResponseBodyHandler; import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; +import jakarta.servlet.Filter; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration; +import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.boot.web.servlet.FilterRegistrationBean; @@ -23,40 +25,55 @@ import org.springframework.web.client.RestTemplate; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; -import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; -import jakarta.annotation.Resource; -import jakarta.servlet.Filter; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.function.Predicate; @AutoConfiguration @EnableConfigurationProperties(WebProperties.class) -public class YudaoWebAutoConfiguration implements WebMvcConfigurer { +public class YudaoWebAutoConfiguration { - @Resource - private WebProperties webProperties; /** * 应用名 */ @Value("${spring.application.name}") private String applicationName; - @Override - public void configurePathMatch(PathMatchConfigurer configurer) { - configurePathMatch(configurer, webProperties.getAdminApi()); - configurePathMatch(configurer, webProperties.getAppApi()); + @Bean + public WebMvcRegistrations webMvcRegistrations(WebProperties webProperties) { + return new WebMvcRegistrations() { + @Override + public RequestMappingHandlerMapping getRequestMappingHandlerMapping() { + var mapping = new RequestMappingHandlerMapping(); + mapping.setPathPrefixes(buildPrefixRules(webProperties)); // 实例化时就带上前缀 + return mapping; + } + }; + } + + /** + * 构建 prefix → 匹配条件 的映射 + */ + private Map>> buildPrefixRules(WebProperties webProperties) { + AntPathMatcher antPathMatcher = new AntPathMatcher("."); + Map>> rules = new LinkedHashMap<>(); + putRule(rules, webProperties.getAdminApi(), antPathMatcher); + putRule(rules, webProperties.getAppApi(), antPathMatcher); + return rules; } /** * 设置 API 前缀,仅仅匹配 controller 包下的 - * - * @param configurer 配置 - * @param api API 配置 */ - private void configurePathMatch(PathMatchConfigurer configurer, WebProperties.Api api) { - AntPathMatcher antPathMatcher = new AntPathMatcher("."); - configurer.addPathPrefix(api.getPrefix(), clazz -> clazz.isAnnotationPresent(RestController.class) - && antPathMatcher.match(api.getController(), clazz.getPackage().getName())); // 仅仅匹配 controller 包 + private void putRule(Map>> rules, WebProperties.Api api, AntPathMatcher matcher) { + if (api == null || api.getPrefix() == null) { + return; + } + rules.put(api.getPrefix(), // api前缀 + clazz -> clazz.isAnnotationPresent(RestController.class) + && matcher.match(api.getController(), clazz.getPackage().getName())); } @Bean From ec3a391981f9c42c22f41c938637518016f99638 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sat, 20 Sep 2025 21:50:04 +0800 Subject: [PATCH 2/4] =?UTF-8?q?fix=EF=BC=9A=E3=80=90framework=20=E6=A1=86?= =?UTF-8?q?=E6=9E=B6=E3=80=91=E7=A1=AE=E4=BF=9D=E5=9C=A8=20Bean=20?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E5=89=8D=E6=98=A0=E5=B0=84=E5=BA=94=E7=94=A8?= =?UTF-8?q?=E8=AF=B7=E6=B1=82=E5=89=8D=E7=BC=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/config/YudaoWebAutoConfiguration.java | 54 ++++++++++--------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java index aff4ff173..65fcd3428 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.framework.web.config; +import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.biz.infra.logger.ApiErrorLogCommonApi; import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum; import cn.iocoder.yudao.framework.web.core.filter.CacheRequestBodyFilter; @@ -7,6 +8,7 @@ import cn.iocoder.yudao.framework.web.core.filter.DemoFilter; import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; import cn.iocoder.yudao.framework.web.core.handler.GlobalResponseBodyHandler; import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; +import com.google.common.collect.Maps; import jakarta.servlet.Filter; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfiguration; @@ -27,7 +29,6 @@ import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; -import java.util.LinkedHashMap; import java.util.Map; import java.util.function.Predicate; @@ -44,38 +45,41 @@ public class YudaoWebAutoConfiguration { @Bean public WebMvcRegistrations webMvcRegistrations(WebProperties webProperties) { return new WebMvcRegistrations() { + @Override public RequestMappingHandlerMapping getRequestMappingHandlerMapping() { var mapping = new RequestMappingHandlerMapping(); - mapping.setPathPrefixes(buildPrefixRules(webProperties)); // 实例化时就带上前缀 + // 实例化时就带上前缀 + mapping.setPathPrefixes(buildPathPrefixes(webProperties)); return mapping; } + + /** + * 构建 prefix → 匹配条件的映射 + */ + private Map>> buildPathPrefixes(WebProperties webProperties) { + AntPathMatcher antPathMatcher = new AntPathMatcher("."); + Map>> pathPrefixes = Maps.newLinkedHashMapWithExpectedSize(2); + putPathPrefix(pathPrefixes, webProperties.getAdminApi(), antPathMatcher); + putPathPrefix(pathPrefixes, webProperties.getAppApi(), antPathMatcher); + return pathPrefixes; + } + + /** + * 设置 API 前缀,仅仅匹配 controller 包下的 + */ + private void putPathPrefix(Map>> pathPrefixes, WebProperties.Api api, AntPathMatcher matcher) { + if (api == null || StrUtil.isEmpty(api.getPrefix())) { + return; + } + pathPrefixes.put(api.getPrefix(), // api 前缀 + clazz -> clazz.isAnnotationPresent(RestController.class) + && matcher.match(api.getController(), clazz.getPackage().getName())); + } + }; } - /** - * 构建 prefix → 匹配条件 的映射 - */ - private Map>> buildPrefixRules(WebProperties webProperties) { - AntPathMatcher antPathMatcher = new AntPathMatcher("."); - Map>> rules = new LinkedHashMap<>(); - putRule(rules, webProperties.getAdminApi(), antPathMatcher); - putRule(rules, webProperties.getAppApi(), antPathMatcher); - return rules; - } - - /** - * 设置 API 前缀,仅仅匹配 controller 包下的 - */ - private void putRule(Map>> rules, WebProperties.Api api, AntPathMatcher matcher) { - if (api == null || api.getPrefix() == null) { - return; - } - rules.put(api.getPrefix(), // api前缀 - clazz -> clazz.isAnnotationPresent(RestController.class) - && matcher.match(api.getController(), clazz.getPackage().getName())); - } - @Bean public GlobalExceptionHandler globalExceptionHandler(ApiErrorLogCommonApi apiErrorLogApi) { return new GlobalExceptionHandler(applicationName, apiErrorLogApi); From f02c0047363d9702b8413d35dd51b8737ee84912 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 2 Oct 2025 17:51:49 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E3=80=90=E5=90=8C=E6=AD=A5=E3=80=91BOOT=20?= =?UTF-8?q?=E5=92=8C=20CLOUD=20=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/YudaoTenantAutoConfiguration.java | 16 +- .../config/YudaoMybatisAutoConfiguration.java | 5 +- .../config/YudaoSwaggerAutoConfiguration.java | 27 +- .../vo/model/BpmModelMetaInfoVO.java | 17 + .../task/BpmProcessInstanceController.java | 28 +- .../BpmProcessInstanceCancelReqVO.java | 3 +- .../BpmProcessInstanceCreateReqVO.java | 2 +- .../instance/BpmProcessPrintDataRespVO.java | 42 +++ .../admin/task/vo/task/BpmTaskPageReqVO.java | 6 + .../task/BpmProcessInstanceConvert.java | 54 +++- .../core/enums/BpmnVariableConstants.java | 13 +- .../task/BpmProcessInstanceServiceImpl.java | 48 ++- .../bpm/service/task/BpmTaskService.java | 8 + .../bpm/service/task/BpmTaskServiceImpl.java | 107 +++++-- .../crm/service/clue/CrmClueServiceImpl.java | 3 +- .../main/resources/codegen/vue/api/api.js.vm | 19 ++ .../main/resources/codegen/vue3/api/api.ts.vm | 83 ++++- .../vue3/views/components/list_sub_erp.vue.vm | 2 +- .../resources/codegen/vue3/views/index.vue.vm | 2 +- .../vue3_vben5_antd/general/api/api.ts.vm | 97 +++--- .../vue3_vben5_antd/general/views/form.vue.vm | 13 +- .../general/views/index.vue.vm | 44 ++- .../general/views/modules/form_sub_erp.vue.vm | 174 +++++------ .../views/modules/form_sub_normal.vue.vm | 48 +-- .../general/views/modules/list_sub_erp.vue.vm | 201 ++++++------ .../vue3_vben5_antd/schema/api/api.ts.vm | 168 +--------- .../vue3_vben5_antd/schema/views/data.ts.vm | 21 +- .../vue3_vben5_antd/schema/views/form.vue.vm | 69 +++-- .../vue3_vben5_antd/schema/views/index.vue.vm | 172 ++++++----- .../schema/views/modules/form_sub_erp.vue.vm | 11 +- .../views/modules/form_sub_normal.vue.vm | 24 +- .../schema/views/modules/list_sub_erp.vue.vm | 44 ++- .../vue3_vben5_ele/general/api/api.ts.vm | 167 ++++++++++ .../vue3_vben5_ele/general/views/form.vue.vm | 3 +- .../vue3_vben5_ele/general/views/index.vue.vm | 31 +- .../general/views/modules/form_sub_erp.vue.vm | 6 +- .../views/modules/form_sub_normal.vue.vm | 27 +- .../general/views/modules/list_sub_erp.vue.vm | 112 +++---- .../vue3_vben5_ele/schema/api/api.ts.vm | 191 ++++++++++++ .../vue3_vben5_ele/schema/views/data.ts.vm | 35 +-- .../vue3_vben5_ele/schema/views/form.vue.vm | 75 ++--- .../vue3_vben5_ele/schema/views/index.vue.vm | 184 +++++------ .../schema/views/modules/form_sub_erp.vue.vm | 19 +- .../views/modules/form_sub_normal.vue.vm | 16 +- .../schema/views/modules/list_sub_erp.vue.vm | 56 ++-- .../enums/IotDeviceMessageMethodEnum.java | 86 ------ .../iot/core/util/IotDeviceMessageUtils.java | 77 +++++ .../mqtt/router/IotMqttUpstreamHandler.java | 20 +- .../src/main/resources/application.yaml | 1 - .../config/IotAbstractDataSinkConfig.java | 2 + .../rule/config/IotDataSinkTcpConfig.java | 63 ++++ .../config/IotDataSinkWebSocketConfig.java | 87 ++++++ .../alert/IotAlertConfigServiceImpl.java | 4 +- .../data/action/IotKafkaDataRuleAction.java | 2 +- .../data/action/IotTcpDataRuleAction.java | 91 ++++++ .../rule/data/action/tcp/IotTcpClient.java | 184 +++++++++++ .../rule/scene/IotSceneRuleServiceImpl.java | 98 +++--- .../IotAlertRecoverSceneRuleAction.java | 1 - .../IotAlertTriggerSceneRuleAction.java | 1 - .../IotDeviceControlSceneRuleAction.java | 127 ++++++-- ...IotDeviceServiceInvokeSceneRuleAction.java | 145 +++++++++ .../scene/matcher/IotSceneRuleMatcher.java | 6 +- .../matcher/IotSceneRuleMatcherHelper.java | 8 +- .../matcher/IotSceneRuleMatcherManager.java | 19 +- ...va => IotCurrentTimeConditionMatcher.java} | 6 +- ...=> IotDevicePropertyConditionMatcher.java} | 14 +- ...va => IotDeviceStateConditionMatcher.java} | 12 +- .../IotSceneRuleConditionMatcher.java | 9 +- ... => IotDeviceEventPostTriggerMatcher.java} | 6 +- ... IotDevicePropertyPostTriggerMatcher.java} | 12 +- ...IotDeviceServiceInvokeTriggerMatcher.java} | 8 +- ...> IotDeviceStateUpdateTriggerMatcher.java} | 18 +- .../trigger/IotSceneRuleTriggerMatcher.java | 9 +- ...tcher.java => IotTimerTriggerMatcher.java} | 7 +- .../scene/timer/IotSceneRuleTimerHandler.java | 154 ++++++++++ .../data/action/IotTcpDataRuleActionTest.java | 162 ++++++++++ .../matcher/IotBaseConditionMatcherTest.java | 32 ++ ...> IotCurrentTimeConditionMatcherTest.java} | 16 +- ...otDevicePropertyConditionMatcherTest.java} | 290 +++++++++++------- ...> IotDeviceStateConditionMatcherTest.java} | 16 +- ...IotDeviceEventPostTriggerMatcherTest.java} | 16 +- ...DevicePropertyPostTriggerMatcherTest.java} | 16 +- ...eviceServiceInvokeTriggerMatcherTest.java} | 16 +- ...tDeviceStateUpdateTriggerMatcherTest.java} | 16 +- ...t.java => IotTimerTriggerMatcherTest.java} | 16 +- .../timer/IotSceneRuleTimerHandlerTest.java | 126 ++++++++ .../service/coupon/CouponServiceImpl.java | 4 +- .../TradeStatusSyncToWxaOrderHandler.java | 4 +- .../service/order/PayOrderServiceTest.java | 4 +- .../service/auth/AdminAuthServiceImpl.java | 4 +- 90 files changed, 3143 insertions(+), 1365 deletions(-) create mode 100644 yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessPrintDataRespVO.java create mode 100644 yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_ele/general/api/api.ts.vm create mode 100644 yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_ele/schema/api/api.ts.vm delete mode 100644 yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageMethodEnum.java create mode 100644 yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkTcpConfig.java create mode 100644 yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/rule/config/IotDataSinkWebSocketConfig.java create mode 100644 yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotTcpDataRuleAction.java create mode 100644 yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/data/action/tcp/IotTcpClient.java create mode 100644 yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotDeviceServiceInvokeSceneRuleAction.java rename yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/{CurrentTimeConditionMatcher.java => IotCurrentTimeConditionMatcher.java} (98%) rename yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/{DevicePropertyConditionMatcher.java => IotDevicePropertyConditionMatcher.java} (85%) rename yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/{DeviceStateConditionMatcher.java => IotDeviceStateConditionMatcher.java} (80%) rename yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/{DeviceEventPostTriggerMatcher.java => IotDeviceEventPostTriggerMatcher.java} (94%) rename yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/{DevicePropertyPostTriggerMatcher.java => IotDevicePropertyPostTriggerMatcher.java} (86%) rename yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/{DeviceServiceInvokeTriggerMatcher.java => IotDeviceServiceInvokeTriggerMatcher.java} (92%) rename yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/{DeviceStateUpdateTriggerMatcher.java => IotDeviceStateUpdateTriggerMatcher.java} (76%) rename yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/{TimerTriggerMatcher.java => IotTimerTriggerMatcher.java} (92%) create mode 100644 yudao-module-iot/yudao-module-iot-server/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/timer/IotSceneRuleTimerHandler.java create mode 100644 yudao-module-iot/yudao-module-iot-server/src/test/java/cn/iocoder/yudao/module/iot/service/rule/data/action/IotTcpDataRuleActionTest.java create mode 100644 yudao-module-iot/yudao-module-iot-server/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotBaseConditionMatcherTest.java rename yudao-module-iot/yudao-module-iot-server/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/{CurrentTimeConditionMatcherTest.java => IotCurrentTimeConditionMatcherTest.java} (96%) rename yudao-module-iot/yudao-module-iot-server/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/{DevicePropertyConditionMatcherTest.java => IotDevicePropertyConditionMatcherTest.java} (56%) rename yudao-module-iot/yudao-module-iot-server/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/{DeviceStateConditionMatcherTest.java => IotDeviceStateConditionMatcherTest.java} (96%) rename yudao-module-iot/yudao-module-iot-server/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/{DeviceEventPostTriggerMatcherTest.java => IotDeviceEventPostTriggerMatcherTest.java} (96%) rename yudao-module-iot/yudao-module-iot-server/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/{DevicePropertyPostTriggerMatcherTest.java => IotDevicePropertyPostTriggerMatcherTest.java} (96%) rename yudao-module-iot/yudao-module-iot-server/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/{DeviceServiceInvokeTriggerMatcherTest.java => IotDeviceServiceInvokeTriggerMatcherTest.java} (96%) rename yudao-module-iot/yudao-module-iot-server/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/{DeviceStateUpdateTriggerMatcherTest.java => IotDeviceStateUpdateTriggerMatcherTest.java} (94%) rename yudao-module-iot/yudao-module-iot-server/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/{TimerTriggerMatcherTest.java => IotTimerTriggerMatcherTest.java} (95%) create mode 100644 yudao-module-iot/yudao-module-iot-server/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/timer/IotSceneRuleTimerHandlerTest.java diff --git a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java index ed5e6c28b..f9ca67adb 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java @@ -165,14 +165,6 @@ public class YudaoTenantAutoConfiguration { return ignoreUrls; } - // ========== Job ========== - - @Bean - @ConditionalOnClass(name = "com.xxl.job.core.handler.annotation.XxlJob") - public TenantJobAspect tenantJobAspect(TenantFrameworkService tenantFrameworkService) { - return new TenantJobAspect(tenantFrameworkService); - } - // ========== MQ ========== /** @@ -203,6 +195,14 @@ public class YudaoTenantAutoConfiguration { return new TenantRocketMQInitializer(); } + // ========== Job ========== + + @Bean + @ConditionalOnClass(name = "com.xxl.job.core.handler.annotation.XxlJob") + public TenantJobAspect tenantJobAspect(TenantFrameworkService tenantFrameworkService) { + return new TenantJobAspect(tenantFrameworkService); + } + // ========== Redis ========== @Bean diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/YudaoMybatisAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/YudaoMybatisAutoConfiguration.java index 4cbb91c2c..775447a06 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/YudaoMybatisAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/config/YudaoMybatisAutoConfiguration.java @@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.mybatis.core.handler.DefaultDBFieldHandler; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; +import com.baomidou.mybatisplus.core.handlers.IJsonTypeHandler; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator; import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; @@ -80,8 +81,8 @@ public class YudaoMybatisAutoConfiguration { throw new IllegalArgumentException(StrUtil.format("DbType{} 找不到合适的 IKeyGenerator 实现类", dbType)); } - @Bean - public JacksonTypeHandler jacksonTypeHandler(List objectMappers) { + @Bean // 特殊:返回结果使用 Object 而不用 JacksonTypeHandler 的原因,避免因为 JacksonTypeHandler 被 mybatis 全局使用! + public Object jacksonTypeHandler(List objectMappers) { // 特殊:设置 JacksonTypeHandler 的 ObjectMapper! ObjectMapper objectMapper = CollUtil.getFirst(objectMappers); if (objectMapper == null) { diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java index ed69dfbd8..e77a065f9 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/swagger/config/YudaoSwaggerAutoConfiguration.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.framework.swagger.config; +import com.github.xiaoymin.knife4j.spring.configuration.Knife4jAutoConfiguration; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Contact; @@ -11,6 +12,7 @@ import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityScheme; import org.springdoc.core.customizers.OpenApiBuilderCustomizer; +import org.springdoc.core.customizers.OperationCustomizer; import org.springdoc.core.customizers.ServerBaseUrlCustomizer; import org.springdoc.core.models.GroupedOpenApi; import org.springdoc.core.properties.SpringDocConfigProperties; @@ -43,7 +45,7 @@ import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_ * * @author 芋道源码 */ -@AutoConfiguration +@AutoConfiguration(before = Knife4jAutoConfiguration.class) // before 原因,保证覆写的 Knife4jOpenApiCustomizer 先生效!相关 https://github.com/YunaiV/ruoyi-vue-pro/issues/954 讨论 @ConditionalOnClass({OpenAPI.class}) @EnableConfigurationProperties(SwaggerProperties.class) @ConditionalOnProperty(prefix = "springdoc.api-docs", name = "enabled", havingValue = "true", matchIfMissing = true) // 设置为 false 时,禁用 @@ -102,7 +104,6 @@ public class YudaoSwaggerAutoConfiguration { Optional> openApiBuilderCustomizers, Optional> serverBaseUrlCustomizers, Optional javadocProvider) { - return new OpenAPIService(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider); } @@ -128,6 +129,7 @@ public class YudaoSwaggerAutoConfiguration { .addOperationCustomizer((operation, handlerMethod) -> operation .addParametersItem(buildTenantHeaderParameter()) .addParametersItem(buildSecurityHeaderParameter())) + .addOperationCustomizer(buildOperationIdCustomizer()) .build(); } @@ -159,5 +161,26 @@ public class YudaoSwaggerAutoConfiguration { .schema(new StringSchema()._default("Bearer test1").name(HEADER_TENANT_ID).description("认证 Token")); // 默认:使用用户编号为 1 } + /** + * 核心:自定义OperationId生成规则,组合「类名前缀 + 方法名」 + * + * @see app-api 前缀不生效,都是使用 admin-api + */ + private static OperationCustomizer buildOperationIdCustomizer() { + return (operation, handlerMethod) -> { + // 1. 获取控制器类名(如 UserController) + String className = handlerMethod.getBeanType().getSimpleName(); + // 2. 提取类名前缀(去除 Controller 后缀,如 UserController -> User) + String classPrefix = className.replaceAll("Controller$", ""); + // 3. 获取方法名(如 list) + String methodName = handlerMethod.getMethod().getName(); + // 4. 组合生成 operationId(如 User_list) + String operationId = classPrefix + "_" + methodName; + // 5. 设置自定义 operationId + operation.setOperationId(operationId); + return operation; + }; + } + } diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java index 943a82d54..5284281f1 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java @@ -100,6 +100,10 @@ public class BpmModelMetaInfoVO { @Schema(description = "任务后置通知设置", example = "{}") private HttpRequestSetting taskAfterTriggerSetting; + @Schema(description = "自定义打印模板设置", example = "{}") + @Valid + private PrintTemplateSetting printTemplateSetting; + @Schema(description = "流程 ID 规则") @Data @Valid @@ -180,4 +184,17 @@ public class BpmModelMetaInfoVO { } + @Schema(description = "自定义打印模板设置") + @Data + public static class PrintTemplateSetting { + + @Schema(description = "是否自定义打印模板", example = "false") + @NotNull(message = "是否自定义打印模板不能为空") + private Boolean enable; + + @Schema(description = "打印模板", example = "

") + private String template; + + } + } diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java index 83ce14498..bf293c314 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; +import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; @@ -26,6 +27,7 @@ import jakarta.validation.Valid; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.task.api.Task; +import org.flowable.task.api.history.HistoricTaskInstance; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -34,9 +36,11 @@ import java.util.List; import java.util.Map; import java.util.Set; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; +import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS; @Tag(name = "管理后台 - 流程实例") // 流程实例,通过流程定义创建的一次“申请” @RestController @@ -192,8 +196,30 @@ public class BpmProcessInstanceController { @GetMapping("/get-bpmn-model-view") @Operation(summary = "获取流程实例的 BPMN 模型视图", description = "在【流程详细】界面中,进行调用") @Parameter(name = "id", description = "流程实例的编号", required = true) - public CommonResult getProcessInstanceBpmnModelView(@RequestParam(value = "id") String id) { + public CommonResult getProcessInstanceBpmnModelView( + @RequestParam(value = "id") String id) { return success(processInstanceService.getProcessInstanceBpmnModelView(id)); } + @GetMapping("/get-print-data") + @Operation(summary = "获得流程实例的打印数据") + @Parameter(name = "id", description = "流程实例的编号", required = true) + @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") + public CommonResult getProcessInstancePrintData( + @RequestParam("processInstanceId") String processInstanceId) { + HistoricProcessInstance historicProcessInstance = processInstanceService.getHistoricProcessInstance(processInstanceId); + if (historicProcessInstance == null) { + throw exception(PROCESS_INSTANCE_NOT_EXISTS); + } + AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(historicProcessInstance.getStartUserId())).getCheckedData(); + DeptRespDTO dept = deptApi.getDept(startUser.getDeptId()).getCheckedData(); + List tasks = taskService.getFinishedTaskListByProcessInstanceIdWithoutCancel(processInstanceId); + Map userMap = adminUserApi.getUserMap( + convertSet(tasks, item -> Long.valueOf(item.getAssignee()))); + return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstancePrintData(historicProcessInstance, + processDefinitionService.getProcessDefinitionInfo(historicProcessInstance.getProcessDefinitionId()), + tasks, userMap, + new UserSimpleBaseVO().setNickname(startUser.getNickname()).setDeptName(dept.getName()))); + } + } diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCancelReqVO.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCancelReqVO.java index b88d7f7c0..36d4724a0 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCancelReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCancelReqVO.java @@ -1,9 +1,10 @@ package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; import lombok.Data; +import jakarta.validation.constraints.NotEmpty; + @Schema(description = "管理后台 - 流程实例的取消 Request VO") @Data public class BpmProcessInstanceCancelReqVO { diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java index e2eacf96f..b13ff561f 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java @@ -1,9 +1,9 @@ package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; import lombok.Data; +import jakarta.validation.constraints.NotEmpty; import java.util.List; import java.util.Map; diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessPrintDataRespVO.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessPrintDataRespVO.java new file mode 100644 index 000000000..8f85da966 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessPrintDataRespVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - 流程实例的打印数据 Response VO") +@Data +public class BpmProcessPrintDataRespVO { + + @Schema(description = "流程实例数据") + private BpmProcessInstanceRespVO processInstance; + + @Schema(description = "是否开启自定义打印模板", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean printTemplateEnable; + + @Schema(description = "自定义打印模板 HTML") + private String printTemplateHtml; + + @Schema(description = "审批任务列表") + private List tasks; + + @Schema(description = "流程任务") + @Data + public static class Task { + + @Schema(description = "流程任务的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private String id; + + @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + private String name; + + @Schema(description = "签名 URL", example = "https://www.iocoder.cn/sign.png") + private String signPicUrl; + + @Schema(description = "任务描述", requiredMode = Schema.RequiredMode.REQUIRED) + private String description; // 该字段由后端拼接 + + } + +} diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java index f129e5a31..45729f9f2 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java @@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task; import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.util.date.DateUtils; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; @@ -21,6 +23,10 @@ public class BpmTaskPageReqVO extends PageParam { @Schema(description = "流程定义的标识", example = "2048") private String processDefinitionKey; // 精准匹配 + @Schema(description = "审批状态", example = "1") + @InEnum(BpmTaskStatusEnum.class) + private Integer status; // 仅【已办】使用 + @Schema(description = "创建时间") @DateTimeFormat(pattern = DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) private LocalDateTime[] createTime; diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java index 45d5973f4..6ba51afec 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java @@ -1,23 +1,29 @@ package cn.iocoder.yudao.module.bpm.convert.task; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.collection.SetUtils; +import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO; +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceBpmnModelViewRespVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO; +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessPrintDataRespVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; import cn.iocoder.yudao.module.bpm.convert.definition.BpmProcessDefinitionConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent; +import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO; @@ -35,10 +41,7 @@ import org.mapstruct.Mapping; import org.mapstruct.MappingTarget; import org.mapstruct.factory.Mappers; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; @@ -293,4 +296,47 @@ public interface BpmProcessInstanceConvert { .setActivityNodes(activityNodes); } + default BpmProcessPrintDataRespVO buildProcessInstancePrintData(HistoricProcessInstance historicProcessInstance, + BpmProcessDefinitionInfoDO processDefinitionInfo, + List tasks, + Map userMap, + UserSimpleBaseVO startUser) { + BpmModelMetaInfoVO.PrintTemplateSetting printTemplateSetting = processDefinitionInfo.getPrintTemplateSetting(); + BpmProcessPrintDataRespVO printData = new BpmProcessPrintDataRespVO(); + // 打印模板是否开启 + printData.setPrintTemplateEnable(printTemplateSetting != null && Boolean.TRUE.equals(printTemplateSetting.getEnable())); + // 流程相关数据 + BpmProcessInstanceRespVO processInstance = new BpmProcessInstanceRespVO() + .setId(historicProcessInstance.getId()).setName(historicProcessInstance.getName()) + .setBusinessKey(historicProcessInstance.getBusinessKey()) + .setStartTime(DateUtils.of(historicProcessInstance.getStartTime())) + .setEndTime(DateUtils.of(historicProcessInstance.getEndTime())) + .setStartUser(startUser).setStatus(FlowableUtils.getProcessInstanceStatus(historicProcessInstance)) + .setFormVariables(historicProcessInstance.getProcessVariables()) + .setProcessDefinition(BeanUtils.toBean(processDefinitionInfo, BpmProcessDefinitionRespVO.class)); + printData.setProcessInstance(processInstance); + // 审批历史 + List approveTasks = new ArrayList<>(tasks.size()); + tasks.forEach(item -> { + Map taskLocalVariables = item.getTaskLocalVariables(); + BpmProcessPrintDataRespVO.Task approveTask = new BpmProcessPrintDataRespVO.Task(); + approveTask.setName(item.getName()); + approveTask.setId(item.getId()); + approveTask.setSignPicUrl((String) taskLocalVariables.get(BpmnVariableConstants.TASK_SIGN_PIC_URL)); + approveTask.setDescription(StrUtil.format("{} / {} / {} / {} / {}", + userMap.get(Long.valueOf(item.getAssignee())).getNickname(), + item.getName(), + DateUtil.formatDateTime(item.getEndTime()), + BpmTaskStatusEnum.valueOf((Integer) taskLocalVariables.get(BpmnVariableConstants.TASK_VARIABLE_STATUS)).getName(), + taskLocalVariables.get(BpmnVariableConstants.TASK_VARIABLE_REASON))); + approveTasks.add(approveTask); + }); + printData.setTasks(approveTasks); + // 自定义模板 + if (printData.getPrintTemplateEnable() && printTemplateSetting != null) { + printData.setPrintTemplateHtml(printTemplateSetting.getTemplate()); + } + return printData; + } + } diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java index 893c4d053..b3ce94614 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java @@ -43,14 +43,23 @@ public class BpmnVariableConstants { * @see ProcessInstance#getProcessVariables() */ public static final String PROCESS_INSTANCE_VARIABLE_START_USER_ID = "PROCESS_START_USER_ID"; + /** - * 流程实例的变量 - 用于判断流程实例变量节点是否驳回. 格式 RETURN_FLAG_{节点 id} + * 流程实例的变量 - 用于判断流程实例变量节点是否驳回:格式 RETURN_FLAG_{节点 id} * - * 目的是:驳回到发起节点时,因为审批人与发起人相同,所以被自动通过。但是,此时还是希望不要自动通过 + * 目的是:退回到发起节点时,因为审批人与发起人相同,所以被自动通过。但是,此时还是希望不要自动通过 * * @see ProcessInstance#getProcessVariables() */ public static final String PROCESS_INSTANCE_VARIABLE_RETURN_FLAG = "RETURN_FLAG_%s"; + + /** + * 流程实例的变量前缀 - 用于退回操作,记录需要预测的节点:格式 NEED_SIMULATE_TASK_{节点定义 id} + * + * 目的是:退回操作,预测节点会不准,在流程变量中记录需要预测的节点,来辅助预测 + */ + public static final String PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_PREFIX = "NEED_SIMULATE_TASK_"; + /** * 流程实例的变量 - 是否跳过表达式 * diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 7946d1289..bdbc7d67a 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.Assert; +import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.*; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; @@ -71,6 +72,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. import static cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ActivityNode; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID; +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_PREFIX; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseNodeType; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; @@ -186,6 +188,10 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService if (CollUtil.isNotEmpty(reqVO.getProcessVariables())) { processVariables.putAll(reqVO.getProcessVariables()); } + // 特殊:如果是未发起的场景,则设置发起用户,解决“发起流程”时,需要使用到该变量的问题。例如说:https://t.zsxq.com/fMw5g + if (historicProcessInstance == null) { + processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, loginUserId); + } // 1.3 读取其它相关数据 ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition( historicProcessInstance != null ? historicProcessInstance.getProcessDefinitionId() @@ -217,10 +223,24 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService // 3.1 计算当前登录用户的待办任务 BpmTaskRespVO todoTask = taskService.getTodoTask(loginUserId, reqVO.getTaskId(), reqVO.getProcessInstanceId()); - // 3.2 预测未运行节点的审批信息 + + // 3.2 获取由于退回操作,需要预测的节点。从流程变量中获取,回退操作会设置这些变量 + Set needSimulateTaskDefKeysByReturn = new HashSet<>(); + if (StrUtil.isNotEmpty(reqVO.getProcessInstanceId())) { + Map variables = runtimeService.getVariables(reqVO.getProcessInstanceId()); + Map simulateTaskVariables = MapUtil.filter(variables, + item -> item.getKey().startsWith(PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_PREFIX)); + simulateTaskVariables.forEach((key, value) -> + needSimulateTaskDefKeysByReturn.add(StrUtil.removePrefix(key, PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_PREFIX))); + } + // 移除运行中的节点,运行中的节点无需预测 + // TODO @jason:是不是 foreach runActivityNodes,然后移除 needSimulateTaskDefKeysByReturn 更好?(理解成本低一点) + CollectionUtils.convertList(runActivityNodes, ActivityNode::getId).forEach(needSimulateTaskDefKeysByReturn::remove); + + // 3.3 预测未运行节点的审批信息 List simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel, processDefinitionInfo, - processVariables, activities); + processVariables, activities, needSimulateTaskDefKeysByReturn); // 4. 拼接最终数据 return buildApprovalDetail(reqVO, bpmnModel, processDefinition, processDefinitionInfo, historicProcessInstance, @@ -460,7 +480,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService } /** - * 获取结束节点的状态 + * 获取结束节点的状态 */ private Integer getEndActivityNodeStatus(HistoricTaskInstance task) { Integer status = FlowableUtils.getTaskStatus(task); @@ -545,7 +565,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService private List getSimulateApproveNodeList(Long startUserId, BpmnModel bpmnModel, BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, - List activities) { + List activities, + Set needSimulateTaskDefKeysByReturn) { // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 Set runActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId); @@ -554,7 +575,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService List flowElements = BpmnModelUtils.simulateProcess(bpmnModel, processVariables); return convertList(flowElements, flowElement -> buildNotRunApproveNodeForBpmn( startUserId, bpmnModel, flowElements, - processDefinitionInfo, processVariables, flowElement, runActivityIds)); + processDefinitionInfo, processVariables, flowElement, runActivityIds, needSimulateTaskDefKeysByReturn)); } // 情况二:SIMPLE 设计器 if (Objects.equals(BpmModelTypeEnum.SIMPLE.getType(), processDefinitionInfo.getModelType())) { @@ -563,17 +584,19 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService List simpleNodes = SimpleModelUtils.simulateProcess(simpleModel, processVariables); return convertList(simpleNodes, simpleNode -> buildNotRunApproveNodeForSimple( startUserId, bpmnModel, - processDefinitionInfo, processVariables, simpleNode, runActivityIds)); + processDefinitionInfo, processVariables, simpleNode, runActivityIds, needSimulateTaskDefKeysByReturn)); } throw new IllegalArgumentException("未知设计器类型:" + processDefinitionInfo.getModelType()); } private ActivityNode buildNotRunApproveNodeForSimple(Long startUserId, BpmnModel bpmnModel, BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, - BpmSimpleModelNodeVO node, Set runActivityIds) { + BpmSimpleModelNodeVO node, Set runActivityIds, + Set needSimulateTaskDefKeysByReturn) { // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 - if (runActivityIds.contains(node.getId())) { + if (runActivityIds.contains(node.getId()) + && !needSimulateTaskDefKeysByReturn.contains(node.getId())) { // 特殊:回退操作时候,会记录需要预测的节点到流程变量中。即使在历史操作中,也需要预测 return null; } Integer status = BpmTaskStatusEnum.NOT_START.getStatus(); @@ -620,13 +643,16 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService private ActivityNode buildNotRunApproveNodeForBpmn(Long startUserId, BpmnModel bpmnModel, List flowElements, BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, - FlowElement node, Set runActivityIds) { - if (runActivityIds.contains(node.getId())) { + FlowElement node, Set runActivityIds, + Set needSimulateTaskDefKeysByReturn) { + // 回退操作时候,会记录需要预测的节点到流程变量中。即使节点在历史操作中,也需要预测。 + if (!needSimulateTaskDefKeysByReturn.contains(node.getId()) && runActivityIds.contains(node.getId())) { return null; } + Integer status = BpmTaskStatusEnum.NOT_START.getStatus(); // 如果节点被跳过,状态设置为跳过 - if(BpmnModelUtils.isSkipNode(node, processVariables)){ + if (BpmnModelUtils.isSkipNode(node, processVariables)) { status = BpmTaskStatusEnum.SKIP.getStatus(); } ActivityNode activityNode = new ActivityNode().setId(node.getId()) diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java index 34db2876f..f7d260ed1 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java @@ -176,6 +176,14 @@ public interface BpmTaskService { */ List getHistoricActivityListByExecutionId(String executionId); + /** + * 获得指定流程实例的已完成的流程任务列表,不包含取消状态 + * + * @param processInstanceId 流程实例的编号 + * @return 流程任务列表 + */ + List getFinishedTaskListByProcessInstanceIdWithoutCancel(String processInstanceId); + // ========== Update 写入相关方法 ========== /** diff --git a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index d26f06527..3b3b1264d 100644 --- a/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -2,10 +2,12 @@ package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; +import cn.hutool.core.date.DateUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.*; import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; @@ -68,8 +70,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID; -import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG; -import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE; +//import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.*; import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; /** @@ -230,6 +231,9 @@ public class BpmTaskServiceImpl implements BpmTaskService { if (StrUtil.isNotBlank(pageVO.getName())) { taskQuery.taskNameLike("%" + pageVO.getName() + "%"); } + if (pageVO.getStatus() != null) { + taskQuery.taskVariableValueEquals(BpmnVariableConstants.TASK_VARIABLE_STATUS, pageVO.getStatus()); + } // if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) { // taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0])); // taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1])); @@ -491,6 +495,17 @@ public class BpmTaskServiceImpl implements BpmTaskService { return historyService.createHistoricActivityInstanceQuery().executionId(executionId).list(); } + @Override + public List getFinishedTaskListByProcessInstanceIdWithoutCancel(String processInstanceId) { + return historyService.createHistoricTaskInstanceQuery() + .finished() + .includeTaskLocalVariables() + .processInstanceId(processInstanceId) + .taskVariableValueNotEquals(BpmnVariableConstants.TASK_VARIABLE_STATUS, + BpmTaskStatusEnum.CANCEL.getStatus()) + .orderByHistoricTaskInstanceStartTime().asc().list(); + } + /** * 判断指定用户,是否是当前任务的审批人 * @@ -590,7 +605,13 @@ public class BpmTaskServiceImpl implements BpmTaskService { bpmnModel, reqVO.getNextAssignees(), instance); runtimeService.setVariables(task.getProcessInstanceId(), variables); - // 5. 调用 BPM complete 去完成任务 + // 5. 移除辅助预测的流程变量,这些变量在回退操作中设置 + // todo @jason:可以直接 + 拼接哈 + String simulateVariableName = StrUtil.concat(false, + BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_PREFIX, task.getTaskDefinitionKey()); + runtimeService.removeVariable(task.getProcessInstanceId(), simulateVariableName); + + // 6. 调用 BPM complete 去完成任务 taskService.complete(task.getId(), variables, true); // 【加签专属】处理加签任务 @@ -840,34 +861,34 @@ public class BpmTaskServiceImpl implements BpmTaskService { if (task.isSuspended()) { throw exception(TASK_IS_PENDING); } - // 1.2 校验源头和目标节点的关系,并返回目标元素 - FlowElement targetElement = validateTargetTaskCanReturn(task.getTaskDefinitionKey(), - reqVO.getTargetTaskDefinitionKey(), task.getProcessDefinitionId()); + // 1.2 获取流程模型信息 + BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); + // 1.3 校验源头和目标节点的关系,并返回目标元素 + FlowElement targetElement = validateTargetTaskCanReturn(bpmnModel, task.getTaskDefinitionKey(), + reqVO.getTargetTaskDefinitionKey()); // 2. 调用 Flowable 框架的退回逻辑 - returnTask(userId, task, targetElement, reqVO); + returnTask(userId, bpmnModel, task, targetElement, reqVO); } /** * 退回流程节点时,校验目标任务节点是否可退回 * - * @param sourceKey 当前任务节点 Key - * @param targetKey 目标任务节点 key - * @param processDefinitionId 当前流程定义 ID + * @param bpmnModel 流程模型 + * @param sourceKey 当前任务节点 Key + * @param targetKey 目标任务节点 key * @return 目标任务节点元素 */ - private FlowElement validateTargetTaskCanReturn(String sourceKey, String targetKey, String processDefinitionId) { - // 1.1 获取流程模型信息 - BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processDefinitionId); - // 1.3 获取当前任务节点元素 + private FlowElement validateTargetTaskCanReturn(BpmnModel bpmnModel, String sourceKey, String targetKey) { + // 1.1 获取当前任务节点元素 FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, sourceKey); - // 1.3 获取跳转的节点元素 + // 1.2 获取跳转的节点元素 FlowElement target = BpmnModelUtils.getFlowElementById(bpmnModel, targetKey); if (target == null) { throw exception(TASK_TARGET_NODE_NOT_EXISTS); } - // 2.2 只有串行可到达的节点,才可以退回。类似非串行、子流程无法退回 + // 2. 只有串行可到达的节点,才可以退回。类似非串行、子流程无法退回 if (!BpmnModelUtils.isSequentialReachable(source, target, null)) { throw exception(TASK_RETURN_FAIL_SOURCE_TARGET_ERROR); } @@ -878,11 +899,12 @@ public class BpmTaskServiceImpl implements BpmTaskService { * 执行退回逻辑 * * @param userId 用户编号 + * @param bpmnModel 流程模型 * @param currentTask 当前退回的任务 * @param targetElement 需要退回到的目标任务 * @param reqVO 前端参数封装 */ - public void returnTask(Long userId, Task currentTask, FlowElement targetElement, BpmTaskReturnReqVO reqVO) { + public void returnTask(Long userId, BpmnModel bpmnModel, Task currentTask, FlowElement targetElement, BpmTaskReturnReqVO reqVO) { // 1. 获得所有需要回撤的任务 taskDefinitionKey,用于稍后的 moveActivityIdsToSingleActivityId 回撤 // 1.1 获取所有正常进行的任务节点 Key List taskList = taskService.createTaskQuery().processInstanceId(currentTask.getProcessInstanceId()).list(); @@ -915,18 +937,54 @@ public class BpmTaskServiceImpl implements BpmTaskService { } }); - // 3. 设置流程变量节点驳回标记:用于驳回到节点,不执行 BpmUserTaskAssignStartUserHandlerTypeEnum 策略。导致自动通过 - runtimeService.setVariable(currentTask.getProcessInstanceId(), - String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, reqVO.getTargetTaskDefinitionKey()), Boolean.TRUE); + // 3. 构建需要预测的任务流程变量 + // TODO @jason:【驳回预测相关】是不是搞成一个变量,里面是 set 更简洁一点呀? + Set taskDefinitionKeyList = getNeedSimulateTaskDefinitionKeys(bpmnModel, currentTask, targetElement); + Map needSimulateVariables = convertMap(taskDefinitionKeyList, + taskId -> StrUtil.concat(false, BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_PREFIX, taskId), item -> Boolean.TRUE); + // 4. 执行驳回 // 使用 moveExecutionsToSingleActivityId 替换 moveActivityIdsToSingleActivityId 原因: // 当多实例任务回退的时候有问题。相关 issue: https://github.com/flowable/flowable-engine/issues/3944 runtimeService.createChangeActivityStateBuilder() .processInstanceId(currentTask.getProcessInstanceId()) .moveExecutionsToSingleActivityId(runExecutionIds, reqVO.getTargetTaskDefinitionKey()) + // 设置需要预测的任务流程变量,用于辅助预测 + .processVariables(needSimulateVariables) + // 设置流程变量(local)节点退回标记, 用于退回到节点,不执行 BpmUserTaskAssignStartUserHandlerTypeEnum 策略,导致自动通过 + .localVariable(reqVO.getTargetTaskDefinitionKey(), + String.format(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, reqVO.getTargetTaskDefinitionKey()), + Boolean.TRUE) .changeState(); } + private Set getNeedSimulateTaskDefinitionKeys(BpmnModel bpmnModel, Task currentTask, FlowElement targetElement) { + // 1. 获取需要预测的任务的 definition key。因为当前任务还没完成,也需要预测 + Set taskDefinitionKeys = CollUtil.newHashSet(currentTask.getTaskDefinitionKey()); + + // 2.1 从已结束任务中找到要回退的目标任务,按时间倒序最近的一个目标任务 + List endTaskList = CollectionUtils.filterList( + getTaskListByProcessInstanceId(currentTask.getProcessInstanceId(), Boolean.FALSE), + item -> item.getEndTime() != null); + // 2.2 遍历已结束的任务,找到在 targetTask 之后生成的任务,且串行可达的任务 + HistoricTaskInstance targetTask = findFirst(endTaskList, + item -> item.getTaskDefinitionKey().equals(targetElement.getId())); + // TODO @jason:【驳回预测相关】是不是 if targetTask 先判空? + endTaskList.forEach(item -> { + FlowElement element = getFlowElementById(bpmnModel, item.getTaskDefinitionKey()); + // 如果已结束的任务在回退目标节点之后生成,且串行可达,则标记为需要预算节点 + // TODO 串行可达的方法需要和判断可回退节点 validateTargetTaskCanReturn 分开吗? 并行网关可能会有问题。 + // TODO @jason:【驳回预测相关】这里是不是判断 element 哈? + if (targetTask != null + // TODO @jason:【驳回预测相关】这里直接 createTime 的 compare 更简单?因为不太会出现空哈。 + && DateUtil.compare(item.getCreateTime(), targetTask.getCreateTime()) > 0 + && BpmnModelUtils.isSequentialReachable(element, targetElement, null)) { + taskDefinitionKeys.add(item.getTaskDefinitionKey()); + } + }); + return taskDefinitionKeys; + } + @Override @Transactional(rollbackFor = Exception.class) public void delegateTask(Long userId, BpmTaskDelegateReqVO reqVO) { @@ -1438,12 +1496,11 @@ public class BpmTaskServiceImpl implements BpmTaskService { return; } FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); - // 判断是否为退回或者驳回:如果是退回或者驳回不走这个策略 - // TODO 芋艿:【优化】未来有没更好的判断方式?!另外,还要考虑清理机制。就是说,下次处理了之后,就移除这个标识 - Boolean returnTaskFlag = runtimeService.getVariable(processInstance.getProcessInstanceId(), - String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class); + // 判断是否为退回或者驳回:如果是退回或者驳回不走这个策略(使用 local variable) + Boolean returnTaskFlag = runtimeService.getVariableLocal(task.getExecutionId(), + String.format(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class); Boolean skipStartUserNodeFlag = Convert.toBool(runtimeService.getVariable(processInstance.getProcessInstanceId(), - PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE, String.class)); + BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE, String.class)); if (userTaskElement.getId().equals(START_USER_NODE_ID) && (skipStartUserNodeFlag == null // 目的:一般是“主流程”,发起人节点,自动通过审核 || BooleanUtil.isTrue(skipStartUserNodeFlag)) // 目的:一般是“子流程”,发起人节点,按配置自动通过审核 diff --git a/yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java b/yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java index fe84db1bd..b5c301a28 100644 --- a/yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-server/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java @@ -7,7 +7,6 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO; import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueSaveReqVO; import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransferReqVO; -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer.CrmCustomerSaveReqVO; import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO; import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO; import cn.iocoder.yudao.module.crm.dal.mysql.clue.CrmClueMapper; @@ -113,7 +112,7 @@ public class CrmClueServiceImpl implements CrmClueService { private void validateRelationDataExists(CrmClueSaveReqVO reqVO) { // 校验负责人 if (Objects.nonNull(reqVO.getOwnerUserId()) && - Objects.isNull(adminUserApi.getUser(reqVO.getOwnerUserId()))) { + Objects.isNull(adminUserApi.getUser(reqVO.getOwnerUserId()).getCheckedData())) { throw exception(USER_NOT_EXISTS); } } diff --git a/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue/api/api.js.vm b/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue/api/api.js.vm index 835c0192e..0d9b66f88 100644 --- a/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue/api/api.js.vm +++ b/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue/api/api.js.vm @@ -27,6 +27,16 @@ export function delete${simpleClassName}(id) { }) } +#if ( $table.templateType != 2 && $deleteBatchEnable) +/** 批量删除${table.classComment} */ +export function delete${simpleClassName}List(ids) { + return request({ + url: `${baseURL}/delete-list?ids=${ids.join(',')}`, + method: 'delete' + }) +} +#end + // 获得${table.classComment} export function get${simpleClassName}(id) { return request({ @@ -130,6 +140,15 @@ export function export${simpleClassName}Excel(params) { method: 'delete' }) } + #if ($deleteBatchEnable) + /** 批量删除${subTable.classComment} */ + export function delete${subSimpleClassName}List(ids) { + return request({ + url: `${baseURL}/${subSimpleClassName_strikeCase}/delete-list?ids=${ids.join(',')}`, + method: 'delete' + }) + } + #end // 获得${subTable.classComment} export function get${subSimpleClassName}(id) { return request({ diff --git a/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3/api/api.ts.vm b/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3/api/api.ts.vm index c3044fb87..c9f736126 100644 --- a/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3/api/api.ts.vm +++ b/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3/api/api.ts.vm @@ -1,19 +1,56 @@ import request from '@/config/axios' +import type { Dayjs } from 'dayjs'; #set ($baseURL = "/${table.moduleName}/${simpleClassName_strikeCase}") -// ${table.classComment} VO -export interface ${simpleClassName}VO { -#foreach ($column in $columns) -#if ($column.createOperation || $column.updateOperation) -#if(${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer" || ${column.javaType.toLowerCase()} == "short" || ${column.javaType.toLowerCase()} == "double" || ${column.javaType.toLowerCase()} == "bigdecimal") - ${column.javaField}: number // ${column.columnComment} -#elseif(${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdate" || ${column.javaType.toLowerCase()} == "localdatetime") - ${column.javaField}: Date // ${column.columnComment} -#else - ${column.javaField}: ${column.javaType.toLowerCase()} // ${column.columnComment} -#end -#end +## 特殊:主子表专属逻辑 +#foreach ($subTable in $subTables) + #set ($index = $foreach.count - 1) + #set ($subSimpleClassName = $subSimpleClassNames.get($index)) + #set ($subColumns = $subColumnsList.get($index))##当前字段数组 +/** ${subTable.classComment}信息 */ +export interface ${subSimpleClassName} { + #foreach ($column in $subColumns) + #if ($column.createOperation || $column.updateOperation) + #if(${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer" || ${column.javaType.toLowerCase()} == "short" || ${column.javaType.toLowerCase()} == "double" || ${column.javaType.toLowerCase()} == "bigdecimal") + ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: number; // ${column.columnComment} + #elseif(${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdate" || ${column.javaType.toLowerCase()} == "localdatetime") + ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: string | Dayjs; // ${column.columnComment} + #else + ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: ${column.javaType.toLowerCase()}; // ${column.columnComment} + #end + #end + #end +} + #end +/** ${table.classComment}信息 */ +export interface ${simpleClassName} { + #foreach ($column in $columns) + #if ($column.createOperation || $column.updateOperation) + #if(${column.javaType.toLowerCase()} == "long" || ${column.javaType.toLowerCase()} == "integer" || ${column.javaType.toLowerCase()} == "short" || ${column.javaType.toLowerCase()} == "double" || ${column.javaType.toLowerCase()} == "bigdecimal") + ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: number; // ${column.columnComment} + #elseif(${column.javaType.toLowerCase()} == "date" || ${column.javaType.toLowerCase()} == "localdate" || ${column.javaType.toLowerCase()} == "localdatetime") + ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: string | Dayjs; // ${column.columnComment} + #else + ${column.javaField}#if($column.updateOperation && !$column.primaryKey && !$column.nullable)?#end: ${column.javaType.toLowerCase()}; // ${column.columnComment} + #end + #end + #end + #if ( $table.templateType == 2 ) + children?: ${simpleClassName}[]; + #end + ## 特殊:主子表专属逻辑 + #if ( $table.templateType == 10 || $table.templateType == 12 ) + #foreach ($subTable in $subTables) + #set ($index = $foreach.count - 1) + #set ($subSimpleClassName = $subSimpleClassNames.get($index)) + #if ( $subTable.subJoinMany ) + ${subSimpleClassName.toLowerCase()}s?: ${subSimpleClassName}[] + #else + ${subSimpleClassName.toLowerCase()}?: ${subSimpleClassName} + #end + #end + #end } // ${table.classComment} API @@ -36,12 +73,12 @@ export const ${simpleClassName}Api = { }, // 新增${table.classComment} - create${simpleClassName}: async (data: ${simpleClassName}VO) => { + create${simpleClassName}: async (data: ${simpleClassName}) => { return await request.post({ url: `${baseURL}/create`, data }) }, // 修改${table.classComment} - update${simpleClassName}: async (data: ${simpleClassName}VO) => { + update${simpleClassName}: async (data: ${simpleClassName}) => { return await request.put({ url: `${baseURL}/update`, data }) }, @@ -50,6 +87,13 @@ export const ${simpleClassName}Api = { return await request.delete({ url: `${baseURL}/delete?id=` + id }) }, +#if ( $table.templateType != 2 && $deleteBatchEnable) + /** 批量删除${table.classComment} */ + delete${simpleClassName}List: async (ids: number[]) => { + return await request.delete({ url: `${baseURL}/delete-list?ids=${ids.join(',')}` }) + }, +#end + // 导出${table.classComment} Excel export${simpleClassName}: async (params) => { return await request.download({ url: `${baseURL}/export-excel`, params }) @@ -92,12 +136,12 @@ export const ${simpleClassName}Api = { ## 特殊:MASTER_ERP 时,支持单个的新增、修改、删除操作 #if ( $table.templateType == 11 ) // 新增${subTable.classComment} - create${subSimpleClassName}: async (data) => { + create${subSimpleClassName}: async (data: ${subSimpleClassName}) => { return await request.post({ url: `${baseURL}/${subSimpleClassName_strikeCase}/create`, data }) }, // 修改${subTable.classComment} - update${subSimpleClassName}: async (data) => { + update${subSimpleClassName}: async (data: ${subSimpleClassName}) => { return await request.put({ url: `${baseURL}/${subSimpleClassName_strikeCase}/update`, data }) }, @@ -106,6 +150,13 @@ export const ${simpleClassName}Api = { return await request.delete({ url: `${baseURL}/${subSimpleClassName_strikeCase}/delete?id=` + id }) }, + #if ($deleteBatchEnable) + /** 批量删除${subTable.classComment} */ + delete${subSimpleClassName}List: async (ids: number[]) => { + return await request.delete({ url: `${baseURL}/${subSimpleClassName_strikeCase}/delete-list?ids=${ids.join(',')}` }) + }, + #end + // 获得${subTable.classComment} get${subSimpleClassName}: async (id: number) => { return await request.get({ url: `${baseURL}/${subSimpleClassName_strikeCase}/get?id=` + id }) diff --git a/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3/views/components/list_sub_erp.vue.vm b/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3/views/components/list_sub_erp.vue.vm index a94cab5a5..29df64de1 100644 --- a/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3/views/components/list_sub_erp.vue.vm +++ b/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3/views/components/list_sub_erp.vue.vm @@ -217,7 +217,7 @@ const handleDeleteBatch = async () => { const checkedIds = ref([]) const handleRowCheckboxChange = (records: ${subSimpleClassName}[]) => { - checkedIds.value = records.map((item) => item.id); + checkedIds.value = records.map((item) => item.id!); } #end #end diff --git a/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3/views/index.vue.vm b/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3/views/index.vue.vm index dfb97804c..fb7485d6d 100644 --- a/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3/views/index.vue.vm +++ b/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3/views/index.vue.vm @@ -374,7 +374,7 @@ const handleDeleteBatch = async () => { const checkedIds = ref([]) const handleRowCheckboxChange = (records: ${simpleClassName}[]) => { - checkedIds.value = records.map((item) => item.id); + checkedIds.value = records.map((item) => item.id!); } #end diff --git a/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_antd/general/api/api.ts.vm b/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_antd/general/api/api.ts.vm index f65994b8c..c3691a8b7 100644 --- a/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_antd/general/api/api.ts.vm +++ b/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_antd/general/api/api.ts.vm @@ -91,18 +91,17 @@ export function delete${simpleClassName}(id: number) { #if ( $table.templateType != 2 && $deleteBatchEnable) /** 批量删除${table.classComment} */ -export function delete${simpleClassName}ListByIds(ids: number[]) { +export function delete${simpleClassName}List(ids: number[]) { return requestClient.delete(`${baseURL}/delete-list?ids=${ids.join(',')}`) } #end /** 导出${table.classComment} */ export function export${simpleClassName}(params: any) { - return requestClient.download('${baseURL}/export-excel', params); + return requestClient.download('${baseURL}/export-excel', { params }); } ## 特殊:主子表专属逻辑 -## TODO @puhui999:下面这块缩进调整了,会乱掉么? #foreach ($subTable in $subTables) #set ($index = $foreach.count - 1) #set ($subSimpleClassName = $subSimpleClassNames.get($index)) @@ -115,54 +114,54 @@ export function export${simpleClassName}(params: any) { // ==================== 子表($subTable.classComment) ==================== - ## 情况一:MASTER_ERP 时,需要分查询页子表 - #if ( $table.templateType == 11 ) - /** 获得${subTable.classComment}分页 */ - export function get${subSimpleClassName}Page(params: PageParam) { - return requestClient.get>(`${baseURL}/${subSimpleClassName_strikeCase}/page`, { params }); - } - ## 情况二:非 MASTER_ERP 时,需要列表查询子表 - #else - #if ( $subTable.subJoinMany ) - /** 获得${subTable.classComment}列表 */ - export function get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaField}: number) { - return requestClient.get<${simpleClassName}Api.${subSimpleClassName}[]>(`${baseURL}/${subSimpleClassName_strikeCase}/list-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=${${subJoinColumn.javaField}}`); - } - #else - /** 获得${subTable.classComment} */ - export function get${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaField}: number) { - return requestClient.get<${simpleClassName}Api.${subSimpleClassName}>(`${baseURL}/${subSimpleClassName_strikeCase}/get-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=${${subJoinColumn.javaField}}`); - } - #end - #end - ## 特殊:MASTER_ERP 时,支持单个的新增、修改、删除操作 - #if ( $table.templateType == 11 ) - /** 新增${subTable.classComment} */ - export function create${subSimpleClassName}(data: ${simpleClassName}Api.${subSimpleClassName}) { - return requestClient.post(`${baseURL}/${subSimpleClassName_strikeCase}/create`, data); - } +## 情况一:MASTER_ERP 时,需要分查询页子表 +#if ( $table.templateType == 11 ) +/** 获得${subTable.classComment}分页 */ +export function get${subSimpleClassName}Page(params: PageParam) { + return requestClient.get>(`${baseURL}/${subSimpleClassName_strikeCase}/page`, { params }); +} + ## 情况二:非 MASTER_ERP 时,需要列表查询子表 +#else +#if ( $subTable.subJoinMany ) +/** 获得${subTable.classComment}列表 */ +export function get${subSimpleClassName}ListBy${SubJoinColumnName}(${subJoinColumn.javaField}: number) { + return requestClient.get<${simpleClassName}Api.${subSimpleClassName}[]>(`${baseURL}/${subSimpleClassName_strikeCase}/list-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=${${subJoinColumn.javaField}}`); +} +#else +/** 获得${subTable.classComment} */ +export function get${subSimpleClassName}By${SubJoinColumnName}(${subJoinColumn.javaField}: number) { + return requestClient.get<${simpleClassName}Api.${subSimpleClassName}>(`${baseURL}/${subSimpleClassName_strikeCase}/get-by-${subJoinColumn_strikeCase}?${subJoinColumn.javaField}=${${subJoinColumn.javaField}}`); +} +#end +#end +## 特殊:MASTER_ERP 时,支持单个的新增、修改、删除操作 +#if ( $table.templateType == 11 ) +/** 新增${subTable.classComment} */ +export function create${subSimpleClassName}(data: ${simpleClassName}Api.${subSimpleClassName}) { + return requestClient.post(`${baseURL}/${subSimpleClassName_strikeCase}/create`, data); +} - /** 修改${subTable.classComment} */ - export function update${subSimpleClassName}(data: ${simpleClassName}Api.${subSimpleClassName}) { - return requestClient.put(`${baseURL}/${subSimpleClassName_strikeCase}/update`, data); - } +/** 修改${subTable.classComment} */ +export function update${subSimpleClassName}(data: ${simpleClassName}Api.${subSimpleClassName}) { + return requestClient.put(`${baseURL}/${subSimpleClassName_strikeCase}/update`, data); +} - /** 删除${subTable.classComment} */ - export function delete${subSimpleClassName}(id: number) { - return requestClient.delete(`${baseURL}/${subSimpleClassName_strikeCase}/delete?id=${id}`); - } +/** 删除${subTable.classComment} */ +export function delete${subSimpleClassName}(id: number) { + return requestClient.delete(`${baseURL}/${subSimpleClassName_strikeCase}/delete?id=${id}`); +} - #if ($deleteBatchEnable) - /** 批量删除${subTable.classComment} */ - export function delete${subSimpleClassName}ListByIds(ids: number[]) { - return requestClient.delete(`${baseURL}/${subSimpleClassName_strikeCase}/delete-list?ids=${ids.join(',')}`) - } - #end - - /** 获得${subTable.classComment} */ - export function get${subSimpleClassName}(id: number) { - return requestClient.get<${simpleClassName}Api.${subSimpleClassName}>(`${baseURL}/${subSimpleClassName_strikeCase}/get?id=${id}`); - } - #end +#if ($deleteBatchEnable) +/** 批量删除${subTable.classComment} */ +export function delete${subSimpleClassName}List(ids: number[]) { + return requestClient.delete(`${baseURL}/${subSimpleClassName_strikeCase}/delete-list?ids=${ids.join(',')}`) +} +#end + +/** 获得${subTable.classComment} */ +export function get${subSimpleClassName}(id: number) { + return requestClient.get<${simpleClassName}Api.${subSimpleClassName}>(`${baseURL}/${subSimpleClassName_strikeCase}/get?id=${id}`); +} +#end #end diff --git a/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_antd/general/views/form.vue.vm b/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_antd/general/views/form.vue.vm index 90c0a6e9c..06dc0c86c 100644 --- a/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_antd/general/views/form.vue.vm +++ b/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/vue3_vben5_antd/general/views/form.vue.vm @@ -1,12 +1,15 @@