From 60eff99d978e557821ce9a73460db9d03deb5ed6 Mon Sep 17 00:00:00 2001
From: yangys <y_ys79@sina.com>
Date: 星期六, 23 八月 2025 19:25:39 +0800
Subject: [PATCH] 增加撤回功能

---
 blade-service/blade-mdm/src/main/java/org/springblade/mdm/flow/service/WIthdrawService.java                 |  101 ++++++++++++++++
 blade-service/blade-mdm/src/main/java/org/springblade/mdm/flow/service/WithdrawProperties.java              |   18 +++
 blade-service/blade-mdm/src/main/java/org/springblade/mdm/flow/controller/FlowMgrController.java            |   25 +++-
 blade-service/blade-mdm/src/main/java/org/springblade/mdm/flow/service/FlowableBackWithAssigneeService.java |  161 ++++++++++++++++++++++++++
 blade-service/blade-mdm/src/main/java/org/springblade/mdm/test/MyTest2Controller.java                       |    2 
 blade-service/blade-mdm/src/main/java/org/springblade/mdm/test/MyTestController.java                        |   13 ++
 6 files changed, 313 insertions(+), 7 deletions(-)

diff --git a/blade-service/blade-mdm/src/main/java/org/springblade/mdm/flow/controller/FlowMgrController.java b/blade-service/blade-mdm/src/main/java/org/springblade/mdm/flow/controller/FlowMgrController.java
index 1a1a0aa..69c095d 100644
--- a/blade-service/blade-mdm/src/main/java/org/springblade/mdm/flow/controller/FlowMgrController.java
+++ b/blade-service/blade-mdm/src/main/java/org/springblade/mdm/flow/controller/FlowMgrController.java
@@ -5,10 +5,10 @@
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.mail.MessagingException;
 import jakarta.servlet.http.HttpServletResponse;
 import lombok.extern.slf4j.Slf4j;
 import org.flowable.engine.HistoryService;
-import org.flowable.engine.RuntimeService;
 import org.flowable.engine.TaskService;
 import org.flowable.engine.task.Comment;
 import org.flowable.task.api.history.HistoricTaskInstance;
@@ -18,9 +18,9 @@
 import org.springblade.core.tool.api.R;
 import org.springblade.core.tool.utils.DateUtil;
 import org.springblade.core.tool.utils.Func;
-import org.springblade.mdm.flow.service.ApproveRecordService;
 import org.springblade.mdm.flow.service.ApproveTableService;
 import org.springblade.mdm.flow.service.FlowBusinessService;
+import org.springblade.mdm.flow.service.WIthdrawService;
 import org.springblade.mdm.flow.vo.FlowVO;
 import org.springblade.mdm.flow.vo.OvertimeTaskExcelVO;
 import org.springblade.mdm.flow.vo.TaskTraceVO;
@@ -31,6 +31,7 @@
 import org.springframework.format.annotation.DateTimeFormat;
 import org.springframework.web.bind.annotation.*;
 
+import java.io.IOException;
 import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
 import java.time.LocalDate;
@@ -51,13 +52,13 @@
 	@Autowired
 	private HistoryService historyService;
 	@Autowired
-	private RuntimeService runtimeService;
-	@Autowired
 	private ApproveTableService approveTableService;
 	@Autowired
-	private ApproveRecordService approveRecordService;
-	@Autowired
 	private IUserClient userClient;
+	@Autowired
+	private WIthdrawService withdrawService;
+
+
 	@GetMapping("overtime-list")
 	@ApiOperationSupport(order = 3)
 	@Operation(summary = "瓒呮椂鏌ヨ", description = "鏌ヨ鎵�鏈夎秴鏃朵换鍔�")
@@ -182,4 +183,16 @@
 		}
 
 	}
+
+	@GetMapping("/withdraw")
+	@Operation(summary = "鎾ゅ洖浠诲姟", description = "浠庡叾浠栦换鍔¤妭鐐圭洿鎺ユ挙鍥炵紪绋嬪憳鑺傜偣")
+	public R<Void> withdraw(String processInstanceId)  {
+		try {
+			withdrawService.withdraw(processInstanceId);
+			return R.success();
+		}catch(Exception e) {
+			log.error("<UNK>", e);
+			return R.fail(e.getMessage());
+		}
+	}
 }
diff --git a/blade-service/blade-mdm/src/main/java/org/springblade/mdm/flow/service/FlowableBackWithAssigneeService.java b/blade-service/blade-mdm/src/main/java/org/springblade/mdm/flow/service/FlowableBackWithAssigneeService.java
new file mode 100644
index 0000000..7ea120b
--- /dev/null
+++ b/blade-service/blade-mdm/src/main/java/org/springblade/mdm/flow/service/FlowableBackWithAssigneeService.java
@@ -0,0 +1,161 @@
+package org.springblade.mdm.flow.service;
+import org.flowable.engine.HistoryService;
+import org.flowable.engine.RuntimeService;
+import org.flowable.engine.TaskService;
+import org.flowable.engine.history.HistoricActivityInstance;
+import org.flowable.engine.runtime.Execution;
+import org.flowable.task.api.Task;
+import org.flowable.task.api.history.HistoricTaskInstance;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Map;
+@Service
+public class FlowableBackWithAssigneeService {
+	@Autowired
+	private RuntimeService runtimeService;
+
+	@Autowired
+	private TaskService taskService;
+
+	@Autowired
+	private HistoryService historyService;
+
+	/**
+	 * 灏嗘祦绋嬮��鍥炲埌鎸囧畾浠诲姟鑺傜偣骞舵寚瀹氭柊鐨勫鐞嗕汉
+	 * @param currentTaskId 褰撳墠浠诲姟ID
+	 * @param targetActivityId 鐩爣娲诲姩鑺傜偣ID
+	 * @param newAssignee 鏂扮殑澶勭悊浜�
+	 * @param comment 閫�鍥炴剰瑙�
+	 * @param variables 娴佺▼鍙橀噺
+	 */
+	@Transactional
+	public void backToTaskWithNewAssignee(String currentTaskId,
+										  String targetActivityId,
+										  String newAssignee,
+										  String comment,
+										  Map<String, Object> variables) {
+		// 鑾峰彇褰撳墠浠诲姟
+		Task currentTask = taskService.createTaskQuery()
+			.taskId(currentTaskId)
+			.singleResult();
+
+		if (currentTask == null) {
+			throw new RuntimeException("褰撳墠浠诲姟涓嶅瓨鍦�");
+		}
+
+		String processInstanceId = currentTask.getProcessInstanceId();
+
+		// 娣诲姞璇勮
+		if (comment != null && !comment.trim().isEmpty()) {
+			taskService.addComment(currentTaskId, processInstanceId, comment);
+		}
+
+		// 楠岃瘉鐩爣鑺傜偣鏄惁瀛樺湪
+		validateTargetActivity(processInstanceId, targetActivityId);
+
+		// 浣跨敤杩愯鏃舵湇鍔¤烦杞埌鐩爣鑺傜偣
+		runtimeService.createChangeActivityStateBuilder()
+			.processInstanceId(processInstanceId)
+			.moveActivityIdTo(currentTask.getTaskDefinitionKey(), targetActivityId)
+			.changeState();
+
+		// 鑾峰彇鏂板垱寤虹殑浠诲姟锛堥��鍥炲悗浜х敓鐨勪换鍔★級
+		Task newTask = taskService.createTaskQuery()
+			.processInstanceId(processInstanceId)
+			.taskDefinitionKey(targetActivityId)
+			.singleResult();
+
+		if (newTask != null) {
+			// 璁剧疆鏂扮殑澶勭悊浜�
+			if (newAssignee != null && !newAssignee.trim().isEmpty()) {
+				taskService.setAssignee(newTask.getId(), newAssignee);
+			}
+
+			// 璁剧疆娴佺▼鍙橀噺
+			if (variables != null && !variables.isEmpty()) {
+				taskService.setVariables(newTask.getId(), variables);
+			}
+		}
+
+		// 鍒犻櫎褰撳墠浠诲姟
+		taskService.deleteTask(currentTaskId, "閫�鍥炴祦绋嬪苟鎸囧畾鏂板鐞嗕汉: " + newAssignee);
+	}
+
+	/**
+	 * 閫�鍥炲苟淇濈暀鍘熷鐞嗕汉淇℃伅锛堣褰曞巻鍙诧級
+	 */
+	@Transactional
+	public void backToTaskWithOriginalAssignee(String currentTaskId,
+											   String targetActivityId,
+											   String comment) {
+		Task currentTask = taskService.createTaskQuery()
+			.taskId(currentTaskId)
+			.singleResult();
+
+		if (currentTask == null) {
+			throw new RuntimeException("褰撳墠浠诲姟涓嶅瓨鍦�");
+		}
+
+		String processInstanceId = currentTask.getProcessInstanceId();
+
+		// 鑾峰彇鐩爣鑺傜偣鐨勫巻鍙蹭换鍔′俊鎭�
+		HistoricTaskInstance historicTask = historyService.createHistoricTaskInstanceQuery()
+			.processInstanceId(processInstanceId)
+			.taskDefinitionKey(targetActivityId)
+			.orderByHistoricTaskInstanceEndTime().desc()
+			.list()
+			.stream()
+			.findFirst()
+			.orElse(null);
+
+		String originalAssignee = historicTask != null ? historicTask.getAssignee() : null;
+
+		// 鎵ц閫�鍥炴搷浣�
+		backToTaskWithNewAssignee(currentTaskId, targetActivityId, originalAssignee, comment, null);
+	}
+
+	/**
+	 * 楠岃瘉鐩爣娲诲姩鑺傜偣鏄惁瀛樺湪
+	 */
+	private void validateTargetActivity(String processInstanceId, String targetActivityId) {
+		List<HistoricActivityInstance> targetActivities = historyService
+			.createHistoricActivityInstanceQuery()
+			.processInstanceId(processInstanceId)
+			.activityId(targetActivityId)
+			.list();
+
+		if (targetActivities.isEmpty()) {
+			throw new RuntimeException("鐩爣鑺傜偣涓嶅瓨鍦ㄤ簬娴佺▼瀹炰緥涓�");
+		}
+	}
+
+	/**
+	 * 鑾峰彇鍙��鍥炵殑鑺傜偣鍒楄〃锛堢敤鎴蜂换鍔¤妭鐐癸級
+	 */
+	public List<HistoricActivityInstance> getBackableUserTasks(String processInstanceId) {
+		return historyService.createHistoricActivityInstanceQuery()
+			.processInstanceId(processInstanceId)
+			.activityType("userTask")
+			.orderByHistoricActivityInstanceStartTime().desc()
+			.list();
+	}
+
+	/**
+	 * 鑾峰彇鎸囧畾鑺傜偣鐨勫巻鍙插鐞嗕汉
+	 */
+	public String getHistoricAssignee(String processInstanceId, String activityId) {
+		HistoricTaskInstance historicTask = historyService.createHistoricTaskInstanceQuery()
+			.processInstanceId(processInstanceId)
+			.taskDefinitionKey(activityId)
+			.orderByHistoricTaskInstanceEndTime().desc()
+			.list()
+			.stream()
+			.findFirst()
+			.orElse(null);
+
+		return historicTask != null ? historicTask.getAssignee() : null;
+	}
+}
diff --git a/blade-service/blade-mdm/src/main/java/org/springblade/mdm/flow/service/WIthdrawService.java b/blade-service/blade-mdm/src/main/java/org/springblade/mdm/flow/service/WIthdrawService.java
new file mode 100644
index 0000000..85526e3
--- /dev/null
+++ b/blade-service/blade-mdm/src/main/java/org/springblade/mdm/flow/service/WIthdrawService.java
@@ -0,0 +1,101 @@
+package org.springblade.mdm.flow.service;
+
+import org.flowable.engine.RuntimeService;
+import org.flowable.engine.TaskService;
+import org.flowable.engine.repository.ProcessDefinition;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.task.api.Task;
+import org.springblade.core.secure.utils.AuthUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.flowable.engine.RepositoryService;
+import java.util.HashMap;
+import java.util.Optional;
+
+/**
+ * 娴佺▼鎾ゅ洖鏈嶅姟
+ */
+
+
+@Service
+public class WIthdrawService {
+	@Autowired
+	private WithdrawProperties withdrawProps;
+	@Autowired
+	private RuntimeService runtimeService;
+
+	@Autowired
+	private TaskService taskService;
+	@Autowired
+	private FlowableBackWithAssigneeService backWithAssigneeService;
+	@Autowired
+	private RepositoryService repositoryService;
+	/*
+	 * 鎾ゅ洖鍒扮紪绋嬪憳
+	 */
+	@Transactional
+	public void withdraw(String processInstanceId) {
+		//System.out.println(withdrawProps);
+
+		String targetAssignee = ""+AuthUtil.getUserId();
+
+		String processDefKey = getProcessDefinitionKeyByProcessInstanceId(processInstanceId);
+		String targetActivityId = this.withdrawProps.getMapping().get(processDefKey);
+		String taskId = getCurrentTaskIdByProcessInstanceId(processInstanceId);
+		backWithAssigneeService.backToTaskWithNewAssignee(taskId,targetActivityId,targetAssignee,"娴佺▼鎾ゅ洖",new HashMap<>());
+	}
+/*
+	String getprocessDefinitionKey(String processInstanceId){
+		Task task = taskService.createTaskQuery()
+			.taskId(taskId)
+			.singleResult();
+
+		ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
+			.processDefinitionId(task.getProcessDefinitionId())
+			.singleResult();
+
+		String taskId = getCurrentTaskIdByProcessInstanceId(processInstanceId);
+		if (processDefinition == null) {
+			throw new RuntimeException("娴佺▼瀹氫箟涓嶅瓨鍦紝processDefinitionId: " + task.getProcessDefinitionId());
+		}
+
+		return processDefinition.getKey();
+	}*/
+
+	public String getCurrentTaskIdByProcessInstanceId(String processInstanceId) {
+		Optional<String> optTaskId = taskService.createTaskQuery()
+			.processInstanceId(processInstanceId)
+			.active()
+			.list()
+			.stream()
+			.map(Task::getId).findFirst();
+
+
+		return optTaskId.orElse(null);
+	}
+
+	public String getProcessDefinitionKeyByProcessInstanceId(String processInstanceId) {
+		// 鑾峰彇娴佺▼瀹炰緥
+		ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
+			.processInstanceId(processInstanceId)
+			.singleResult();
+
+		if (processInstance == null) {
+			throw new RuntimeException("娴佺▼瀹炰緥涓嶅瓨鍦�: " + processInstanceId);
+		}
+
+		// 鑾峰彇娴佺▼瀹氫箟
+		ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
+			.processDefinitionId(processInstance.getProcessDefinitionId())
+			.singleResult();
+
+		if (processDefinition == null) {
+			throw new RuntimeException("娴佺▼瀹氫箟涓嶅瓨鍦�: " + processInstance.getProcessDefinitionId());
+		}
+
+		return processDefinition.getKey();
+	}
+}
+
+
diff --git a/blade-service/blade-mdm/src/main/java/org/springblade/mdm/flow/service/WithdrawProperties.java b/blade-service/blade-mdm/src/main/java/org/springblade/mdm/flow/service/WithdrawProperties.java
new file mode 100644
index 0000000..6dd3504
--- /dev/null
+++ b/blade-service/blade-mdm/src/main/java/org/springblade/mdm/flow/service/WithdrawProperties.java
@@ -0,0 +1,18 @@
+package org.springblade.mdm.flow.service;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Configuration
+@ConfigurationProperties(prefix = "withdraw")
+@Getter
+@Setter
+public class WithdrawProperties {
+	private Map<String, String> mapping = new HashMap<>();
+
+}
diff --git a/blade-service/blade-mdm/src/main/java/org/springblade/mdm/test/MyTest2Controller.java b/blade-service/blade-mdm/src/main/java/org/springblade/mdm/test/MyTest2Controller.java
index 98fcd3c..4ecbcc1 100644
--- a/blade-service/blade-mdm/src/main/java/org/springblade/mdm/test/MyTest2Controller.java
+++ b/blade-service/blade-mdm/src/main/java/org/springblade/mdm/test/MyTest2Controller.java
@@ -42,4 +42,6 @@
 		return this.bizDictClient.getList(code);
 	}
 
+
+
 }
diff --git a/blade-service/blade-mdm/src/main/java/org/springblade/mdm/test/MyTestController.java b/blade-service/blade-mdm/src/main/java/org/springblade/mdm/test/MyTestController.java
index 353a355..a5e317f 100644
--- a/blade-service/blade-mdm/src/main/java/org/springblade/mdm/test/MyTestController.java
+++ b/blade-service/blade-mdm/src/main/java/org/springblade/mdm/test/MyTestController.java
@@ -8,6 +8,7 @@
 import org.springblade.core.tool.api.R;
 import org.springblade.mdm.basesetting.machine.entity.Machine;
 import org.springblade.mdm.commons.service.EmailService;
+import org.springblade.mdm.flow.service.WIthdrawService;
 import org.springblade.mdm.gkw.programnode.entity.MachineFile;
 import org.springblade.mdm.program.entity.NcNode;
 import org.springblade.mdm.program.service.NcNodeService;
@@ -22,7 +23,6 @@
 import org.springframework.web.bind.annotation.RestController;
 
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.List;
@@ -44,6 +44,9 @@
 	private IDictBizClient bizDictClient;
 	@Autowired
 	private EmailService mailService;
+
+	@Autowired
+	private WIthdrawService withdrawService;
 	/**
 	 * 鏂板
 	 */
@@ -113,4 +116,12 @@
 		return R.success();
 	}
 
+	@GetMapping("/withdraw")
+	public R<Void> withdraw(String id) throws IOException, MessagingException {
+		withdrawService.withdraw(id);
+		return R.success();
+	}
+
+
+
 }

--
Gitblit v1.9.3