feat(workflow): 新增强制终止工作流功能并优化受限空间管理

- 添加 forceTerminate 接口用于强制终止工作流
- 实现多人签字步骤的支持和分支流程图构建
- 优化 TaskFlowChartQueryExe 中的分支步骤处理逻辑
- 更新 TaskLogUpdateExe 中的签字步骤处理机制
- 添加 ConfinedSpace 的主管部门字段管理
- 重构 TaskLogAddExe 中的多人签字步骤创建逻辑
- 优化工作流跳过状态处理逻辑
- 添加更多日志输出便于调试跟踪
master
fangjiakai 2026-03-31 11:05:05 +08:00
parent 2788eccbb5
commit 9bd9b69e4c
19 changed files with 440 additions and 83 deletions

View File

@ -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();
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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());
}
/**
*
*/

View File

@ -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<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,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<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;
}
// 检查是否需要跳过
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<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(),
signInfo.getActUserDepartmentName(),
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, 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());

View File

@ -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;
}

View File

@ -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());
}
}

View File

@ -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();

View File

@ -25,5 +25,12 @@ public interface EightworkInfoServiceI {
void remove(Long id);
void removeBatch(Long[] ids);
/**
*
*
* @param id ID
*/
void forceTerminate(Long id);
}

View File

@ -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;

View File

@ -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;

View File

@ -55,5 +55,12 @@ public class EightworkInfoPageQry extends PageQuery {
*/
private Long eqCurrentStepId;
/**
*
*/
private Integer eqIsInnerWork;
private String likeLimitedSpaceNameAndCode;
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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>

View File

@ -60,6 +60,9 @@
<if test="params.leCreateTime != null and params.leCreateTime != ''">
and t.create_time &lt;= #{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' &lt;= #{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