diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml
index 72d2a8f08a..07ab2b483a 100644
--- a/yudao-dependencies/pom.xml
+++ b/yudao-dependencies/pom.xml
@@ -75,8 +75,8 @@
2.30.14
1.16.7
1.4.0
- 2.1.0
- 1.9.5
+ 2.1.1
+ 2.1.0
4.7.7-20250808.182223
1.2.13
@@ -628,6 +628,10 @@
com.github.jsqlparser
jsqlparser
+
+ cn.hutool
+ hutool-core
+
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
index 399ca5a6e3..0f44eacbf4 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
@@ -34,7 +34,7 @@ import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
import cn.iocoder.yudao.module.ai.service.model.AiModelService;
import cn.iocoder.yudao.module.ai.service.model.AiToolService;
import cn.iocoder.yudao.module.ai.util.AiUtils;
-import cn.iocoder.yudao.module.infra.framework.file.core.utils.FileTypeUtils;
+import cn.iocoder.yudao.module.ai.util.FileTypeUtils;
import com.google.common.collect.Maps;
import io.modelcontextprotocol.client.McpSyncClient;
import jakarta.annotation.Resource;
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/util/FileTypeUtils.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/util/FileTypeUtils.java
new file mode 100644
index 0000000000..9c3b202c4f
--- /dev/null
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/util/FileTypeUtils.java
@@ -0,0 +1,37 @@
+package cn.iocoder.yudao.module.ai.util;
+
+import cn.hutool.core.util.StrUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.tika.Tika;
+
+/**
+ * 文件类型 Utils
+ *
+ * @author 芋道源码
+ */
+@Slf4j
+public class FileTypeUtils {
+
+ private static final Tika TIKA = new Tika();
+
+ /**
+ * 已知文件名,获取文件类型,在某些情况下比通过字节数组准确,例如使用 jar 文件时,通过名字更为准确
+ *
+ * @param name 文件名
+ * @return mineType 无法识别时会返回“application/octet-stream”
+ */
+ public static String getMineType(String name) {
+ return TIKA.detect(name);
+ }
+
+ /**
+ * 判断是否是图片
+ *
+ * @param mineType 类型
+ * @return 是否是图片
+ */
+ public static boolean isImage(String mineType) {
+ return StrUtil.startWith(mineType, "image/");
+ }
+
+}
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java
index 6ce6f65b82..162dd63d5a 100644
--- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java
@@ -19,6 +19,7 @@ public enum BpmReasonEnum {
CANCEL_PROCESS_INSTANCE_BY_START_USER("用户主动取消流程,原因:{}"), // 场景:用户主动取消流程
CANCEL_PROCESS_INSTANCE_BY_ADMIN("管理员【{}】取消流程,原因:{}"), // 场景:管理员取消流程
CANCEL_CHILD_PROCESS_INSTANCE_BY_MAIN_PROCESS("子流程自动取消,原因:主流程已取消"),
+ REJECT_CHILD_PROCESS("子流程审批不通过"),
// ========== 流程任务的独有原因 ==========
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
index 406cc444f0..42642202c9 100644
--- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
@@ -50,6 +50,7 @@ import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.repository.ProcessDefinition;
+import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.runtime.ProcessInstanceBuilder;
import org.flowable.task.api.Task;
@@ -69,6 +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.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ActivityNode;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
+import static cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum.REJECT_CHILD_PROCESS;
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.util.BpmnModelUtils.parseNodeType;
import static java.util.Arrays.asList;
@@ -949,6 +951,29 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
status);
}
+ // 1.3 如果子流程拒绝,设置其父流程也为拒绝状态,且结束父流程
+ // 相关问题链接:https://t.zsxq.com/kZhyb
+ if (Objects.equals(status, BpmProcessInstanceStatusEnum.REJECT.getStatus())
+ && StrUtil.isNotBlank(instance.getSuperExecutionId())) {
+ // 1.3.1 获取父流程实例 并标记为不通过
+ Execution execution = runtimeService.createExecutionQuery().executionId(instance.getSuperExecutionId()).singleResult();
+ ProcessInstance parentProcessInstance = getProcessInstance(execution.getProcessInstanceId());
+ updateProcessInstanceReject(parentProcessInstance, REJECT_CHILD_PROCESS.getReason());
+
+ // 1.3.2 结束父流程。需要在子流程结束事务提交后执行
+ TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
+
+ @Override
+ public void afterCompletion(int transactionStatus) {
+ // 回滚情况,直接返回
+ if (ObjectUtil.equal(transactionStatus, TransactionSynchronization.STATUS_ROLLED_BACK)) {
+ return;
+ }
+ taskService.moveTaskToEnd(parentProcessInstance.getId(),REJECT_CHILD_PROCESS.getReason());
+ }
+ });
+ }
+
// 2. 发送对应的消息通知
if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) {
messageService.sendMessageWhenProcessInstanceApprove(
@@ -996,17 +1021,18 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
if (ObjUtil.notEqual(instance.getName(), name)) {
runtimeService.setProcessInstanceName(instance.getProcessInstanceId(), name);
}
+
+ // 流程前置通知:需要在流程启动后(事务提交后),保证 variables 已设置
+ // 相关问题链接:https://t.zsxq.com/DF7Kq
+ if (ObjUtil.isNull(processDefinitionInfo.getProcessBeforeTriggerSetting())) {
+ return;
+ }
+ BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessBeforeTriggerSetting();
+ BpmHttpRequestUtils.executeBpmHttpRequest(instance,
+ setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse());
}
});
-
- // 流程前置通知
- if (ObjUtil.isNull(processDefinitionInfo.getProcessBeforeTriggerSetting())) {
- return;
- }
- BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessBeforeTriggerSetting();
- BpmHttpRequestUtils.executeBpmHttpRequest(instance,
- setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse());
}
}
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
index 52e0f69084..e0d2ce9e8d 100644
--- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
@@ -804,7 +804,8 @@ public class BpmTaskServiceImpl implements BpmTaskService {
.setTargetTaskDefinitionKey(returnTaskId).setReason(reqVO.getReason()));
return;
}
- // 3.2 情况二:直接结束,审批不通过
+
+ // 3.2 情况二: 标记流程为不通过并结束流程
processInstanceService.updateProcessInstanceReject(instance, reqVO.getReason()); // 标记不通过
moveTaskToEnd(task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason())); // 结束流程
}
@@ -986,6 +987,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
}
@Override
+ @Transactional(rollbackFor = Exception.class)
public void moveTaskToEnd(String processInstanceId, String reason) {
List taskList = getRunningTaskListByProcessInstanceId(processInstanceId, null, null);
if (CollUtil.isEmpty(taskList)) {
diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml
index 3cb4dda2b8..9f13b1f046 100644
--- a/yudao-server/src/main/resources/application.yaml
+++ b/yudao-server/src/main/resources/application.yaml
@@ -198,13 +198,13 @@ spring:
rerank: false # 是否开启“通义千问”的 Rerank 模型,填写 dashscope 开启
mcp:
server:
- enabled: true
+ enabled: false
name: yudao-mcp-server
version: 1.0.0
instructions: 一个 MCP 示例服务
sse-endpoint: /sse
client:
- enabled: true
+ enabled: false
name: mcp
sse:
connections: