feat(workflow): 新增强制终止工作流功能并优化受限空间管理
- 添加 forceTerminate 接口用于强制终止工作流 - 实现多人签字步骤的支持和分支流程图构建 - 优化 TaskFlowChartQueryExe 中的分支步骤处理逻辑 - 更新 TaskLogUpdateExe 中的签字步骤处理机制 - 添加 ConfinedSpace 的主管部门字段管理 - 重构 TaskLogAddExe 中的多人签字步骤创建逻辑 - 优化工作流跳过状态处理逻辑 - 添加更多日志输出便于调试跟踪master
parent
2788eccbb5
commit
9bd9b69e4c
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<TaskLogCO> 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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<TaskFlowE> flows = taskFlowGateway.listAllByWorkType(cmd.getWorkType(), cmd.getWorkLevel());
|
||||
|
||||
// 7. 生成所有步骤 task_log
|
||||
// 7. 生成所有步骤 task_log(多人签字步骤会生成多条记录)
|
||||
List<TaskLogDO> 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<TaskFlowE> flows = taskFlowGateway.listAllByWorkType(cmd.getWorkType(), cmd.getWorkLevel());
|
||||
log.info("获取到 {} 个流程步骤", flows.size());
|
||||
|
||||
// 3. 生成任务日志
|
||||
List<TaskLogDO> taskLogs = flows.stream().map(flow -> createTaskLog(flow, workId, cmd)).collect(Collectors.toList());
|
||||
// 3. 生成任务日志(多人签字步骤会生成多条记录)
|
||||
List<TaskLogDO> 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<TaskLogDO> createTaskLogs(TaskFlowE flow, String workId, TaskLogAddCmd cmd) {
|
||||
List<TaskLogDO> result = new ArrayList<>();
|
||||
|
||||
// 判断是否为多人签字步骤
|
||||
if (Integer.valueOf(1).equals(flow.getMultipleFlag())) {
|
||||
// 多人签字步骤:为每个签字人创建独立的记录
|
||||
List<TaskSignStepInfoCmd> 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<TaskSignStepInfoCmd> 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());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理用户预设的签字人
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
@ -379,7 +380,6 @@ public class TaskLogUpdateExe {
|
|||
}
|
||||
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -685,20 +685,9 @@ public class TaskLogUpdateExe {
|
|||
List<TaskLogE> allLogs = taskLogGateway.listAllByWorkId(cmd.getWorkId());
|
||||
List<TaskLogDO> 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<TaskLogDO> actionLogs, List<TaskLogE> allLogs) {
|
||||
|
|
@ -983,24 +973,51 @@ public class TaskLogUpdateExe {
|
|||
return;
|
||||
}
|
||||
|
||||
for (TaskSignStepInfoCmd signInfo : signLogs) {
|
||||
TaskLogE signStepLog = allLogs.stream()
|
||||
.filter(log -> log.getStepId().equals(signInfo.getStepId()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
// 按 stepId 分组,处理多人签字步骤
|
||||
Map<Long, List<TaskSignStepInfoCmd>> signLogsByStepId = signLogs.stream()
|
||||
.collect(Collectors.groupingBy(TaskSignStepInfoCmd::getStepId));
|
||||
|
||||
if (signStepLog == null) {
|
||||
for (Map.Entry<Long, List<TaskSignStepInfoCmd>> entry : signLogsByStepId.entrySet()) {
|
||||
Long stepId = entry.getKey();
|
||||
List<TaskSignStepInfoCmd> stepSignInfos = entry.getValue();
|
||||
|
||||
// 获取该步骤的所有现有记录
|
||||
List<TaskLogE> existingLogs = allLogs.stream()
|
||||
.filter(log -> log.getStepId().equals(stepId))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (existingLogs.isEmpty()) {
|
||||
log.warn("未找到步骤记录,跳过设置签字人: stepId={}", stepId);
|
||||
continue;
|
||||
}
|
||||
|
||||
TaskLogE firstLog = existingLogs.get(0);
|
||||
|
||||
// 判断是否为多人签字步骤
|
||||
boolean isMultipleSign = MULTIPLE_FLAG.equals(firstLog.getMultipleFlag());
|
||||
|
||||
if (isMultipleSign) {
|
||||
// 多人签字步骤:需要确保记录数等于签字人数
|
||||
handleMultipleSignStep(firstLog, existingLogs, stepSignInfos, actionLogs, allLogs);
|
||||
} else {
|
||||
// 单人签字步骤:只处理第一个签字人
|
||||
if (!stepSignInfos.isEmpty()) {
|
||||
handleSingleSignStep(firstLog, stepSignInfos.get(0), actionLogs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理单人签字步骤
|
||||
*/
|
||||
private void handleSingleSignStep(TaskLogE signStepLog, TaskSignStepInfoCmd signInfo, List<TaskLogDO> 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(),
|
||||
|
|
@ -1008,11 +1025,99 @@ public class TaskLogUpdateExe {
|
|||
signInfo.getActUser(),
|
||||
signInfo.getActUserName()
|
||||
);
|
||||
|
||||
addActionLog(actionLogs, signStepLog);
|
||||
log.info("已设置签字人: step={}, user={}", signStepLog.getStepName(), signInfo.getActUserName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理多人签字步骤
|
||||
* 如果现有记录数少于签字人数,自动创建新记录
|
||||
*/
|
||||
private void handleMultipleSignStep(TaskLogE firstLog, List<TaskLogE> existingLogs,
|
||||
List<TaskSignStepInfoCmd> signInfos,
|
||||
List<TaskLogDO> actionLogs, List<TaskLogE> 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<TaskLogE> 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, 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());
|
||||
|
|
|
|||
|
|
@ -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<TaskLogE> allLogs = taskLogGateway.listAllByWorkId(workId);
|
||||
log.info("查询到所有步骤数量: {}", allLogs.size());
|
||||
|
||||
// 2. 过滤 SKIPPED 状态的步骤
|
||||
List<TaskLogE> filteredLogs = allLogs.stream()
|
||||
.filter(log -> !TaskLogStatus.SKIPPED.equalsCode(log.getStatus()))
|
||||
.collect(Collectors.toList());
|
||||
log.info("过滤SKIPPED后步骤数量: {}", filteredLogs.size());
|
||||
|
||||
// 3. 区分主流程步骤和分支步骤
|
||||
List<TaskLogE> mainSteps = filteredLogs.stream()
|
||||
|
|
@ -55,24 +55,36 @@ public class TaskFlowChartQueryExe {
|
|||
List<TaskLogE> branchSteps = filteredLogs.stream()
|
||||
.filter(log -> !isMainStep(log))
|
||||
.collect(Collectors.toList());
|
||||
log.info("主流程步骤数量: {}, 分支步骤数量: {}", mainSteps.size(), branchSteps.size());
|
||||
|
||||
// 4. 按 branchStep 分组分支步骤
|
||||
Map<Long, List<TaskLogE>> 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<TaskFlowChartCO> 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<TaskLogE> branches = branchGroupMap.get(mainStep.getStepId());
|
||||
if (branches != null && !branches.isEmpty()) {
|
||||
// 按步骤ID排序,确保分支内顺序正确
|
||||
branches.sort((a, b) -> a.getStepOrder().compareTo(b.getStepOrder()));
|
||||
List<TaskFlowChartCO> branchCOs = convertBranches(branches);
|
||||
mainCO.setBranches(branchCOs);
|
||||
// 获取分支第一个节点
|
||||
Long firstBranchStepId = mainStep.getBranchStep();
|
||||
log.info("开启分支,分支第一个节点ID: {}", firstBranchStepId);
|
||||
if (firstBranchStepId != null) {
|
||||
// 从分支第一个节点开始,通过 nextStep 遍历整个分支链
|
||||
List<TaskFlowChartCO> 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<TaskFlowChartCO> buildBranchChain(Long firstBranchStepId, List<TaskLogE> branchSteps) {
|
||||
List<TaskFlowChartCO> 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -25,5 +25,12 @@ public interface EightworkInfoServiceI {
|
|||
void remove(Long id);
|
||||
|
||||
void removeBatch(Long[] ids);
|
||||
|
||||
/**
|
||||
* 强制终止工作流
|
||||
*
|
||||
* @param id 主表 ID
|
||||
*/
|
||||
void forceTerminate(Long id);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -55,5 +55,12 @@ public class EightworkInfoPageQry extends PageQuery {
|
|||
*/
|
||||
private Long eqCurrentStepId;
|
||||
|
||||
/**
|
||||
* 受限空间名称和编号筛选(模糊查询)
|
||||
*/
|
||||
private Integer eqIsInnerWork;
|
||||
private String likeLimitedSpaceNameAndCode;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<TaskFlowChartCO> branches;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="com.zcloud.eightwork.persistence.mapper.ConfinedSpaceMapper">
|
||||
|
||||
</mapper>
|
||||
|
||||
|
|
@ -60,6 +60,9 @@
|
|||
<if test="params.leCreateTime != null and params.leCreateTime != ''">
|
||||
and t.create_time <= #{params.leCreateTime}
|
||||
</if>
|
||||
<if test="params.eqIsInnerWork != null and params.eqIsInnerWork != ''">
|
||||
and t.info->>'$.isInnerWork' = #{params.eqIsInnerWork}
|
||||
</if>
|
||||
<if test="params.likeCreateName != null and params.likeCreateName != ''">
|
||||
and t.create_name like concat('%', #{params.likeCreateName}, '%')
|
||||
</if>
|
||||
|
|
@ -72,6 +75,9 @@
|
|||
<if test="params.leWorkStartTime != null and params.leWorkStartTime != ''">
|
||||
and t.info->>'$.workStartTime' <= #{params.leWorkStartTime}
|
||||
</if>
|
||||
<if test="params.likeLimitedSpaceNameAndCode != null and params.likeLimitedSpaceNameAndCode != ''">
|
||||
and t.info->>'$.limitedSpaceNameAndCode' like concat('%', #{params.likeLimitedSpaceNameAndCode}, '%')
|
||||
</if>
|
||||
<if test="params.eqCurrentStepId != null">
|
||||
and exists (select 1
|
||||
from task_log log
|
||||
|
|
|
|||
Loading…
Reference in New Issue