From b9b52f0af9a221a3468e7c52445db2d734a312d7 Mon Sep 17 00:00:00 2001 From: hongqiwei <1143662660@qq.com> Date: Fri, 13 Nov 2026 18:18:24 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=A1=E6=A0=B8=E6=B5=81=E7=A8=8B=E6=92=A4?= =?UTF-8?q?=E5=9B=9E=E6=97=B6=E5=90=8C=E6=AD=A5=E6=9B=B4=E6=96=B0oa?= =?UTF-8?q?=E5=BE=85=E5=8A=9E=E7=8A=B6=E6=80=81=EF=BC=8C=E5=B7=B2=E5=8A=9E?= =?UTF-8?q?--=E3=80=8B=E5=BE=85=E5=8A=9E=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flow/controller/FlowCommonController.java | 38 ++--------- .../app/flow/service/FlowCommonService.java | 68 +++++++++++++++++++ .../app/flow/service/SchoolOaTodoService.java | 7 ++ .../service/impl/SchoolOaTodoServiceImpl.java | 65 ++++++++++++++++++ 4 files changed, 144 insertions(+), 34 deletions(-) diff --git a/src/main/java/com/budwk/app/flow/controller/FlowCommonController.java b/src/main/java/com/budwk/app/flow/controller/FlowCommonController.java index c5031ca..373966e 100644 --- a/src/main/java/com/budwk/app/flow/controller/FlowCommonController.java +++ b/src/main/java/com/budwk/app/flow/controller/FlowCommonController.java @@ -8,17 +8,13 @@ import com.budwk.app.base.result.Result; import com.budwk.app.base.vo.LabelValueVO; import com.budwk.app.flow.constant.FlowConst; import com.budwk.app.flow.engine.FlowEngine; -import com.budwk.app.flow.engine.event.ProcessEvent; -import com.budwk.app.flow.engine.event.ProcessPublisher; import com.budwk.app.flow.engine.model.ProcessModel; import com.budwk.app.flow.engine.model.TaskModel; import com.budwk.app.flow.entity.Candidate; import com.budwk.app.flow.entity.ProcessDefine; import com.budwk.app.flow.entity.ProcessInstance; import com.budwk.app.flow.entity.ProcessTask; -import com.budwk.app.flow.enums.ProcessEventTypeEnum; import com.budwk.app.flow.enums.ProcessInstanceStateEnum; -import com.budwk.app.flow.enums.ProcessTaskPerformTypeEnum; import com.budwk.app.flow.enums.ProcessTaskStateEnum; import com.budwk.app.flow.service.FlowCommonService; import com.budwk.app.flow.vo.HighLightVO; @@ -248,37 +244,11 @@ public class FlowCommonController { @Aop(TransAop.READ_COMMITTED) @ApiOperation("撤销任务") public Result revokeTask(@Param("taskId") Long taskId) { - // 自己任务 - ProcessTask selfTask = dao.fetch(ProcessTask.class, taskId); - selfTask.setTaskState(ProcessTaskStateEnum.DOING.getCode()); - - // 撤销任务 - List taskList = dao.query(ProcessTask.class, Cnd.where(ProcessTask::getTaskParentId, "=", taskId)); - for (ProcessTask task : taskList) { - task.setTaskState(ProcessTaskStateEnum.WITHDRAW.getCode()); - dao.update(task); + if (taskId == null) { + return Result.error("任务ID不能为空"); } - // 会签并行任务 撤销后续任务 - if (selfTask.getPerformType().equals(ProcessTaskPerformTypeEnum.COUNTERSIGN.getCode())) { - List doingTasks = dao.query(ProcessTask.class, Cnd.where(ProcessTask::getTaskState, "=", ProcessTaskStateEnum.DOING.getCode()) - .and(ProcessTask::getProcessInstanceId, "=", selfTask.getProcessInstanceId())); - for (ProcessTask doingTask : doingTasks) { - doingTask.setTaskState(ProcessTaskStateEnum.WITHDRAW.getCode()); - dao.update(doingTask); - } - } - - // 激活 - dao.update(selfTask); - - // 流程激活 - dao.update(ProcessInstance.class, Chain.make("state", ProcessInstanceStateEnum.DOING.getCode()),Cnd.where(ProcessInstance::getId, "=", selfTask.getProcessInstanceId())); - - // 发送任务撤回事件 确保上面执行成功 - for (ProcessTask task : taskList) { - ProcessPublisher.notify(ProcessEvent.builder().eventType(ProcessEventTypeEnum.PROCESS_TASK_REVOKE).sourceId(task.getId()).build()); - } - + // taskId 为原已完成任务 ID;服务同步 OA 后返回 Result 成功结果。 + flowCommonService.revokeTask(taskId); return Result.success(); } diff --git a/src/main/java/com/budwk/app/flow/service/FlowCommonService.java b/src/main/java/com/budwk/app/flow/service/FlowCommonService.java index d25c8dd..79e5131 100644 --- a/src/main/java/com/budwk/app/flow/service/FlowCommonService.java +++ b/src/main/java/com/budwk/app/flow/service/FlowCommonService.java @@ -1,5 +1,18 @@ package com.budwk.app.flow.service; +import com.budwk.app.base.exception.BaseException; +import com.budwk.app.flow.engine.event.ProcessEvent; +import com.budwk.app.flow.engine.event.ProcessPublisher; +import com.budwk.app.flow.entity.ProcessInstance; +import com.budwk.app.flow.entity.ProcessTask; +import com.budwk.app.flow.enums.ProcessEventTypeEnum; +import com.budwk.app.flow.enums.ProcessInstanceStateEnum; +import com.budwk.app.flow.enums.ProcessTaskPerformTypeEnum; +import com.budwk.app.flow.enums.ProcessTaskStateEnum; +import org.nutz.dao.Dao; +import org.nutz.dao.Cnd; +import org.nutz.dao.Chain; +import java.util.List; import cn.hutool.core.lang.Dict; import cn.hutool.core.util.ObjectUtil; import com.budwk.app.base.event.flow.jumptofirsttask.FlowJumpToFirstTaskNodePublisher; @@ -17,6 +30,10 @@ public class FlowCommonService { @Inject private FlowEngine flowEngine; + @Inject + private Dao dao; + @Inject + private SchoolOaTodoService schoolOaTodoService; /** * 执行 WF 任务,任务状态、业务监听器和后续任务写入共用本地事务;OA 异常向外抛出触发回滚。 @@ -75,4 +92,55 @@ public class FlowCommonService { } } + /** + * 撤回已完成的 WF 任务,恢复原 OA 待办并清理被撤销的后续待办。 + * @param taskId 原已完成任务 ID;沿用原任务及参与人,保持 OA uniqueId 不变 + * 返回 void;失败抛出异常回滚本地事务,外部 OA 请求不参与数据库事务。 + */ + @Aop(TransAop.READ_COMMITTED) + public void revokeTask(Long taskId) { + // 自己任务 + ProcessTask selfTask = dao.fetch(ProcessTask.class, taskId); + if (selfTask == null) { + throw new BaseException("撤回失败:任务不存在"); + } + // 防止重复撤回再次删除后续 OA 待办;原任务必须是已完成状态。 + if (!ProcessTaskStateEnum.FINISHED.getCode().equals(selfTask.getTaskState())) { + throw new BaseException("撤回失败:任务不是已完成状态,请刷新后重试"); + } + schoolOaTodoService.restoreTodo(selfTask); + + // 撤销任务 + List taskList = dao.query(ProcessTask.class, Cnd.where(ProcessTask::getTaskParentId, "=", taskId)); + for (ProcessTask task : taskList) { + // 在本地状态改变前删除 OA 待办,保留 deleteTodo 对历史任务的保护。 + schoolOaTodoService.deleteTodo(task); + task.setTaskState(ProcessTaskStateEnum.WITHDRAW.getCode()); + dao.update(task); + } + // 会签并行任务 撤销后续任务 + if (selfTask.getPerformType().equals(ProcessTaskPerformTypeEnum.COUNTERSIGN.getCode())) { + List doingTasks = dao.query(ProcessTask.class, Cnd.where(ProcessTask::getTaskState, "=", ProcessTaskStateEnum.DOING.getCode()) + .and(ProcessTask::getProcessInstanceId, "=", selfTask.getProcessInstanceId())); + for (ProcessTask doingTask : doingTasks) { + schoolOaTodoService.deleteTodo(doingTask); + doingTask.setTaskState(ProcessTaskStateEnum.WITHDRAW.getCode()); + dao.update(doingTask); + } + } + + // 激活 + selfTask.setTaskState(ProcessTaskStateEnum.DOING.getCode()); + dao.update(selfTask); + + // 流程激活 + dao.update(ProcessInstance.class, Chain.make("state", ProcessInstanceStateEnum.DOING.getCode()),Cnd.where(ProcessInstance::getId, "=", selfTask.getProcessInstanceId())); + + // 发送任务撤回事件 确保上面执行成功 + for (ProcessTask task : taskList) { + ProcessPublisher.notify(ProcessEvent.builder().eventType(ProcessEventTypeEnum.PROCESS_TASK_REVOKE).sourceId(task.getId()).build()); + } + + } + } diff --git a/src/main/java/com/budwk/app/flow/service/SchoolOaTodoService.java b/src/main/java/com/budwk/app/flow/service/SchoolOaTodoService.java index 5da6504..f3d76a2 100644 --- a/src/main/java/com/budwk/app/flow/service/SchoolOaTodoService.java +++ b/src/main/java/com/budwk/app/flow/service/SchoolOaTodoService.java @@ -22,6 +22,13 @@ public interface SchoolOaTodoService { */ void completeTodo(ProcessTask processTask); + /** + * 审核撤回时恢复原任务的 OA 待办,保留原任务和参与人组成的 uniqueId。 + * @param processTask 需要重新办理的原审核任务,包含任务及实例 ID、节点和表单地址 + * 返回 void;OA 返回非成功状态时抛出异常。 + */ + void restoreTodo(ProcessTask processTask); + /** * 根据本系统流程任务删除学校 OA 单条待办。 * diff --git a/src/main/java/com/budwk/app/flow/service/impl/SchoolOaTodoServiceImpl.java b/src/main/java/com/budwk/app/flow/service/impl/SchoolOaTodoServiceImpl.java index c300e60..804499c 100644 --- a/src/main/java/com/budwk/app/flow/service/impl/SchoolOaTodoServiceImpl.java +++ b/src/main/java/com/budwk/app/flow/service/impl/SchoolOaTodoServiceImpl.java @@ -158,6 +158,71 @@ public class SchoolOaTodoServiceImpl implements SchoolOaTodoService { } } + /** + * 使用修改信息接口把原已办恢复为待办;不能重复创建同一个 uniqueId。 + * @param processTask 被撤回的原审核任务,按实例读取发起人、按任务读取全部接收人 + * 返回 void;state=200 表示恢复成功,其他状态保持失败并向外抛出。 + */ + @Override + @Aop(TransAop.READ_COMMITTED) + public void restoreTodo(ProcessTask processTask) { + if (processTask == null || processTask.getId() == null) { + throw new BaseException("学校OA待办恢复失败:流程任务为空"); + } + if (!isSchoolOaEnabled("恢复待办", processTask.getId(), processTask.getProcessInstanceId())) { + return; + } + // 发起节点原本不推送 OA,因此撤回申请时也不恢复该节点的 OA 记录。 + if (Long.valueOf(0L).equals(processTask.getTaskParentId()) && "startTask".equals(processTask.getTaskName())) { + return; + } + ProcessInstance instance = dao.fetch(ProcessInstance.class, processTask.getProcessInstanceId()); + if (instance == null) { + throw new BaseException("学校OA待办恢复失败:流程实例不存在"); + } + ProcessDefine define = dao.fetch(ProcessDefine.class, instance.getProcessDefineId()); + Sys_user creator = dao.fetch(Sys_user.class, instance.getOperator()); + if (define == null || creator == null || StrUtil.hasBlank(creator.getLoginname(), creator.getUsername())) { + throw new BaseException("学校OA待办恢复失败:流程定义或发起人信息不完整"); + } + List actors = dao.query(ProcessTaskActor.class, + Cnd.where(ProcessTaskActor::getProcessTaskId, "=", processTask.getId())); + if (actors.isEmpty()) { + throw new BaseException("学校OA待办恢复失败:流程任务接收人为空,taskId={}", processTask.getId()); + } + for (ProcessTaskActor actor : actors) { + if (skipSuperAdmin(actor, "恢复待办", processTask.getId())) { + continue; + } + if (StrUtil.hasBlank(actor.getActorId(), actor.getActorAccount(), actor.getActorName())) { + throw new BaseException("学校OA待办恢复失败:接收人信息不完整,taskId={}", processTask.getId()); + } + String uniqueId = processTask.getId() + "_" + actor.getActorId(); + JSONObject body = new JSONObject(); + body.set("flowTypeName", getFlowTypeName(define)); + body.set("flowId", String.valueOf(instance.getId())); + body.set("title", getInstanceTitle(instance, define)); + body.set("stepName", processTask.getDisplayName()); + body.set("creatorCode", creator.getLoginname()); + body.set("creatorName", creator.getUsername()); + body.set("creatorTime", formatTime(instance.getCreatedAt())); + String pcUrl = buildTaskUrl(processTask.getFormKey(), processTask); + String appUrl = buildAppTaskUrl(processTask.getH5FormKey(), processTask); + body.set("pcUrl", pcUrl); + body.set("appUrl", appUrl); + body.set("pcPreviewUrl", pcUrl); + body.set("appPreviewUrl", appUrl); + body.set("uniqueId", uniqueId); + body.set("receiveCode", actor.getActorAccount()); + body.set("receiveName", actor.getActorName()); + body.set("receiveTime", formatTime(processTask.getCreatedAt())); + body.set("isRemark", "0"); + String url = requireConfig("school-oa.todo.update-handle-url").replace("{uniqueId}", uniqueId); + String response = sendTodoRequest(url, body, "学校OA待办恢复失败", "学校OA待办恢复异常"); + log.info("学校OA待办恢复成功,taskId={},uniqueId={},response={}", processTask.getId(), uniqueId, response); + } + } + @Override @Aop(TransAop.READ_COMMITTED) public void deleteFlow(Long flowId) {