From 9bd9b69e4ccbf9f29b7b229c7977d6b58ff749a2 Mon Sep 17 00:00:00 2001 From: fangjiakai <450850793@qq.com> Date: Tue, 31 Mar 2026 11:05:05 +0800 Subject: [PATCH] =?UTF-8?q?feat(workflow):=20=E6=96=B0=E5=A2=9E=E5=BC=BA?= =?UTF-8?q?=E5=88=B6=E7=BB=88=E6=AD=A2=E5=B7=A5=E4=BD=9C=E6=B5=81=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=B9=B6=E4=BC=98=E5=8C=96=E5=8F=97=E9=99=90=E7=A9=BA?= =?UTF-8?q?=E9=97=B4=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 forceTerminate 接口用于强制终止工作流 - 实现多人签字步骤的支持和分支流程图构建 - 优化 TaskFlowChartQueryExe 中的分支步骤处理逻辑 - 更新 TaskLogUpdateExe 中的签字步骤处理机制 - 添加 ConfinedSpace 的主管部门字段管理 - 重构 TaskLogAddExe 中的多人签字步骤创建逻辑 - 优化工作流跳过状态处理逻辑 - 添加更多日志输出便于调试跟踪 --- .../web/EightworkInfoController.java | 7 + .../eightwork/web/TaskLogController.java | 4 + .../command/ConfinedSpaceAddExe.java | 10 +- .../eightwork/command/TaskLogAddExe.java | 64 ++++++- .../eightwork/command/TaskLogUpdateExe.java | 175 ++++++++++++++---- .../command/query/TaskFlowChartQueryExe.java | 112 +++++++++-- .../service/EightworkInfoServiceImpl.java | 63 +++++++ .../eightwork/service/TaskLogServiceImpl.java | 2 +- .../eightwork/api/EightworkInfoServiceI.java | 7 + .../eightwork/dto/ConfinedSpaceAddCmd.java | 10 +- .../eightwork/dto/ConfinedSpaceUpdateCmd.java | 11 +- .../eightwork/dto/EightworkInfoPageQry.java | 7 + .../dto/clientobject/ConfinedSpaceCO.java | 10 +- .../dto/clientobject/MeasuresLogsCO.java | 3 + .../dto/clientobject/TaskFlowChartCO.java | 5 +- .../domain/model/ConfinedSpaceE.java | 8 +- .../dataobject/ConfinedSpaceDO.java | 11 +- .../resources/mapper/ConfinedSpaceMapper.xml | 8 + .../resources/mapper/EightworkInfoMapper.xml | 6 + 19 files changed, 440 insertions(+), 83 deletions(-) create mode 100644 web-infrastructure/src/main/resources/mapper/ConfinedSpaceMapper.xml diff --git a/web-adapter/src/main/java/com/zcloud/eightwork/web/EightworkInfoController.java b/web-adapter/src/main/java/com/zcloud/eightwork/web/EightworkInfoController.java index f07b42d..11f2ea9 100644 --- a/web-adapter/src/main/java/com/zcloud/eightwork/web/EightworkInfoController.java +++ b/web-adapter/src/main/java/com/zcloud/eightwork/web/EightworkInfoController.java @@ -80,5 +80,12 @@ public class EightworkInfoController { eightworkInfoService.edit(eightworkInfoUpdateCmd); return SingleResponse.buildSuccess(); } + + @ApiOperation("强制终止工作流") + @PostMapping("/forceTerminate/{id}") + public Response forceTerminate(@PathVariable("id") Long id) { + eightworkInfoService.forceTerminate(id); + return SingleResponse.buildSuccess(); + } } diff --git a/web-adapter/src/main/java/com/zcloud/eightwork/web/TaskLogController.java b/web-adapter/src/main/java/com/zcloud/eightwork/web/TaskLogController.java index a4a83dc..0e5da91 100644 --- a/web-adapter/src/main/java/com/zcloud/eightwork/web/TaskLogController.java +++ b/web-adapter/src/main/java/com/zcloud/eightwork/web/TaskLogController.java @@ -1,6 +1,7 @@ package com.zcloud.eightwork.web; +import cn.hutool.core.util.ObjectUtil; import com.zcloud.eightwork.api.TaskLogServiceI; import com.zcloud.eightwork.dto.EightworkInfoSaveDraftCmd; import com.zcloud.eightwork.dto.TaskLogAddCmd; @@ -46,6 +47,9 @@ public class TaskLogController { public SingleResponse add(@Validated @RequestBody TaskLogAddCmd cmd) { cmd.setDepartmentId(AuthContext.getOrgId()); cmd.setDepartmentName(AuthContext.getCurrentUser().getOrgName()); + if(ObjectUtil.isEmpty(cmd.getCorpinfoId())){ + cmd.setCorpinfoId(AuthContext.getTenantId()); + } return taskLogService.add(cmd); } diff --git a/web-app/src/main/java/com/zcloud/eightwork/command/ConfinedSpaceAddExe.java b/web-app/src/main/java/com/zcloud/eightwork/command/ConfinedSpaceAddExe.java index 2cb01b3..1c8e043 100644 --- a/web-app/src/main/java/com/zcloud/eightwork/command/ConfinedSpaceAddExe.java +++ b/web-app/src/main/java/com/zcloud/eightwork/command/ConfinedSpaceAddExe.java @@ -25,15 +25,7 @@ public class ConfinedSpaceAddExe { public boolean execute(ConfinedSpaceAddCmd cmd) { ConfinedSpaceE confinedSpaceE = new ConfinedSpaceE(); BeanUtils.copyProperties(cmd, confinedSpaceE); - boolean res = false; - try { - res = confinedSpaceGateway.add(confinedSpaceE); - } catch (Exception e) { - throw new RuntimeException(e); - } - if (!res) { - throw new BizException("保存失败"); - } + confinedSpaceGateway.add(confinedSpaceE); return true; } } diff --git a/web-app/src/main/java/com/zcloud/eightwork/command/TaskLogAddExe.java b/web-app/src/main/java/com/zcloud/eightwork/command/TaskLogAddExe.java index 0fa0603..3567af9 100644 --- a/web-app/src/main/java/com/zcloud/eightwork/command/TaskLogAddExe.java +++ b/web-app/src/main/java/com/zcloud/eightwork/command/TaskLogAddExe.java @@ -26,6 +26,7 @@ import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -125,9 +126,9 @@ public class TaskLogAddExe { // 6. 获取流程配置 List flows = taskFlowGateway.listAllByWorkType(cmd.getWorkType(), cmd.getWorkLevel()); - // 7. 生成所有步骤 task_log + // 7. 生成所有步骤 task_log(多人签字步骤会生成多条记录) List taskLogs = flows.stream() - .map(flow -> createTaskLog(flow, existingInfo.getWorkId(), cmd)) + .flatMap(flow -> createTaskLogs(flow, existingInfo.getWorkId(), cmd).stream()) .collect(Collectors.toList()); // 8. 设置所有步骤的 checkNo(使用主表的票号) @@ -162,8 +163,10 @@ public class TaskLogAddExe { List flows = taskFlowGateway.listAllByWorkType(cmd.getWorkType(), cmd.getWorkLevel()); log.info("获取到 {} 个流程步骤", flows.size()); - // 3. 生成任务日志 - List taskLogs = flows.stream().map(flow -> createTaskLog(flow, workId, cmd)).collect(Collectors.toList()); + // 3. 生成任务日志(多人签字步骤会生成多条记录) + List taskLogs = flows.stream() + .flatMap(flow -> createTaskLogs(flow, workId, cmd).stream()) + .collect(Collectors.toList()); // 4. 设置所有步骤的 checkNo for (TaskLogDO taskLog : taskLogs) { @@ -202,8 +205,40 @@ public class TaskLogAddExe { /** * 创建任务日志记录 + * 支持多人签字步骤,为每个签字人创建独立的记录 */ - private TaskLogDO createTaskLog(TaskFlowE flow, String workId, TaskLogAddCmd cmd) { + private List createTaskLogs(TaskFlowE flow, String workId, TaskLogAddCmd cmd) { + List result = new ArrayList<>(); + + // 判断是否为多人签字步骤 + if (Integer.valueOf(1).equals(flow.getMultipleFlag())) { + // 多人签字步骤:为每个签字人创建独立的记录 + List signInfos = findSignInfos(cmd, flow.getStepId()); + + if (signInfos.isEmpty()) { + // 没有预设签字人,创建一条空记录(等待后续设置) + result.add(createSingleTaskLog(flow, workId, cmd, null)); + log.info("多人签字步骤未预设签字人,创建空记录: stepName={}", flow.getStepName()); + } else { + // 为每个签字人创建独立的记录 + for (TaskSignStepInfoCmd signInfo : signInfos) { + result.add(createSingleTaskLog(flow, workId, cmd, signInfo)); + } + log.info("多人签字步骤已创建 {} 条记录: stepName={}", result.size(), flow.getStepName()); + } + } else { + // 单人签字步骤:只创建一条记录 + TaskSignStepInfoCmd signInfo = findSignInfo(cmd, flow.getStepId()); + result.add(createSingleTaskLog(flow, workId, cmd, signInfo)); + } + + return result; + } + + /** + * 创建单条任务日志记录 + */ + private TaskLogDO createSingleTaskLog(TaskFlowE flow, String workId, TaskLogAddCmd cmd, TaskSignStepInfoCmd signInfo) { TaskLogDO taskLogDO = new TaskLogDO(Tools.get32UUID()); BeanUtils.copyProperties(flow, taskLogDO, "id"); taskLogDO.setWorkId(workId); @@ -217,8 +252,8 @@ public class TaskLogAddExe { taskLogDO.setCurrentFillTimes(0); } - // 第一步自动设置为当前申请人 - if (FIRST_STEP_ID.equals(flow.getStepId())) { + // 第一步自动设置为当前申请人(仅当是单人签字且没有预设签字人时) + if (FIRST_STEP_ID.equals(flow.getStepId()) && signInfo == null) { taskLogDO.setSign( flow.getActorField(), AuthContext.getOrgId(), @@ -230,7 +265,6 @@ public class TaskLogAddExe { } // 处理用户预设的签字人 - TaskSignStepInfoCmd signInfo = findSignInfo(cmd, flow.getStepId()); if (signInfo != null) { handlePredefinedSigner(taskLogDO, flow, signInfo, cmd); } @@ -239,7 +273,7 @@ public class TaskLogAddExe { } /** - * 查找用户预设的签字人信息 + * 查找用户预设的签字人信息(单人签字) */ private TaskSignStepInfoCmd findSignInfo(TaskLogAddCmd cmd, Long stepId) { if (cmd.getSignLogs() == null) { @@ -251,6 +285,18 @@ public class TaskLogAddExe { .orElse(null); } + /** + * 查找用户预设的签字人信息列表(多人签字) + */ + private List findSignInfos(TaskLogAddCmd cmd, Long stepId) { + if (cmd.getSignLogs() == null) { + return new ArrayList<>(); + } + return cmd.getSignLogs().stream() + .filter(info -> info.getStepId().equals(stepId)) + .collect(Collectors.toList()); + } + /** * 处理用户预设的签字人 */ diff --git a/web-app/src/main/java/com/zcloud/eightwork/command/TaskLogUpdateExe.java b/web-app/src/main/java/com/zcloud/eightwork/command/TaskLogUpdateExe.java index e4aebae..ec8919d 100644 --- a/web-app/src/main/java/com/zcloud/eightwork/command/TaskLogUpdateExe.java +++ b/web-app/src/main/java/com/zcloud/eightwork/command/TaskLogUpdateExe.java @@ -40,6 +40,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; @@ -363,13 +364,13 @@ public class TaskLogUpdateExe { } } - if(ObjectUtil.isNotEmpty(cmd.getOthers().get("measuresType")) && cmd.getOthers().get("measuresType").equals("2")){ + if (ObjectUtil.isNotEmpty(cmd.getOthers().get("measuresType")) && cmd.getOthers().get("measuresType").equals("2")) { measuresLogsRepository.updateBatchById(measuresList.stream().map(item -> { MeasuresLogsDO d = new MeasuresLogsDO(); BeanUtils.copyProperties(item, d); return d; }).collect(Collectors.toList())); - }else { + } else { measuresLogsRepository.saveBatch(measuresList.stream().map(item -> { MeasuresLogsDO d = new MeasuresLogsDO(); BeanUtils.copyProperties(item, d, "id"); @@ -379,7 +380,6 @@ public class TaskLogUpdateExe { } - return true; } @@ -685,20 +685,9 @@ public class TaskLogUpdateExe { List allLogs = taskLogGateway.listAllByWorkId(cmd.getWorkId()); List actionLogs = new ArrayList<>(); - // 查找当前步骤并标记为已通过 - TaskLogE currentLog = findCurrentLog(allLogs, cmd.getId()); - if (currentLog != null) { - currentLog.setStatus(TaskLogStatus.APPROVED.getCode()); - if (SIGN_STEP_FLAG.equals(currentLog.getSignStepFlag())) { - currentLog.setSignPath(cmd.getSignPath()); - } - addActionLog(actionLogs, currentLog); - sendTodoCompleteEvent(currentLog.getId()); - } - - // 将所有未开始的步骤设置为跳过状态 + // 将所有未完成的步骤设置为跳过状态 for (TaskLogE log : allLogs) { - if (TaskLogStatus.NOT_STARTED.equalsCode(log.getStatus())) { + if (TaskLogStatus.NOT_STARTED.equalsCode(log.getStatus()) || TaskLogStatus.IN_PROGRESS.equalsCode(log.getStatus())) { log.setStatus(TaskLogStatus.SKIPPED.getCode()); addActionLog(actionLogs, log); } @@ -975,6 +964,7 @@ public class TaskLogUpdateExe { /** * 设置后续签字人 * 处理当前步骤设置的签字人信息 + * 支持多人签字步骤:如果多人签字步骤的记录数少于传入的签字人数,会自动创建新记录 */ private void handleSignStepsIfNeeded(TaskLogE currentLog, TaskLogNextCmd cmd, List actionLogs, List allLogs) { @@ -983,38 +973,153 @@ public class TaskLogUpdateExe { return; } - for (TaskSignStepInfoCmd signInfo : signLogs) { - TaskLogE signStepLog = allLogs.stream() - .filter(log -> log.getStepId().equals(signInfo.getStepId())) - .findFirst() - .orElse(null); + // 按 stepId 分组,处理多人签字步骤 + Map> signLogsByStepId = signLogs.stream() + .collect(Collectors.groupingBy(TaskSignStepInfoCmd::getStepId)); - if (signStepLog == null) { + for (Map.Entry> entry : signLogsByStepId.entrySet()) { + Long stepId = entry.getKey(); + List stepSignInfos = entry.getValue(); + + // 获取该步骤的所有现有记录 + List existingLogs = allLogs.stream() + .filter(log -> log.getStepId().equals(stepId)) + .collect(Collectors.toList()); + + if (existingLogs.isEmpty()) { + log.warn("未找到步骤记录,跳过设置签字人: stepId={}", stepId); continue; } - // 检查是否需要跳过 - if (CAN_SKIP_FLAG.equals(signStepLog.getCanSkip()) && signInfo.getActUser() == null) { - // 设置为跳过状态 - signStepLog.setStatus(TaskLogStatus.SKIPPED.getCode()); - addActionLog(actionLogs, signStepLog); - log.info("步骤设置为跳过: {}", signStepLog.getStepName()); + TaskLogE firstLog = existingLogs.get(0); + + // 判断是否为多人签字步骤 + boolean isMultipleSign = MULTIPLE_FLAG.equals(firstLog.getMultipleFlag()); + + if (isMultipleSign) { + // 多人签字步骤:需要确保记录数等于签字人数 + handleMultipleSignStep(firstLog, existingLogs, stepSignInfos, actionLogs, allLogs); } else { - // 设置签字人 - signStepLog.setSign( + // 单人签字步骤:只处理第一个签字人 + if (!stepSignInfos.isEmpty()) { + handleSingleSignStep(firstLog, stepSignInfos.get(0), actionLogs); + } + } + } + } + + /** + * 处理单人签字步骤 + */ + private void handleSingleSignStep(TaskLogE signStepLog, TaskSignStepInfoCmd signInfo, List actionLogs) { + // 检查是否需要跳过 + if (CAN_SKIP_FLAG.equals(signStepLog.getCanSkip()) && signInfo.getActUser() == null) { + signStepLog.setStatus(TaskLogStatus.SKIPPED.getCode()); + addActionLog(actionLogs, signStepLog); + log.info("步骤设置为跳过: {}", signStepLog.getStepName()); + } else { + signStepLog.setSign( + signInfo.getActorField(), + signInfo.getActUserDepartment(), + signInfo.getActUserDepartmentName(), + signInfo.getActUser(), + signInfo.getActUserName() + ); + addActionLog(actionLogs, signStepLog); + log.info("已设置签字人: step={}, user={}", signStepLog.getStepName(), signInfo.getActUserName()); + } + } + + /** + * 处理多人签字步骤 + * 如果现有记录数少于签字人数,自动创建新记录 + */ + private void handleMultipleSignStep(TaskLogE firstLog, List existingLogs, + List signInfos, + List actionLogs, List allLogs) { + int existingCount = existingLogs.size(); + int requiredCount = signInfos.size(); + + log.info("多人签字步骤: stepName={}, 现有记录数={}, 需要签字人数={}", firstLog.getStepName(), existingCount, requiredCount); + + // 如果现有记录数少于需要数,创建新记录 + if (existingCount < requiredCount) { + int newCount = requiredCount - existingCount; + log.info("需要为多人签字步骤创建 {} 条新记录: stepName={}", newCount, firstLog.getStepName()); + + for (int i = existingCount; i < requiredCount; i++) { + TaskSignStepInfoCmd signInfo = signInfos.get(i); + TaskLogE newLog = createNewTaskLogForMultipleSign(firstLog, signInfo); + allLogs.add(newLog); // 添加到 allLogs,供后续使用 + addActionLog(actionLogs, newLog); // 使用 addActionLog 添加到 actionLogs + + log.info("已创建新的多人签字记录: stepName={}, userName={}", firstLog.getStepName(), signInfo.getActUserName()); + } + } + + // 重新获取所有记录(包括新创建的) + List allLogsForStep = allLogs.stream() + .filter(log -> log.getStepId().equals(firstLog.getStepId())) + .collect(Collectors.toList()); + + // 为每个记录设置签字人 + for (int i = 0; i < signInfos.size() && i < allLogsForStep.size(); i++) { + TaskSignStepInfoCmd signInfo = signInfos.get(i); + TaskLogE taskLogE = allLogsForStep.get(i); + + if (CAN_SKIP_FLAG.equals(taskLogE.getCanSkip()) && signInfo.getActUser() == null) { + taskLogE.setStatus(TaskLogStatus.SKIPPED.getCode()); + addActionLog(actionLogs, taskLogE); + log.info("多人签字步骤记录设置为跳过: stepName={}, recordId={}", taskLogE.getStepName(), taskLogE.getId()); + } else { + taskLogE.setSign( signInfo.getActorField(), signInfo.getActUserDepartment(), signInfo.getActUserDepartmentName(), signInfo.getActUser(), signInfo.getActUserName() ); - - addActionLog(actionLogs, signStepLog); - log.info("已设置签字人: step={}, user={}", signStepLog.getStepName(), signInfo.getActUserName()); + addActionLog(actionLogs, taskLogE); + log.info("已设置多人签字人: stepName={}, userName={}", taskLogE.getStepName(), signInfo.getActUserName()); } } } + /** + * 为多人签字步骤创建新的记录 + */ + private TaskLogE createNewTaskLogForMultipleSign(TaskLogE template, TaskSignStepInfoCmd signInfo) { + // 将模板 E 转换为 DO + TaskLogDO templateDO = TaskLogConvertUtil.toDO(template); + + // 创建新的 DO + TaskLogDO newLogDO = new TaskLogDO(); + BeanUtils.copyProperties(templateDO, newLogDO, "id", "taskLogId"); + + // 生成新的业务主键 ID + newLogDO.setTaskLogId(Tools.get32UUID()); + + // 设置初始状态为未开始 + newLogDO.setStatus(TaskLogStatus.NOT_STARTED.getCode()); + + // 清空签字相关字段 + newLogDO.setActUser(null); + newLogDO.setActUserName(null); + newLogDO.setSignPath(null); + + // 保存到数据库(获取自增 ID) + taskLogRepository.save(newLogDO); + + // 创建对应的 E 对象 + TaskLogE newLogE = new TaskLogE(); + BeanUtils.copyProperties(newLogDO, newLogE); + + log.info("创建多人签字新记录: stepId={}, stepName={}, newId={}", + newLogE.getStepId(), newLogE.getStepName(), newLogE.getId()); + + return newLogE; + } + /** * 发送待办完成事件 */ @@ -1034,7 +1139,7 @@ public class TaskLogUpdateExe { private void sendTodoAddEvent(Long workId, TaskLogE nextStep, String workType) { try { TodoListAddEvent event = new TodoListAddEvent(); - event.setTitle("您有一条" + WorkCodeEnum.getNameByWorkType(workType) + "流程待处理"); + event.setTitle("您有一条【" + WorkCodeEnum.getNameByWorkType(workType) + "】流程待处理"); event.setContent(nextStep.getNextStepName()); event.setForeignKey(workId); event.setForeignSubsidiaryKey(nextStep.getId()); @@ -1073,7 +1178,7 @@ public class TaskLogUpdateExe { // 查找是否已存在该步骤的记录 for (int i = 0; i < actionLogs.size(); i++) { TaskLogDO existing = actionLogs.get(i); - if (existing.getId().equals(newDO.getId())) { + if (ObjectUtil.isNotEmpty(existing.getId()) && existing.getId().equals(newDO.getId())) { // 存在则合并:保留所有非空字段 mergeTaskLogDO(existing, newDO); log.debug("合并步骤修改: stepId={}, stepName={}", taskLogE.getStepId(), taskLogE.getStepName()); diff --git a/web-app/src/main/java/com/zcloud/eightwork/command/query/TaskFlowChartQueryExe.java b/web-app/src/main/java/com/zcloud/eightwork/command/query/TaskFlowChartQueryExe.java index 5268507..6232131 100644 --- a/web-app/src/main/java/com/zcloud/eightwork/command/query/TaskFlowChartQueryExe.java +++ b/web-app/src/main/java/com/zcloud/eightwork/command/query/TaskFlowChartQueryExe.java @@ -1,7 +1,6 @@ package com.zcloud.eightwork.command.query; import com.zcloud.eightwork.domain.gateway.TaskLogGateway; -import com.zcloud.eightwork.domain.model.TaskFlowE; import com.zcloud.eightwork.domain.model.TaskLogE; import com.zcloud.eightwork.domain.model.enums.BranchFlag; import com.zcloud.eightwork.domain.model.enums.StepType; @@ -11,7 +10,6 @@ import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -41,11 +39,13 @@ public class TaskFlowChartQueryExe { // 1. 查询所有步骤 List allLogs = taskLogGateway.listAllByWorkId(workId); + log.info("查询到所有步骤数量: {}", allLogs.size()); // 2. 过滤 SKIPPED 状态的步骤 List filteredLogs = allLogs.stream() .filter(log -> !TaskLogStatus.SKIPPED.equalsCode(log.getStatus())) .collect(Collectors.toList()); + log.info("过滤SKIPPED后步骤数量: {}", filteredLogs.size()); // 3. 区分主流程步骤和分支步骤 List mainSteps = filteredLogs.stream() @@ -55,24 +55,36 @@ public class TaskFlowChartQueryExe { List branchSteps = filteredLogs.stream() .filter(log -> !isMainStep(log)) .collect(Collectors.toList()); + log.info("主流程步骤数量: {}, 分支步骤数量: {}", mainSteps.size(), branchSteps.size()); - // 4. 按 branchStep 分组分支步骤 - Map> branchGroupMap = branchSteps.stream() - .collect(Collectors.groupingBy(TaskLogE::getBranchStep)); + // 输出分支步骤详情 + for (TaskLogE branch : branchSteps) { + log.info("分支步骤: stepId={}, stepName={}, branchFlag={}, branchStep={}, nextStep={}, branchMergeStep={}", + branch.getStepId(), branch.getStepName(), branch.getBranchFlag(), + branch.getBranchStep(), branch.getNextStep(), branch.getBranchMergeStep()); + } - // 5. 构建流程图 + // 4. 构建流程图 List result = new ArrayList<>(); for (TaskLogE mainStep : mainSteps) { + log.info("处理主流程步骤: stepId={}, stepName={}, branchFlag={}, branchStep={}", + mainStep.getStepId(), mainStep.getStepName(), mainStep.getBranchFlag(), mainStep.getBranchStep()); TaskFlowChartCO mainCO = convertToCO(mainStep); // 如果该主流程步骤开启了分支,添加分支信息 if (BranchFlag.getByCode(mainStep.getBranchFlag()).isBranchStart()) { - List branches = branchGroupMap.get(mainStep.getStepId()); - if (branches != null && !branches.isEmpty()) { - // 按步骤ID排序,确保分支内顺序正确 - branches.sort((a, b) -> a.getStepOrder().compareTo(b.getStepOrder())); - List branchCOs = convertBranches(branches); - mainCO.setBranches(branchCOs); + // 获取分支第一个节点 + Long firstBranchStepId = mainStep.getBranchStep(); + log.info("开启分支,分支第一个节点ID: {}", firstBranchStepId); + if (firstBranchStepId != null) { + // 从分支第一个节点开始,通过 nextStep 遍历整个分支链 + List branchChain = buildBranchChain(firstBranchStepId, branchSteps); + log.info("构建分支链完成,节点数量: {}", branchChain.size()); + if (!branchChain.isEmpty()) { + mainCO.setBranches(branchChain); + } + } else { + log.info("branchStep 为 null,无法获取分支"); } } @@ -114,6 +126,75 @@ public class TaskFlowChartQueryExe { return result; } + /** + * 构建分支链条 + * 从分支第一个节点开始,通过 nextStep 遍历整个分支链 + * + * @param firstBranchStepId 分支第一个节点的 stepId + * @param branchSteps 所有分支步骤列表 + * @return 分支链条的 CO 列表 + */ + private List buildBranchChain(Long firstBranchStepId, List branchSteps) { + List result = new ArrayList<>(); + log.info("开始构建分支链,第一个节点ID: {}", firstBranchStepId); + + // 查找第一个节点 + TaskLogE currentNode = branchSteps.stream() + .filter(log -> log.getStepId().equals(firstBranchStepId)) + .findFirst() + .orElse(null); + + if (currentNode == null) { + log.info("未找到第一个节点,stepId={}", firstBranchStepId); + return result; + } + + log.info("找到第一个节点: stepId={}, stepName={}, nextStep={}", + currentNode.getStepId(), currentNode.getStepName(), currentNode.getNextStep()); + + // 遍历整个分支链 + int chainCount = 0; + while (currentNode != null) { + chainCount++; + log.info("处理第{}个节点: stepId={}, stepName={}, nextStep={}", + chainCount, currentNode.getStepId(), currentNode.getStepName(), currentNode.getNextStep()); + + TaskFlowChartCO co = convertToCO(currentNode); + + // 如果没有 nextStep,说明是最后一个节点,设置 mergeStepId + if (currentNode.getBranchMergeStep() != null) { + co.setMergeStepId(currentNode.getBranchMergeStep()); + log.info("最后一个节点,设置 mergeStepId={}", currentNode.getBranchMergeStep()); + } + + result.add(co); + + // 查找下一个节点 + Long nextStepId = currentNode.getNextStep(); + if (nextStepId == null) { + log.info("nextStep 为 null,结束遍历"); + break; + } + + if (currentNode.getBranchMergeStep() != null) { + log.info("branchMergeStep 不为 null,结束遍历"); + break; + } + + currentNode = branchSteps.stream() + .filter(log -> log.getStepId().equals(nextStepId)) + .findFirst() + .orElse(null); + + if (currentNode == null) { + log.info("未找到下一个节点,nextStepId={}", nextStepId); + } + } + + log.info("分支链构建完成,共{}个节点", result.size()); + return result; + } + /** * 将 TaskLogE 转换为 TaskFlowChartCO */ @@ -123,12 +204,7 @@ public class TaskFlowChartQueryExe { co.setStatus(log.getStatus()); co.setStepName(log.getStepName()); co.setActUserName(log.getActUserName()); - - // 格式化时间 - if (log.getUpdateTime() != null) { - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm"); - co.setUpdateTime(sdf.format(log.getUpdateTime())); - } + co.setUpdateTime(log.getUpdateTime()); return co; } diff --git a/web-app/src/main/java/com/zcloud/eightwork/service/EightworkInfoServiceImpl.java b/web-app/src/main/java/com/zcloud/eightwork/service/EightworkInfoServiceImpl.java index bc1f9e2..ca40a50 100644 --- a/web-app/src/main/java/com/zcloud/eightwork/service/EightworkInfoServiceImpl.java +++ b/web-app/src/main/java/com/zcloud/eightwork/service/EightworkInfoServiceImpl.java @@ -2,17 +2,27 @@ package com.zcloud.eightwork.service; import com.alibaba.cola.dto.PageResponse; import com.alibaba.cola.dto.SingleResponse; +import com.alibaba.cola.exception.BizException; import com.zcloud.eightwork.api.EightworkInfoServiceI; +import com.zcloud.eightwork.api.TaskLogServiceI; import com.zcloud.eightwork.command.EightworkInfoAddExe; import com.zcloud.eightwork.command.EightworkInfoRemoveExe; import com.zcloud.eightwork.command.EightworkInfoUpdateExe; import com.zcloud.eightwork.command.query.EightworkInfoQueryExe; +import com.zcloud.eightwork.domain.gateway.TaskLogGateway; +import com.zcloud.eightwork.domain.model.TaskLogE; +import com.zcloud.eightwork.domain.model.enums.TaskLogStatus; import com.zcloud.eightwork.dto.EightworkInfoAddCmd; import com.zcloud.eightwork.dto.EightworkInfoPageQry; import com.zcloud.eightwork.dto.EightworkInfoUpdateCmd; +import com.zcloud.eightwork.dto.TaskLogNextCmd; import com.zcloud.eightwork.dto.clientobject.EightworkInfoCO; +import com.zcloud.eightwork.persistence.dataobject.EightworkInfoDO; +import com.zcloud.eightwork.persistence.repository.EightworkInfoRepository; import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; /** * web-app @@ -20,6 +30,7 @@ import org.springframework.stereotype.Service; * @Author fangjiakai * @Date 2025-11-12 10:58:53 */ +@Slf4j @Service @AllArgsConstructor public class EightworkInfoServiceImpl implements EightworkInfoServiceI { @@ -27,6 +38,9 @@ public class EightworkInfoServiceImpl implements EightworkInfoServiceI { private final EightworkInfoUpdateExe eightworkInfoUpdateExe; private final EightworkInfoRemoveExe eightworkInfoRemoveExe; private final EightworkInfoQueryExe eightworkInfoQueryExe; + private final TaskLogServiceI taskLogService; + private final TaskLogGateway taskLogGateway; + private final EightworkInfoRepository eightworkInfoRepository; @Override public EightworkInfoCO queryById(Long id) { @@ -60,5 +74,54 @@ public class EightworkInfoServiceImpl implements EightworkInfoServiceI { public void removeBatch(Long[] ids) { eightworkInfoRemoveExe.execute(ids); } + + /** + * 强制终止工作流 + * 业务逻辑参考 TaskLogUpdateExe.handleForceTerminate 方法 + * 1. 根据主表 ID 查询 workId + * 2. 查找当前正在进行的步骤(status=0) + * 3. 构建 TaskLogNextCmd 并设置 status=998 + * 4. 调用 TaskLogService.nextStep 处理强制终止 + * + * @param id 主表 ID + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void forceTerminate(Long id) { + log.info("强制终止工作流: id={}", id); + + // 1. 根据主表 ID 查询 EightworkInfoDO 获取 workId + EightworkInfoDO infoDO = eightworkInfoRepository.getById(id); + if (infoDO == null) { + throw new BizException("作业记录不存在"); + } + + String workId = infoDO.getWorkId(); + log.info("查询到 workId: {}", workId); + + // 2. 根据 workId 查询当前正在进行的步骤(status=0) + TaskLogE currentLog = taskLogGateway.listAllByWorkId(workId).stream() + .filter(log -> TaskLogStatus.IN_PROGRESS.equalsCode(log.getStatus())) + .findFirst() + .orElse(null); + + if (currentLog == null) { + throw new BizException("没有正在进行的步骤,无法强制终止"); + } + + log.info("找到当前进行中的步骤: stepId={}, stepName={}", currentLog.getStepId(), currentLog.getStepName()); + + // 3. 构建 TaskLogNextCmd 并设置 status=998(强制终止状态) + TaskLogNextCmd cmd = new TaskLogNextCmd(); + cmd.setId(currentLog.getId()); + cmd.setWorkId(workId); + cmd.setStepId(currentLog.getStepId()); + cmd.setStatus(998); // 强制终止状态码 + + // 4. 调用 TaskLogService.nextStep 处理强制终止 + taskLogService.nextStep(cmd); + + log.info("工作流已强制终止: workId={}, stepId={}", workId, currentLog.getStepId()); + } } diff --git a/web-app/src/main/java/com/zcloud/eightwork/service/TaskLogServiceImpl.java b/web-app/src/main/java/com/zcloud/eightwork/service/TaskLogServiceImpl.java index fc411ae..802cb9a 100644 --- a/web-app/src/main/java/com/zcloud/eightwork/service/TaskLogServiceImpl.java +++ b/web-app/src/main/java/com/zcloud/eightwork/service/TaskLogServiceImpl.java @@ -62,7 +62,7 @@ public class TaskLogServiceImpl implements TaskLogServiceI { TaskLogNextCmd taskLogNextCmd = new TaskLogNextCmd(commitTaskLogDO.getId(), commitTaskLogDO.getWorkId(), commitTaskLogDO.getStepId(), - TaskLogStatus.APPROVED.getCode(),null,null,null,cmd.getOthers(),null,null); + TaskLogStatus.APPROVED.getCode(),null,null,null,cmd.getOthers(),null,null,null); taskLogUpdateExe.nextStep(taskLogNextCmd); return SingleResponse.buildSuccess(); diff --git a/web-client/src/main/java/com/zcloud/eightwork/api/EightworkInfoServiceI.java b/web-client/src/main/java/com/zcloud/eightwork/api/EightworkInfoServiceI.java index c97c278..d59d51d 100644 --- a/web-client/src/main/java/com/zcloud/eightwork/api/EightworkInfoServiceI.java +++ b/web-client/src/main/java/com/zcloud/eightwork/api/EightworkInfoServiceI.java @@ -25,5 +25,12 @@ public interface EightworkInfoServiceI { void remove(Long id); void removeBatch(Long[] ids); + + /** + * 强制终止工作流 + * + * @param id 主表 ID + */ + void forceTerminate(Long id); } diff --git a/web-client/src/main/java/com/zcloud/eightwork/dto/ConfinedSpaceAddCmd.java b/web-client/src/main/java/com/zcloud/eightwork/dto/ConfinedSpaceAddCmd.java index 3834364..9e845a1 100644 --- a/web-client/src/main/java/com/zcloud/eightwork/dto/ConfinedSpaceAddCmd.java +++ b/web-client/src/main/java/com/zcloud/eightwork/dto/ConfinedSpaceAddCmd.java @@ -40,9 +40,15 @@ public class ConfinedSpaceAddCmd extends Command { @NotEmpty(message = "空间类型名称不能为空") private String typeName; + @ApiModelProperty(value = "主管部门", name = "manageDeptId") + private Long manageDeptId; + + @ApiModelProperty(value = "主管部门名称", name = "manageDeptName") + private String manageDeptName; + @ApiModelProperty(value = "主要介质", name = "medium", required = true) @NotEmpty(message = "主要介质不能为空") - private String medium; + private String mediumInfo; @ApiModelProperty(value = "危险因素", name = "hazards", required = true) @NotEmpty(message = "危险因素不能为空") @@ -77,7 +83,7 @@ public class ConfinedSpaceAddCmd extends Command { @ApiModelProperty(value = "位置及范围", name = "position", required = true) @NotEmpty(message = "位置及范围不能为空") - private String position; + private String positionAndRange; @ApiModelProperty(value = "所属部门", name = "departmentId", required = true) private Long departmentId; diff --git a/web-client/src/main/java/com/zcloud/eightwork/dto/ConfinedSpaceUpdateCmd.java b/web-client/src/main/java/com/zcloud/eightwork/dto/ConfinedSpaceUpdateCmd.java index dbe4881..3c877eb 100644 --- a/web-client/src/main/java/com/zcloud/eightwork/dto/ConfinedSpaceUpdateCmd.java +++ b/web-client/src/main/java/com/zcloud/eightwork/dto/ConfinedSpaceUpdateCmd.java @@ -42,9 +42,16 @@ public class ConfinedSpaceUpdateCmd extends Command { @ApiModelProperty(value = "空间类型名称", name = "typeName", required = true) @NotEmpty(message = "空间类型名称不能为空") private String typeName; + + @ApiModelProperty(value = "主管部门", name = "manageDeptId") + private Long manageDeptId; + + @ApiModelProperty(value = "主管部门名称", name = "manageDeptName") + private String manageDeptName; + @ApiModelProperty(value = "主要介质", name = "medium", required = true) @NotEmpty(message = "主要介质不能为空") - private String medium; + private String mediumInfo; @ApiModelProperty(value = "危险因素", name = "hazards", required = true) @NotEmpty(message = "危险因素不能为空") private String hazards; @@ -70,7 +77,7 @@ public class ConfinedSpaceUpdateCmd extends Command { private String emergencyBookFile; @ApiModelProperty(value = "位置及范围", name = "position", required = true) @NotEmpty(message = "位置及范围不能为空") - private String position; + private String positionAndRange; @ApiModelProperty(value = "所属部门", name = "departmentId", required = true) @NotNull(message = "所属部门不能为空") private Long departmentId; diff --git a/web-client/src/main/java/com/zcloud/eightwork/dto/EightworkInfoPageQry.java b/web-client/src/main/java/com/zcloud/eightwork/dto/EightworkInfoPageQry.java index 1c0f98f..f4bb90d 100644 --- a/web-client/src/main/java/com/zcloud/eightwork/dto/EightworkInfoPageQry.java +++ b/web-client/src/main/java/com/zcloud/eightwork/dto/EightworkInfoPageQry.java @@ -55,5 +55,12 @@ public class EightworkInfoPageQry extends PageQuery { */ private Long eqCurrentStepId; + /** + * 受限空间名称和编号筛选(模糊查询) + */ + private Integer eqIsInnerWork; + private String likeLimitedSpaceNameAndCode; + + } diff --git a/web-client/src/main/java/com/zcloud/eightwork/dto/clientobject/ConfinedSpaceCO.java b/web-client/src/main/java/com/zcloud/eightwork/dto/clientobject/ConfinedSpaceCO.java index a50b641..c16c53a 100644 --- a/web-client/src/main/java/com/zcloud/eightwork/dto/clientobject/ConfinedSpaceCO.java +++ b/web-client/src/main/java/com/zcloud/eightwork/dto/clientobject/ConfinedSpaceCO.java @@ -34,9 +34,15 @@ public class ConfinedSpaceCO extends ClientObject { //空间类型名称 @ApiModelProperty(value = "空间类型名称") private String typeName; + //主管部门 + @ApiModelProperty(value = "主管部门") + private Long manageDeptId; + //主管部门名称 + @ApiModelProperty(value = "主管部门名称") + private String manageDeptName; //主要介质 @ApiModelProperty(value = "主要介质") - private String medium; + private String mediumInfo; //危险因素 @ApiModelProperty(value = "危险因素") private String hazards; @@ -63,7 +69,7 @@ public class ConfinedSpaceCO extends ClientObject { private String emergencyBookFile; //位置及范围 @ApiModelProperty(value = "位置及范围") - private String position; + private String positionAndRange; //所属部门 @ApiModelProperty(value = "所属部门") private Long departmentId; diff --git a/web-client/src/main/java/com/zcloud/eightwork/dto/clientobject/MeasuresLogsCO.java b/web-client/src/main/java/com/zcloud/eightwork/dto/clientobject/MeasuresLogsCO.java index 3d7f09d..82e73db 100644 --- a/web-client/src/main/java/com/zcloud/eightwork/dto/clientobject/MeasuresLogsCO.java +++ b/web-client/src/main/java/com/zcloud/eightwork/dto/clientobject/MeasuresLogsCO.java @@ -40,6 +40,9 @@ public class MeasuresLogsCO extends ClientObject { //是否合格1是2否 @ApiModelProperty(value = "是否合格1是2否") private Integer status; + //类型1主要2其他 + @ApiModelProperty(value = "类型1主要2其他") + private Integer type; //签字 @ApiModelProperty(value = "签字") private String signPath; diff --git a/web-client/src/main/java/com/zcloud/eightwork/dto/clientobject/TaskFlowChartCO.java b/web-client/src/main/java/com/zcloud/eightwork/dto/clientobject/TaskFlowChartCO.java index 21c29c9..ece219a 100644 --- a/web-client/src/main/java/com/zcloud/eightwork/dto/clientobject/TaskFlowChartCO.java +++ b/web-client/src/main/java/com/zcloud/eightwork/dto/clientobject/TaskFlowChartCO.java @@ -1,11 +1,13 @@ package com.zcloud.eightwork.dto.clientobject; +import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import java.time.LocalDateTime; import java.util.List; /** @@ -33,7 +35,8 @@ public class TaskFlowChartCO { private String actUserName; @ApiModelProperty(value = "更新时间") - private String updateTime; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime updateTime; @ApiModelProperty(value = "分支步骤列表(仅主流程步骤中的分支开始节点有值)") private List branches; diff --git a/web-domain/src/main/java/com/zcloud/eightwork/domain/model/ConfinedSpaceE.java b/web-domain/src/main/java/com/zcloud/eightwork/domain/model/ConfinedSpaceE.java index acd1b61..e9e4359 100644 --- a/web-domain/src/main/java/com/zcloud/eightwork/domain/model/ConfinedSpaceE.java +++ b/web-domain/src/main/java/com/zcloud/eightwork/domain/model/ConfinedSpaceE.java @@ -23,8 +23,12 @@ public class ConfinedSpaceE extends BaseE { private String type; //空间类型名称 private String typeName; + //主管部门 + private Long manageDeptId; + //主管部门名称 + private String manageDeptName; //主要介质 - private String medium; + private String mediumInfo; //危险因素 private String hazards; //风险等级 @@ -42,7 +46,7 @@ public class ConfinedSpaceE extends BaseE { //应急指导书附件 private String emergencyBookFile; //位置及范围 - private String position; + private String positionAndRange; //所属部门 private Long departmentId; } diff --git a/web-infrastructure/src/main/java/com/zcloud/eightwork/persistence/dataobject/ConfinedSpaceDO.java b/web-infrastructure/src/main/java/com/zcloud/eightwork/persistence/dataobject/ConfinedSpaceDO.java index 926e820..f8ebb18 100644 --- a/web-infrastructure/src/main/java/com/zcloud/eightwork/persistence/dataobject/ConfinedSpaceDO.java +++ b/web-infrastructure/src/main/java/com/zcloud/eightwork/persistence/dataobject/ConfinedSpaceDO.java @@ -1,5 +1,6 @@ package com.zcloud.eightwork.persistence.dataobject; +import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import com.jjb.saas.framework.repository.basedo.BaseDO; import io.swagger.annotations.ApiModelProperty; @@ -36,9 +37,15 @@ public class ConfinedSpaceDO extends BaseDO { //空间类型名称 @ApiModelProperty(value = "空间类型名称") private String typeName; + //主管部门 + @ApiModelProperty(value = "主管部门") + private Long manageDeptId; + //主管部门名称 + @ApiModelProperty(value = "主管部门名称") + private String manageDeptName; //主要介质 @ApiModelProperty(value = "主要介质") - private String medium; + private String mediumInfo; //危险因素 @ApiModelProperty(value = "危险因素") private String hazards; @@ -65,7 +72,7 @@ public class ConfinedSpaceDO extends BaseDO { private String emergencyBookFile; //位置及范围 @ApiModelProperty(value = "位置及范围") - private String position; + private String positionAndRange; //所属部门 @ApiModelProperty(value = "所属部门") private Long departmentId; diff --git a/web-infrastructure/src/main/resources/mapper/ConfinedSpaceMapper.xml b/web-infrastructure/src/main/resources/mapper/ConfinedSpaceMapper.xml new file mode 100644 index 0000000..6fe9f7c --- /dev/null +++ b/web-infrastructure/src/main/resources/mapper/ConfinedSpaceMapper.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/web-infrastructure/src/main/resources/mapper/EightworkInfoMapper.xml b/web-infrastructure/src/main/resources/mapper/EightworkInfoMapper.xml index 8547f44..7b47ac3 100644 --- a/web-infrastructure/src/main/resources/mapper/EightworkInfoMapper.xml +++ b/web-infrastructure/src/main/resources/mapper/EightworkInfoMapper.xml @@ -60,6 +60,9 @@ and t.create_time <= #{params.leCreateTime} + + and t.info->>'$.isInnerWork' = #{params.eqIsInnerWork} + and t.create_name like concat('%', #{params.likeCreateName}, '%') @@ -72,6 +75,9 @@ and t.info->>'$.workStartTime' <= #{params.leWorkStartTime} + + and t.info->>'$.limitedSpaceNameAndCode' like concat('%', #{params.likeLimitedSpaceNameAndCode}, '%') + and exists (select 1 from task_log log