feat(workflow): 优化作业信息查询和流程处理功能

master
zhaokai 2026-04-22 17:25:37 +08:00
parent a441f76b53
commit fbe3c0b64d
22 changed files with 828 additions and 38 deletions

View File

@ -29,6 +29,7 @@ import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@ -201,6 +202,7 @@ public class EightworkInfoSaveDraftExe {
AuthContext.getUserId(),
AuthContext.getName()
);
taskLog.setSignTime(LocalDateTime.now());
// 暂存时申请步骤状态为进行中
taskLog.setStatus(DRAFT_STATUS);

View File

@ -33,6 +33,7 @@ import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
@ -280,6 +281,7 @@ public class TaskLogAddExe {
AuthContext.getUserId(),
AuthContext.getName()
);
taskLogDO.setSignTime(LocalDateTime.now());
log.info("第一步已设置申请人: stepName={}", flow.getStepName());
}

View File

@ -13,6 +13,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.jjb.saas.message.client.message.facede.MessageFacade;
import com.jjb.saas.message.client.message.request.MessageSendCmd;
import com.jjb.saas.message.client.message.request.MessageTargetCmd;
import com.zcloud.eightwork.command.convertor.TaskLogCoConvertor;
import com.zcloud.eightwork.command.convertor.TaskLogConvertUtil;
import com.zcloud.eightwork.domain.config.MessageConfig;
import com.zcloud.eightwork.domain.gateway.EightworkSupplementaryInfoGateway;
@ -46,6 +47,7 @@ import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
@ -84,7 +86,7 @@ public class TaskLogUpdateExe {
/**
* PC
*/
private static final Integer PC_FLAG = 1;
private static final Integer PC_FLAG = 0;
/**
* APP
*/
@ -105,10 +107,15 @@ public class TaskLogUpdateExe {
*
*/
private static final Integer REJECTED_STATUS = 2;
/**
* 使,1:,2:
*/
private static final Integer STEP_TYPE = 2;
private final TaskLogGateway taskLogGateway;
private final TaskFlowGateway taskFlowGateway;
private final TaskLogRepository taskLogRepository;
private final TaskLogCoConvertor taskLogCoConvertor;
private final TaskLogArchiveRepository taskLogArchiveRepository;
private final MeasuresLogsRepository measuresLogsRepository;
private final MeasuresLogsArchiveRepository measuresLogsArchiveRepository;
@ -146,6 +153,7 @@ public class TaskLogUpdateExe {
*
*/
private static final Integer FORCE_TERMINATE_STATUS = 998;
private static final Integer REJECT_STEP_STATUS = 997;
/**
*
*/
@ -180,6 +188,13 @@ public class TaskLogUpdateExe {
return;
}
if (REJECT_STEP_STATUS.equals(cmd.getStatus())) {
handleRejectStep(cmd);
log.info("打回到某一步步骤,重新处理步骤流转: workId={}, stepId={}, status={}",
cmd.getWorkId(), cmd.getStepId(), cmd.getStatus());
return;
}
List<TaskLogE> logs = taskLogGateway.listAllByWorkId(cmd.getWorkId());
List<TaskLogDO> actionLogs = new ArrayList<>();
@ -241,6 +256,8 @@ public class TaskLogUpdateExe {
log.info("步骤流转完成: workId={}, currentStep={}", cmd.getWorkId(), currentLog.getStepName());
}
/**
*
*/
@ -266,6 +283,7 @@ public class TaskLogUpdateExe {
// 签字步骤,保存签字图片
if (SIGN_STEP_FLAG.equals(currentLog.getSignStepFlag())) {
currentLog.setSignPath(cmd.getSignPath());
currentLog.setSignTime(LocalDateTime.now());
}
// 定位步骤,保存经纬度
@ -378,6 +396,7 @@ public class TaskLogUpdateExe {
// 修改当前步骤状态为已完成
currentLog.setStatus(TaskLogStatus.APPROVED.getCode());
currentLog.setSignTime(LocalDateTime.now());
addActionLog(actionLogs, currentLog);
}
@ -408,6 +427,7 @@ public class TaskLogUpdateExe {
return d;
}).collect(Collectors.toList()));
} else {
measuresLogsRepository.deleteByWorkId(currentLog.getWorkId());
measuresLogsRepository.saveBatch(measuresList.stream().map(item -> {
MeasuresLogsDO d = new MeasuresLogsDO();
BeanUtils.copyProperties(item, d, "id");
@ -444,6 +464,7 @@ public class TaskLogUpdateExe {
// 持续步骤状态保持 IN_PROGRESS不随审批流转
currentLog.setStatus(TaskLogStatus.IN_PROGRESS.getCode());
currentLog.setSignTime(LocalDateTime.now());
// 添加到待更新列表
addActionLog(actionLogs, currentLog);
@ -884,7 +905,8 @@ public class TaskLogUpdateExe {
.orElseThrow(() -> new BizException("未找到申请步骤配置"));
applyFlow.setStatus(TaskLogStatus.IN_PROGRESS.getCode());
addActionLog(actionLogs, applyFlow);
//将分支步骤改为已打回
handleBranchStepsForReject(allLogs, actionLogs);
// 更新本次步骤变化
taskLogRepository.updateBatchById(actionLogs);
@ -928,20 +950,357 @@ public class TaskLogUpdateExe {
messageNotice(branchStep, currentLog);
log.info("工作流已打回到申请步骤: workId={}", cmd.getWorkId());
}
/**
*
*
*/
private void handleBranchStepsForReject(List<TaskLogE> allLogs, List<TaskLogDO> actionLogs) {
log.info("开始处理打回时的分支步骤状态");
// 收集所有分支开始节点
List<TaskLogE> branchStartNodes = allLogs.stream()
.filter(log -> BranchFlag.BRANCH_START.getCode().equals(log.getBranchFlag()))
.collect(Collectors.toList());
if (branchStartNodes.isEmpty()) {
log.info("没有分支流程需要处理");
return;
}
for (TaskLogE branchStartNode : branchStartNodes) {
Long branchStepId = branchStartNode.getBranchStep();
if (branchStepId == null) {
log.warn("分支开始节点 {} 没有配置分支步骤ID", branchStartNode.getStepName());
continue;
}
// 查找对应的分支步骤
TaskLogE branchStep = allLogs.stream()
.filter(log -> log.getStepId().equals(branchStepId))
.findFirst()
.orElse(null);
if (branchStep == null) {
log.warn("未找到分支步骤: branchStepId={}", branchStepId);
continue;
}
// 如果分支步骤进行中),将其改为打回状态
if (TaskLogStatus.IN_PROGRESS.equalsCode(branchStep.getStatus())) {
Integer oldStatus = branchStep.getStatus();
branchStep.setStatus(TaskLogStatus.REJECTED.getCode());
addActionLog(actionLogs, branchStep);
log.info("将分支步骤 {} (stepId={}) 状态从 {} 改为打回(2)",
branchStep.getStepName(), branchStepId, oldStatus);
// 7. 分支发送待办完成事件
sendTodoCompleteEvent(branchStep.getId());
}
}
log.info("分支步骤打回处理完成");
}
private void handleRejectStep(TaskLogNextCmd cmd) {
log.info("开始处理打回到某一步: workId={}, stepId={}", cmd.getWorkId(), cmd.getStepId());
List<TaskLogE> allLogs = taskLogGateway.listAllByWorkId(cmd.getWorkId());
TaskLogE currentLog = findCurrentLog(allLogs, cmd.getId());
if (currentLog == null) {
throw new BizException("未找到对应的步骤记录");
}
Integer rejectStepId = currentLog.getRejectStepId();
if(rejectStepId==null){
log.warn("未找到对应的步骤记录.workId={}, stepId={}", cmd.getWorkId(), cmd.getStepId());
return;
}
EightworkInfoDO infoDO = eightworkInfoRepository.getOne(
new LambdaQueryWrapper<EightworkInfoDO>()
.eq(EightworkInfoDO::getWorkId, cmd.getWorkId())
);
if (infoDO == null) {
log.warn("未找到作业信息: workId={}", cmd.getWorkId());
return;
}
// 1. 归档当前的 task_log 记录(除了申请步骤)
archiveTaskLogs(allLogs);
// 2. 删除的所有 task_log
deleteTaskLogsExceptFirst(cmd.getWorkId());
List<TaskLogDO> taskLogs = taskLogCoConvertor.converEsToDOs(allLogs);
// 3. 设置所有步骤的id
for (TaskLogDO taskLog : taskLogs) {
taskLog.setId(null);
taskLog.setTaskLogId(Tools.get32UUID());
}
//4. 将指定步骤之后的步骤设置为未开始
resetStepsAfterTargetStep(taskLogs, rejectStepId);
// 5. 批量保存
taskLogRepository.saveBatch(taskLogs);
// 6. 发送待办完成事件
sendTodoCompleteEvent(currentLog.getId());
TaskLogE branchStep = new TaskLogE();
branchStep.setActUser(currentLog.getActUser());
branchStep.setStatus(TaskLogStatus.REJECTED.getCode());
branchStep.setStepName(currentLog.getStepName());
messageNotice(branchStep, currentLog);
log.info("工作流已打回到指定步骤: workId={},step={}", cmd.getWorkId(),rejectStepId);
//获取目标步骤
// 6. 从新保存的数据中获取目标步骤(用于发送待办和消息)
TaskLogDO targetTaskLogDO = taskLogs.stream()
.filter(log -> log.getStepId().equals(rejectStepId.longValue()))
.findFirst()
.orElse(null);
//发送待办和消息
// 发送待办通知
log.info("发送退回步骤待办通知");
if(targetTaskLogDO!=null){
log.info("发送待办通知workId={},workType={},数据:{}",currentLog.getWorkId(),currentLog.getWorkType(),JSONUtil.toJsonStr(targetTaskLogDO));
TaskLogE targetTaskLogE = new TaskLogE();
BeanUtils.copyProperties(targetTaskLogDO, targetTaskLogE);
sendTodoAddEvent(getWorkId(currentLog.getWorkId()), targetTaskLogE, currentLog.getWorkType());
} else {
log.warn("未找到目标步骤,无法发送待办通知: rejectStepId={}", rejectStepId);
}
log.info("工作流已打回到指定步骤: workId={}, step={}", cmd.getWorkId(), rejectStepId);
}
/**
* ID
* - targetStepId 0
* - targetStepId 1 -99
* -
*
* @param taskLogs
* @param targetStepId ID
*/
private void resetStepsAfterTargetStep(List<TaskLogDO> taskLogs, Integer targetStepId) {
if (targetStepId == null) {
log.warn("目标步骤ID为空跳过重置后续步骤");
return;
}
log.info("开始重置步骤状态: targetStepId={}", targetStepId);
// 构建 stepId -> TaskLogDO 的映射,方便快速查找
Map<Long, TaskLogDO> stepMap = taskLogs.stream()
.collect(Collectors.toMap(TaskLogDO::getStepId, log -> log, (v1, v2) -> v1));
// 1. 将目标步骤设置为进行中
TaskLogDO targetStep = stepMap.get(targetStepId.longValue());
if (targetStep != null) {
Integer oldStatus = targetStep.getStatus();
targetStep.setStatus(DRAFT_STATUS); // 0 进行中/暂存状态
log.info("将目标步骤 {} (stepId={}) 状态从 {} 改为 {}",
targetStep.getStepName(), targetStepId, oldStatus, DRAFT_STATUS);
} else {
log.warn("未找到目标步骤: stepId={}", targetStepId);
}
// 2. 收集所有分支开始节点及其对应的分支步骤
List<BranchInfo> branchInfos = collectBranchInfos(taskLogs, stepMap);
// 2. 从目标步骤开始,沿着 nextStep 链路遍历,将通过的步骤改为未开始
Long currentStepId = targetStepId.longValue();
int resetCount = 0;
Set<Long> visitedSteps = new HashSet<>();
visitedSteps.add(currentStepId);
while (currentStepId != null) {
TaskLogDO currentStep = stepMap.get(currentStepId);
if (currentStep == null) {
log.warn("未找到步骤: stepId={}", currentStepId);
break;
}
// 获取下一步骤ID
Long nextStepId = currentStep.getNextStep();
// 如果下一步骤存在且不是归档状态999
if (nextStepId != null && !nextStepId.equals(TaskLogStatus.ARCHIVED.getCode().longValue())) {
TaskLogDO nextStep = stepMap.get(nextStepId);
if (nextStep != null) {
// 只将通过状态1的步骤改为未开始-99
if (TaskLogStatus.APPROVED.equalsCode(nextStep.getStatus())||TaskLogStatus.IN_PROGRESS.equalsCode(nextStep.getStatus())) {
Integer oldStatus = nextStep.getStatus();
nextStep.setStatus(TaskLogStatus.NOT_STARTED.getCode());
resetCount++;
log.info("将步骤 {} (stepId={}) 状态从 {} 改为 {}",
nextStep.getStepName(), nextStepId, oldStatus, TaskLogStatus.NOT_STARTED.getCode());
}
/* //如果有分支开始,将分支步骤设置为进行中
if (BranchFlag.getByCode(nextStep.getBranchFlag()).isBranchStart()) {
TaskLogDO branchStep = stepMap.get(nextStep.getBranchStep());
}*/
}
}
// 移动到下一步
currentStepId = nextStepId;
}
log.info("准备调用 handleBranchStepsStatusbranchInfos 数量: {}", branchInfos.size());
// 4. 处理分支流程的状态设置
handleBranchStepsStatus(targetStepId, branchInfos, stepMap, visitedSteps);
log.info("状态重置完成: 目标步骤设置为进行中, 共重置 {} 个已通过步骤为未开始", resetCount);
}
/**
*
*/
private static class BranchInfo {
TaskLogDO branchStartNode; // 分支开始节点
Long branchStepId; // 分支步骤ID
BranchInfo(TaskLogDO branchStartNode, Long branchStepId) {
this.branchStartNode = branchStartNode;
this.branchStepId = branchStepId;
}
}
/**
*
*/
private List<BranchInfo> collectBranchInfos(List<TaskLogDO> taskLogs, Map<Long, TaskLogDO> stepMap) {
List<BranchInfo> branchInfos = new ArrayList<>();
for (TaskLogDO taskLogDO : taskLogs) {
// 找到所有分支开始节点branchFlag == 1
if (BranchFlag.BRANCH_START.getCode().equals(taskLogDO.getBranchFlag())) {
Long branchStepId = taskLogDO.getBranchStep() != null ? taskLogDO.getBranchStep().longValue() : null;
if (branchStepId != null) {
branchInfos.add(new BranchInfo(taskLogDO, branchStepId));
log.info("发现分支开始节点: branchStartStepId={}, branchStepId={}, branchStartNodeName={}",
taskLogDO.getStepId(), branchStepId, taskLogDO.getStepName());
}
}
}
return branchInfos;
}
/**
*
* - targetStepId 0
* - targetStepId -99
*/
private void handleBranchStepsStatus(Integer targetStepId, List<BranchInfo> branchInfos,
Map<Long, TaskLogDO> stepMap, Set<Long> visitedSteps) {
if (branchInfos.isEmpty()) {
log.info("没有分支流程需要处理");
return;
}
for (BranchInfo branchInfo : branchInfos) {
//只处理已完成和进行中的
TaskLogDO branchStartNode = branchInfo.branchStartNode;
Long branchStepId = branchInfo.branchStepId;
if (!TaskLogStatus.APPROVED.equalsCode(branchStartNode.getStatus())&&!TaskLogStatus.IN_PROGRESS.equalsCode(branchStartNode.getStatus())) {
log.warn("分支节点 {} (stepId={}) 状态不是通过或进行中,无法处理",branchStepId);
continue;
}
// 判断 targetStepId 是否在分支开始节点之前
boolean isBeforeBranchStart = isStepBeforeBranchStart(targetStepId.longValue(),
branchStartNode.getStepId(), stepMap);
log.info("判断 targetStepId 是否在分支开始节点之前: targetStepId={}, branchStartNodeId={}, isBeforeBranchStart={}",
targetStepId, branchStartNode.getStepId(), isBeforeBranchStart);
TaskLogDO branchStep = stepMap.get(branchStepId);
if (branchStep == null) {
log.warn("未找到分支步骤: branchStepId={}", branchStepId);
continue;
}
if (isBeforeBranchStart) {
// targetStepId 在分支开始之后或就是分支开始,将分支步骤设置为未开始
if (!TaskLogStatus.NOT_STARTED.equalsCode(branchStep.getStatus())&& !TaskLogStatus.SKIPPED.equalsCode(branchStep.getStatus())) {
Integer oldStatus = branchStep.getStatus();
branchStep.setStatus(TaskLogStatus.NOT_STARTED.getCode()); // -99 未开始
log.info("分支步骤 {} (stepId={}) 在目标步骤之前,状态从 {} 改为未开始(-99)",
branchStep.getStepName(), branchStepId, oldStatus);
}
} else {
// targetStepId 在分支开始之前,将分支步骤设置为进行中
if (!TaskLogStatus.IN_PROGRESS.equalsCode(branchStep.getStatus()) && !TaskLogStatus.SKIPPED.equalsCode(branchStep.getStatus())) {
Integer oldStatus = branchStep.getStatus();
branchStep.setStatus(DRAFT_STATUS); // 0 进行中
log.info("分支步骤 {} (stepId={}) 在目标步骤之后或同时,状态从 {} 改为进行中(0)",
branchStep.getStepName(), branchStepId, oldStatus);
}
}
}
}
/**
*
*
*/
private boolean isStepBeforeBranchStart(Long targetStepId, Long branchStartStepId,
Map<Long, TaskLogDO> stepMap) {
if (targetStepId.equals(branchStartStepId)) {
// 目标步骤就是分支开始节点,视为"在分支开始之后"
return false;
}
// 从目标步骤开始遍历,看是否能到达分支开始节点
Long currentStepId = targetStepId;
Set<Long> visited = new HashSet<>();
visited.add(currentStepId);
while (currentStepId != null) {
TaskLogDO currentStep = stepMap.get(currentStepId);
if (currentStep == null) {
log.warn("未找到步骤: stepId={}", currentStepId);
break;
}
Long nextStepId = currentStep.getNextStep();
// 如果下一步是分支开始节点,说明 targetStepId 在分支开始之前
if (branchStartStepId.equals(nextStepId)) {
log.info("目标步骤 {} (stepId={}) 在分支开始节点之前", currentStep.getStepName(), currentStepId);
return true;
}
// 防止循环
if (nextStepId == null || visited.contains(nextStepId)) {
log.info("无法到达分支开始节点,目标步骤 {} (stepId={}) 在分支开始之后或同时",
currentStep.getStepName(), currentStepId);
break;
}
visited.add(nextStepId);
currentStepId = nextStepId;
}
// 无法到达分支开始节点,说明 targetStepId 在分支开始之后
return false;
}
/**
* task_log task_log_archive
*/
private void archiveTaskLogs(List<TaskLogE> allLogs) {
List<TaskLogArchiveDO> archiveList = new ArrayList<>();
log.info("开始归档 {} 条 task_log 记录", allLogs.size());
for (TaskLogE log : allLogs) {
// 只归档已处理的记录(状态不是未开始)
if (!TaskLogStatus.NOT_STARTED.equalsCode(log.getStatus())) {
// 只归档已处理的记录(状态不是未开始),暂时全部归档
// if (!TaskLogStatus.NOT_STARTED.equalsCode(log.getStatus())) {
TaskLogArchiveDO archiveDO = new TaskLogArchiveDO();
BeanUtils.copyProperties(log, archiveDO, "id");
BeanUtils.copyProperties(log, archiveDO, "id", "update_id", "update_time");
archiveList.add(archiveDO);
}
// }
}
if (!archiveList.isEmpty()) {
@ -1203,15 +1562,21 @@ public class TaskLogUpdateExe {
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());
Integer stepType = signInfo.getStepType();
if (!STEP_TYPE.equals(stepType)) {
signStepLog.setSign(
signInfo.getActorField(),
signInfo.getActUserDepartment(),
signInfo.getActUserDepartmentName(),
signInfo.getActUser(),
signInfo.getActUserName()
);
addActionLog(actionLogs, signStepLog);
log.info("已设置签字人: step={}, user={}", signStepLog.getStepName(), signInfo.getActUserName());
}else{
log.info("延时监火已跳过设置多人签字人: stepName={}", signStepLog.getStepName());
}
}
}
@ -1257,15 +1622,21 @@ public class TaskLogUpdateExe {
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());
Integer stepType = signInfo.getStepType();
if (!STEP_TYPE.equals(stepType)) {
taskLogE.setSign(
signInfo.getActorField(),
signInfo.getActUserDepartment(),
signInfo.getActUserDepartmentName(),
signInfo.getActUser(),
signInfo.getActUserName()
);
addActionLog(actionLogs, taskLogE);
log.info("已设置多人签字人: stepName={}, userName={}", taskLogE.getStepName(), signInfo.getActUserName());
}else{
log.info("延时监火已跳过设置多人签字人: stepName={}", taskLogE.getStepName());
}
}
}
}
@ -1310,6 +1681,7 @@ public class TaskLogUpdateExe {
*/
private void sendTodoCompleteEvent(Long taskLogId) {
try {
log.info("发送待办完成事件: taskLogId={}", taskLogId);
TodoListCompleteEvent event = new TodoListCompleteEvent();
event.setForeignSubsidiaryKey(taskLogId);
todoListEventPusherUtil.sendMessageCompleteEvent(event);
@ -1478,7 +1850,7 @@ public class TaskLogUpdateExe {
* eightworkInfo.info
*
*/
private void updateEightworkInfo(String workId, List<TaskLogDO> actionLogs) {
/* private void updateEightworkInfo(String workId, List<TaskLogDO> actionLogs) {
if (actionLogs == null || actionLogs.isEmpty()) {
return;
}
@ -1546,8 +1918,36 @@ public class TaskLogUpdateExe {
stepInfo.put("otherParams", logDO.getOtherParams());
}
}
// 只处理signInfo.getStepId() =3的数据其他的直接put替换,如果对应的infoJson.get(baseKey) 存在则替换baseKey 只有step_3和step_3-1
String baseKey = "step_" + logDO.getStepId();
if (logDO.getMultipleFlag() != null && logDO.getMultipleFlag() == 1) {
// 多人签字步骤先删除该步骤的所有旧记录step_3, step_3_1, step_3_2...
removeStepKeys(infoJson, baseKey);
infoJson.put("step_" + logDO.getStepId(), stepInfo);
// 然后查找下一个可用的序号并添加
if (infoJson.containsKey(baseKey)) {
// 如果基础key已存在理论上不会因为刚删除了
int suffix = 1;
String newKey;
do {
newKey = baseKey + "_" + suffix;
suffix++;
} while (infoJson.containsKey(newKey));
infoJson.put(newKey, stepInfo);
log.info("多人签字步骤 {} 追加为新键: {}", logDO.getStepId(), newKey);
} else {
// 直接使用基础key
infoJson.put(baseKey, stepInfo);
log.info("多人签字步骤添加新记录: {}", baseKey);
}
}else{
// 非多人签字步骤直接替换put
infoJson.put(baseKey, stepInfo);
log.info("更新步骤: {}", baseKey);
}
// infoJson.put("step_" + logDO.getStepId(), stepInfo);
}
// 更新到数据库
@ -1555,6 +1955,214 @@ public class TaskLogUpdateExe {
eightworkInfoRepository.updateById(infoDO);
log.info("已批量更新步骤 info: workId={}, count={}", workId, actionLogs.size());
}*/
private void updateEightworkInfo(String workId, List<TaskLogDO> actionLogs) {
if (actionLogs == null || actionLogs.isEmpty()) {
return;
}
EightworkInfoDO infoDO = eightworkInfoRepository.getOne(
new LambdaQueryWrapper<EightworkInfoDO>()
.eq(EightworkInfoDO::getWorkId, workId)
);
if (infoDO == null) {
log.warn("未找到作业信息: workId={}", workId);
return;
}
// 解析现有的 info
JSONObject infoJson;
if (StringUtils.isNotBlank(infoDO.getInfo())) {
try {
infoJson = JSONObject.parseObject(infoDO.getInfo());
} catch (Exception e) {
log.warn("解析 info 失败,使用空对象: workId={}", workId, e);
infoJson = new JSONObject();
}
} else {
infoJson = new JSONObject();
}
// 循环更新本次变化的步骤
for (TaskLogDO logDO : actionLogs) {
String baseKey = "step_" + logDO.getStepId();
// 判断是否为多人签字步骤
if (logDO.getMultipleFlag() != null && logDO.getMultipleFlag() == 1) {
// 多人签字步骤:根据 actUser 查找并更新,或追加新记录
updateOrAppendMultipleSignStepByUser(infoJson, baseKey, logDO);
} else {
// 普通步骤:直接替换
JSONObject stepInfo = buildStepInfo(logDO);
infoJson.put(baseKey, stepInfo);
log.info("更新普通步骤: {}", baseKey);
}
}
// 更新到数据库
infoDO.setInfo(infoJson.toJSONString());
eightworkInfoRepository.updateById(infoDO);
log.info("已批量更新步骤 info: workId={}, count={}", workId, actionLogs.size());
}
/**
* actUser
*
* 1. keystep_X, step_X_1, step_X_2...
* 2. actUser
* 3.
*
* @param infoJson JSON
* @param baseKey key "step_3"
* @param logDO
*/
private void updateOrAppendMultipleSignStepByUser(JSONObject infoJson, String baseKey, TaskLogDO logDO) {
JSONObject stepInfo = buildStepInfo(logDO);
Long targetActUser = logDO.getActUser();
// 如果 actUser 为空,直接追加
if (targetActUser == null) {
appendNewMultipleSignRecord(infoJson, baseKey, stepInfo);
return;
}
// 1. 遍历所有相关key查找是否有相同 actUser 的记录
boolean found = false;
for (String key : infoJson.keySet()) {
// 只处理匹配的key
if (!key.equals(baseKey) && !key.startsWith(baseKey + "_")) {
continue;
}
Object value = infoJson.get(key);
if (value instanceof JSONObject) {
JSONObject existingStep = (JSONObject) value;
Long existingActUser = existingStep.getLong("actUser");
// 找到相同 actUser 的记录,更新它
if (targetActUser.equals(existingActUser)) {
infoJson.put(key, stepInfo);
log.info("更新多人签字步骤记录: stepId={}, actUser={}, key={}",
logDO.getStepId(), targetActUser, key);
found = true;
break;
}
}
}
// 2. 如果没有找到相同 actUser 的记录,追加为新记录
if (!found) {
appendNewMultipleSignRecord(infoJson, baseKey, stepInfo);
}
}
/**
*
* baseKey 使 baseKey使 baseKey_1, baseKey_2...
*
* @param infoJson JSON
* @param baseKey key
* @param stepInfo
*/
private void appendNewMultipleSignRecord(JSONObject infoJson, String baseKey, JSONObject stepInfo) {
if (!infoJson.containsKey(baseKey)) {
// 基础key不存在直接使用
infoJson.put(baseKey, stepInfo);
log.info("多人签字步骤添加首条记录: {}", baseKey);
} else {
// 基础key已存在查找下一个可用序号
int suffix = 1;
String newKey;
do {
newKey = baseKey + "_" + suffix;
suffix++;
} while (infoJson.containsKey(newKey));
infoJson.put(newKey, stepInfo);
log.info("多人签字步骤追加新记录: key={}", newKey);
}
}
/**
* JSON
*/
private JSONObject buildStepInfo(TaskLogDO logDO) {
JSONObject stepInfo = new JSONObject();
stepInfo.put("stepName", logDO.getStepName());
stepInfo.put("actUserDepartment", logDO.getActUserDepartment());
stepInfo.put("actUserDepartmentName", logDO.getActUserDepartmentName());
stepInfo.put("actUser", logDO.getActUser());
stepInfo.put("actUserName", logDO.getActUserName());
stepInfo.put("signPath", logDO.getSignPath());
stepInfo.put("signTime", DateUtil.format(new Date(), DatePattern.NORM_DATETIME_PATTERN));
stepInfo.put("status", logDO.getStatus());
// 定位步骤,添加经纬度信息
if (logDO.getLatitude() != null || logDO.getLongitude() != null) {
JSONObject location = new JSONObject();
location.put("latitude", logDO.getLatitude());
location.put("longitude", logDO.getLongitude());
stepInfo.put("location", location);
}
// 附件步骤,添加附件信息
if (StringUtils.isNotBlank(logDO.getFilePath())) {
stepInfo.put("filePath", logDO.getFilePath());
}
// 有填写意见步骤,添加意见
if (StringUtils.isNotBlank(logDO.getRemarks())) {
stepInfo.put("remarks", logDO.getRemarks());
}
// 其他自定义步骤,添加 otherParams
if (StringUtils.isNotBlank(logDO.getOtherParams())) {
try {
JSONObject otherParams = JSONObject.parseObject(logDO.getOtherParams());
stepInfo.put("otherParams", otherParams);
} catch (Exception e) {
// 如果不是JSON格式直接存储字符串
stepInfo.put("otherParams", logDO.getOtherParams());
}
}
return stepInfo;
}
/**
* key
* baseKey = "step_3" step_3, step_3_1, step_3_2...
*
* @param infoJson JSON
* @param baseKey key "step_3"
*/
private void removeStepKeys(JSONObject infoJson, String baseKey) {
if (infoJson == null || baseKey == null || baseKey.isEmpty()) {
return;
}
// 收集需要删除的key
List<String> keysToRemove = new ArrayList<>();
// 遍历所有key找出匹配的
for (String key : infoJson.keySet()) {
// 匹配规则:
// 1. 完全匹配key.equals(baseKey) -> "step_3"
// 2. 前缀匹配key.startsWith(baseKey + "_") -> "step_3_1", "step_3_2"
if (key.equals(baseKey) || key.startsWith(baseKey + "_")) {
keysToRemove.add(key);
}
}
// 批量删除
for (String key : keysToRemove) {
infoJson.remove(key);
log.debug("删除旧记录: {}", key);
}
if (!keysToRemove.isEmpty()) {
log.info("已删除步骤 {} 的 {} 条旧记录", baseKey, keysToRemove.size());
}
}
/**

View File

@ -1,5 +1,6 @@
package com.zcloud.eightwork.command.convertor;
import com.zcloud.eightwork.domain.model.TaskLogE;
import com.zcloud.eightwork.dto.clientobject.TaskLogCO;
import com.zcloud.eightwork.persistence.dataobject.TaskLogDO;
import org.mapstruct.Mapper;
@ -21,5 +22,7 @@ public interface TaskLogCoConvertor {
*/
List<TaskLogCO> converDOsToCOs(List<TaskLogDO> taskLogDOs);
TaskLogCO converDOToCO(TaskLogDO taskLogDO);
List<TaskLogDO> converEsToDOs(List<TaskLogE> allLogs);
}

View File

@ -48,9 +48,9 @@ public class EightworkInfoQueryExe {
* @return
*/
public PageResponse<EightworkInfoCO> execute(EightworkInfoPageQry eightworkInfoPageQry) {
if(eightworkInfoPageQry.getCorpinfoId()==null){
/* if(eightworkInfoPageQry.getCorpinfoId()==null){
eightworkInfoPageQry.setCorpinfoId(AuthContext.getTenantId());
}
}*/
Map<String, Object> params = PageQueryHelper.toHashMap(eightworkInfoPageQry);
PageResponse<EightworkInfoDO> pageResponse = eightworkInfoRepository.listPage(params);
List<EightworkInfoCO> examCenterCOS = eightworkInfoCoConvertor.converDOsToCOs(pageResponse.getData());

View File

@ -204,6 +204,8 @@ public class TaskFlowChartQueryExe {
co.setStatus(log.getStatus());
co.setStepName(log.getStepName());
co.setActUserName(log.getActUserName());
co.setActUserDepartment(log.getActUserDepartment());
co.setActUserDepartmentName(log.getActUserDepartmentName());
co.setUpdateTime(log.getUpdateTime());
return co;

View File

@ -1,6 +1,7 @@
package com.zcloud.eightwork.dto;
import com.alibaba.cola.dto.PageQuery;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@ -35,6 +36,7 @@ public class EightworkInfoPageQry extends PageQuery {
private String likeWorkContent;
private Long eqDepartmentId;
private Long eqCreateId;
private Long eqTenantId;
private Integer eqXgfFlag;
private Integer eqInternalOperationFlag;
private Long eqProjectId;

View File

@ -4,6 +4,8 @@ import com.alibaba.cola.dto.PageQuery;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
* web-client
@ -27,5 +29,46 @@ public class TaskLogPageQry extends PageQuery {
private String eqWorkType;
private Integer eqStatus;
private Integer eqStepId;
private List<Long> inCorpInfoId;
private Long corpinfoId;
private String eqCheckNo;
private String eqWorkLevel;
private String likeWorkContent;
private Long eqDepartmentId;
private Long eqCreateId;
private Integer eqXgfFlag;
private Integer eqInternalOperationFlag;
private Long eqProjectId;
private Long eqWorkDepartmentId;
private Long eqWorkUserId;
private List<Long> inDepartmentId;
private String leCreateTime;
private String geCreateTime;
private String leWorkStartTime;
private String geWorkStartTime;
private String likeCreateName;
/**
* ID
*/
private Long eqCurrentStepId;
/**
*
*/
private Integer eqIsInnerWork;
private String likeLimitedSpaceNameAndCode;
/**
*
*/
private String eqBlindboardWorkType;
}

View File

@ -27,6 +27,8 @@ public class TaskSignStepInfoCmd extends Command {
//操作人
@ApiModelProperty(value = "操作人编码")
private String actorField;
@ApiModelProperty(value = "延时监火使用,1:正常流程,2:任务转发")
private Integer stepType;
//操作人部门
@ApiModelProperty(value = "操作人部门")
private Long actUserDepartment;

View File

@ -34,6 +34,13 @@ public class TaskFlowChartCO {
@ApiModelProperty(value = "签字人姓名")
private String actUserName;
//操作人部门
@ApiModelProperty(value = "操作人部门")
private Long actUserDepartment;
//操作人部门
@ApiModelProperty(value = "操作人部门名称")
private String actUserDepartmentName;
@ApiModelProperty(value = "更新时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;

View File

@ -53,7 +53,7 @@ public class TaskLogCO extends ClientObject {
@ApiModelProperty(value = "操作人")
private String actUserName;
//-99未开始0进行中1通过2打回
@ApiModelProperty(value = "-99未开始0进行中1通过2打回")
@ApiModelProperty(value = "-99未开始-1跳过0进行中1通过2打回")
private Integer status;
//签字图片路径
@ApiModelProperty(value = "签字图片路径")

View File

@ -68,5 +68,7 @@ public class TaskFlowE extends BaseE {
private Long blockingStepId;
//手机端组件名称special_step_code==other 时使用)
private String componentName;
@ApiModelProperty(value = "打回步骤id")
private Integer rejectStepId;
}

View File

@ -6,6 +6,7 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
/**
* web-domain
@ -55,6 +56,8 @@ public class TaskLogE extends BaseE {
private Integer stepType;
//是否签字步骤1是
private Integer signStepFlag;
private LocalDateTime signTime;
//选择级别1部门2人员
private Integer selectLevel;
//是否可选相关方0否1是
@ -101,6 +104,8 @@ public class TaskLogE extends BaseE {
private String otherParams;
//手机端组件名称special_step_code==other 时使用)
private String componentName;
@ApiModelProperty(value = "打回步骤id")
private Integer rejectStepId;
public TaskLogE(TaskLogE log) {
this.taskLogId = log.getTaskLogId();
@ -141,6 +146,7 @@ public class TaskLogE extends BaseE {
this.filePath = log.getFilePath();
this.otherParams = log.getOtherParams();
this.componentName = log.getComponentName();
this.rejectStepId = log.getRejectStepId();
}

View File

@ -68,6 +68,7 @@ public class TaskLogGatewayImpl implements TaskLogGateway {
return list.stream().map(d -> {
TaskLogE taskLog = new TaskLogE();
BeanUtils.copyProperties(d, taskLog);
taskLog.setUpdateTime(d.getSignTime());
return taskLog;
}).collect(Collectors.toList());
}

View File

@ -96,6 +96,9 @@ public class TaskFlowDO extends BaseDO {
@ApiModelProperty(value = "手机端组件名称")
private String componentName;
@ApiModelProperty(value = "打回步骤id")
private Integer rejectStepId;
}

View File

@ -142,6 +142,8 @@ public class TaskLogArchiveDO extends BaseDO {
//手机端组件名称special_step_code==other 时使用)
@ApiModelProperty(value = "手机端组件名称")
private String componentName;
@ApiModelProperty(value = "打回步骤id")
private Integer rejectStepId;
public TaskLogArchiveDO(String taskLogId) {
this.taskLogId = taskLogId;

View File

@ -8,6 +8,7 @@ import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
/**
* web-infrastructure
@ -59,6 +60,7 @@ public class TaskLogDO extends BaseDO {
//签字图片路径
@ApiModelProperty(value = "签字图片路径")
private String signPath;
private LocalDateTime signTime;
//任务类型
@ApiModelProperty(value = "任务类型")
private String workType;
@ -146,6 +148,8 @@ public class TaskLogDO extends BaseDO {
//手机端组件名称special_step_code==other 时使用)
@ApiModelProperty(value = "手机端组件名称")
private String componentName;
@ApiModelProperty(value = "打回步骤id")
private Integer rejectStepId;
public TaskLogDO(String taskLogId) {
this.taskLogId = taskLogId;

View File

@ -24,5 +24,7 @@ public interface MeasuresLogsRepository extends BaseRepository<MeasuresLogsDO> {
* @return
*/
int physicalDeleteByWorkId(String workId);
void deleteByWorkId(String workId);
}

View File

@ -12,6 +12,7 @@ import com.zcloud.eightwork.persistence.repository.MeasuresLogsRepository;
import com.zcloud.gbscommon.utils.PageQueryHelper;
import com.zcloud.gbscommon.utils.Query;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
@ -24,6 +25,7 @@ import java.util.Map;
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class MeasuresLogsRepositoryImpl extends BaseRepositoryImpl<MeasuresLogsMapper, MeasuresLogsDO> implements MeasuresLogsRepository {
private final MeasuresLogsMapper measuresLogsMapper;
@ -52,5 +54,19 @@ public class MeasuresLogsRepositoryImpl extends BaseRepositoryImpl<MeasuresLogsM
// 使用 Mapper 中定义的物理删除 SQL
return measuresLogsMapper.physicalDeleteByWorkId(workId);
}
@Override
public void deleteByWorkId(String workId) {
if (workId == null || workId.isEmpty()) {
log.warn("workId 为空,跳过删除操作");
return;
}
LambdaQueryWrapper<MeasuresLogsDO> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(MeasuresLogsDO::getWorkId, workId);
int deletedCount = measuresLogsMapper.delete(queryWrapper);
log.info("逻辑删除安全措施记录: workId={}, 删除数量={}", workId, deletedCount);
}
}

View File

@ -64,7 +64,7 @@
and t.create_name like concat('%', #{params.likeCreateName}, '%')
</if>
<if test="params.likeWorkContent != null and params.likeWorkContent != ''">
and t.info->>'$.workContent' like concat('%', #{params.likeCreateName}, '%')
and t.info->>'$.workContent' like concat('%', #{params.likeWorkContent}, '%')
</if>
<if test="params.eqBlindboardWorkType != null and params.eqBlindboardWorkType != ''">
and t.info->>'$.blindboardWorkType' = #{params.eqBlindboardWorkType}
@ -93,6 +93,11 @@
</if>
<if test="params.corpinfoId != null">
and t.corpinfo_id = #{params.corpinfoId}
</if>
<if test="params.eqCreateId != null">
and t.create_id = #{params.eqCreateId}
</if> <if test="params.eqTenantId != null">
and t.tenant_id = #{params.eqTenantId}
</if>
ORDER BY
CASE WHEN t.status = 0 THEN 0 ELSE 1 END ASC,

View File

@ -7,14 +7,14 @@
<select id="listAllByWorkType" resultType="com.zcloud.eightwork.persistence.dataobject.TaskFlowDO">
select f.* from task_flow f
left join eightwork_task t on t.id = f.task_id
<where>
where
f.delete_enum='FALSE' and t.delete_enum='FALSE'
<if test="workType != null">
and t.work_type = #{workType}
</if>
<if test="workLevel != null">
and t.work_level = #{workLevel}
</if>
</where>
order by step_order
</select>

View File

@ -6,8 +6,8 @@
<select id="listPage" resultType="com.zcloud.eightwork.persistence.dataobject.TaskLogDO">
select task_log.*
from task_log
left join eightwork_info on task_log.work_id = eightwork_info.work_id
where eightwork_info.delete_enum = 'FALSE'
left join eightwork_info t on task_log.work_id = t.work_id
where t.delete_enum = 'FALSE' and task_log.delete_enum='FALSE'
<if test="params.eqWorkType != null">
and task_log.work_type = #{params.eqWorkType}
</if>
@ -17,6 +17,84 @@
<if test="params.eqStepId != null">
and task_log.step_id = #{params.eqStepId}
</if>
<if test="params.eqWorkLevel != null and params.eqWorkLevel != ''">
and t.work_level = #{params.eqWorkLevel}
</if>
<if test="params.eqDepartmentId != null">
and t.department_id = #{params.eqDepartmentId}
</if>
<if test="params.eqXgfFlag != null">
and t.xgf_flag = #{params.eqXgfFlag}
</if>
<if test="params.eqInternalOperationFlag != null">
and t.internal_operation_flag = #{params.eqInternalOperationFlag}
</if>
<if test="params.eqProjectId != null and params.eqProjectId != ''">
and t.project_id = #{params.eqProjectId}
</if>
<if test="params.eqWorkDepartmentId != null">
and exists (select 1
from task_log log
where log.work_id = t.work_id
and log.act_user_department = #{params.eqWorkDepartmentId})
</if>
<if test="params.eqWorkUserId != null">
and exists (select 1
from task_log log
where log.work_id = t.work_id
and log.act_user = #{params.eqWorkUserId})
</if>
<if test="params.inDepartmentId != null and params.inDepartmentId.size() > 0">
and t.department_id in
<foreach collection="params.inDepartmentId" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="params.geCreateTime != null and params.geCreateTime != ''">
AND t.create_time >= DATE_FORMAT(CONCAT(#{params.geCreateTime}, ' 00:00:00'), '%Y-%m-%d %H:%i:%s')
</if>
<if test="params.leCreateTime != null and params.leCreateTime != ''">
AND t.create_time &lt; DATE_FORMAT(DATE_ADD(#{params.leCreateTime}, INTERVAL 1 DAY), '%Y-%m-%d %H:%i:%s')
</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>
<if test="params.likeWorkContent != null and params.likeWorkContent != ''">
and t.info->>'$.workContent' like concat('%', #{params.likeWorkContent}, '%')
</if>
<if test="params.eqBlindboardWorkType != null and params.eqBlindboardWorkType != ''">
and t.info->>'$.blindboardWorkType' = #{params.eqBlindboardWorkType}
</if>
<if test="params.geWorkStartTime != null and params.geWorkStartTime != ''">
and t.info->>'$.workStartTime' &gt;= DATE_FORMAT(CONCAT(#{params.geWorkStartTime}, ' 00:00:00'), '%Y-%m-%d %H:%i:%s')
</if>
<if test="params.leWorkStartTime != null and params.leWorkStartTime != ''">
and t.info->>'$.workStartTime' &lt; DATE_FORMAT(DATE_ADD(#{params.leWorkStartTime}, INTERVAL 1 DAY), '%Y-%m-%d %H:%i:%s')
</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
where log.work_id = t.work_id
and log.status = 0
and log.step_id = #{params.eqCurrentStepId})
</if>
<if test="params.inCorpInfoId != null and params.inCorpInfoId.size() > 0">
and t.corpinfo_id in
<foreach collection="params.inCorpInfoId" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="params.corpinfoId != null">
and t.corpinfo_id = #{params.corpinfoId}
</if>
and (task_log.act_user = #{params.userId}
or (task_log.act_user_department = #{params.departmentId}
and task_log.act_user is null))
@ -28,7 +106,7 @@
count(1) as todoCount
from task_log
left join eightwork_info on task_log.work_id = eightwork_info.work_id
where task_log.status = 0
where task_log.status = 0 and task_log.delete_enum = 'FALSE'
and eightwork_info.status != 0
and eightwork_info.delete_enum = 'FALSE'
and (task_log.act_user = #{userId}
@ -43,7 +121,7 @@
count(1) as todoCount
from task_log
left join eightwork_info on task_log.work_id = eightwork_info.work_id
where task_log.status = 0
where task_log.status = 0 and task_log.delete_enum = 'FALSE'
and task_log.work_type = #{workType}
and eightwork_info.status != 0
and eightwork_info.delete_enum = 'FALSE'
@ -56,7 +134,7 @@
<select id="getByWorkIdAndStepId" resultType="com.zcloud.eightwork.persistence.dataobject.TaskLogDO">
select *
from task_log
where work_id = #{workId}
where delete_enum = 'FALSE' and work_id = #{workId}
and step_id = #{stepId}
limit 1
</select>