fix: 防止定时任务引用失效工作流

- 保存任务时校验工作流权限与必填参数

- 删除工作流前检查并重新确认定时任务引用
This commit is contained in:
2026-08-03 14:50:28 +08:00
parent 93db17b384
commit 8c334be65d
12 changed files with 817 additions and 43 deletions

View File

@@ -6,6 +6,7 @@ import tech.easyflow.job.entity.SysJob;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.Collection;
import java.util.List;
/**
* 系统任务表 服务层。
@@ -26,4 +27,12 @@ public interface SysJobService extends IService<SysJob> {
void startJob(BigInteger id);
void stopJob(BigInteger id);
/**
* 查询引用指定工作流的定时任务。
*
* @param workflowId 工作流 ID
* @return 引用该工作流的定时任务
*/
List<SysJob> listWorkflowJobsByWorkflowId(BigInteger workflowId);
}

View File

@@ -1,10 +1,12 @@
package tech.easyflow.job.service.impl;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import tech.easyflow.common.constant.enums.EnumJobType;
import tech.easyflow.common.constant.enums.EnumMisfirePolicy;
import tech.easyflow.common.constant.enums.EnumJobStatus;
import tech.easyflow.common.cache.RedisLockExecutor;
@@ -14,6 +16,7 @@ import tech.easyflow.job.job.QuartzJob;
import tech.easyflow.job.job.QuartzJobNoConcurrent;
import tech.easyflow.job.mapper.SysJobMapper;
import tech.easyflow.job.service.SysJobService;
import tech.easyflow.job.support.SysJobWorkflowReferenceSupport;
import tech.easyflow.job.util.JobUtil;
import javax.annotation.Resource;
@@ -22,6 +25,7 @@ import java.math.BigInteger;
import java.time.Duration;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* 系统任务表 服务层实现。
@@ -153,4 +157,19 @@ public class SysJobServiceImpl extends ServiceImpl<SysJobMapper, SysJob> implem
}
});
}
/**
* {@inheritDoc}
*/
@Override
public List<SysJob> listWorkflowJobsByWorkflowId(BigInteger workflowId) {
if (workflowId == null) {
return List.of();
}
QueryWrapper queryWrapper = QueryWrapper.create()
.eq(SysJob::getJobType, EnumJobType.TINY_FLOW.getCode());
return list(queryWrapper).stream()
.filter(job -> workflowId.equals(SysJobWorkflowReferenceSupport.resolveWorkflowId(job)))
.toList();
}
}

View File

@@ -0,0 +1,51 @@
package tech.easyflow.job.service.impl;
import org.springframework.stereotype.Component;
import tech.easyflow.ai.service.WorkflowScheduleReferenceProvider;
import tech.easyflow.ai.vo.OfflineImpactBindingVo;
import tech.easyflow.job.entity.SysJob;
import tech.easyflow.job.service.SysJobService;
import java.math.BigInteger;
import java.util.List;
/**
* 定时任务对工作流的引用查询实现。
*/
@Component
public class SysJobWorkflowReferenceProvider implements WorkflowScheduleReferenceProvider {
private final SysJobService sysJobService;
/**
* 创建定时任务工作流引用查询提供者。
*
* @param sysJobService 定时任务服务
*/
public SysJobWorkflowReferenceProvider(SysJobService sysJobService) {
this.sysJobService = sysJobService;
}
/**
* {@inheritDoc}
*/
@Override
public List<OfflineImpactBindingVo> listScheduledJobsByWorkflowId(BigInteger workflowId) {
return sysJobService.listWorkflowJobsByWorkflowId(workflowId).stream()
.map(this::toBinding)
.toList();
}
/**
* 将定时任务转换为删除影响摘要。
*
* @param job 定时任务
* @return 影响摘要
*/
private OfflineImpactBindingVo toBinding(SysJob job) {
OfflineImpactBindingVo binding = new OfflineImpactBindingVo();
binding.setId(job.getId());
binding.setTitle(job.getJobName());
return binding;
}
}

View File

@@ -0,0 +1,70 @@
package tech.easyflow.job.support;
import tech.easyflow.common.constant.enums.EnumJobType;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.job.entity.SysJob;
import tech.easyflow.job.job.JobConstant;
import java.math.BigInteger;
import java.util.Map;
/**
* 工作流类型定时任务的引用解析工具。
*/
public final class SysJobWorkflowReferenceSupport {
private SysJobWorkflowReferenceSupport() {
}
/**
* 判断任务是否为工作流类型。
*
* @param job 定时任务
* @return 工作流类型时为 true
*/
public static boolean isWorkflowJob(SysJob job) {
return job != null
&& Integer.valueOf(EnumJobType.TINY_FLOW.getCode()).equals(job.getJobType());
}
/**
* 解析工作流 ID参数缺失或格式非法时返回 null。
*
* @param job 定时任务
* @return 工作流 ID无法解析时为 null
*/
public static BigInteger resolveWorkflowId(SysJob job) {
if (!isWorkflowJob(job)) {
return null;
}
Map<String, Object> jobParams = job.getJobParams();
Object workflowId = jobParams == null ? null : jobParams.get(JobConstant.WORKFLOW_KEY);
if (workflowId == null) {
return null;
}
String value = String.valueOf(workflowId).trim();
if (value.isEmpty()) {
return null;
}
try {
return new BigInteger(value);
} catch (NumberFormatException ignored) {
return null;
}
}
/**
* 获取有效的工作流 ID。
*
* @param job 定时任务
* @return 工作流 ID
* @throws BusinessException 工作流参数缺失或格式非法时抛出
*/
public static BigInteger requireWorkflowId(SysJob job) {
BigInteger workflowId = resolveWorkflowId(job);
if (workflowId == null) {
throw new BusinessException("定时任务未配置有效工作流,请重新选择");
}
return workflowId;
}
}

View File

@@ -14,10 +14,12 @@ import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.util.SpringContextUtil;
import tech.easyflow.job.entity.SysJob;
import tech.easyflow.job.job.JobConstant;
import tech.easyflow.job.support.SysJobWorkflowReferenceSupport;
import tech.easyflow.system.entity.SysAccount;
import tech.easyflow.system.service.SysAccountService;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Map;
@@ -69,7 +71,7 @@ public class JobUtil {
public static Object execWorkFlow(SysJob job) {
Map<String, Object> jobParams = job.getJobParams();
JSONObject obj = new JSONObject(jobParams);
String workflowId = obj.getString(JobConstant.WORKFLOW_KEY);
BigInteger workflowId = SysJobWorkflowReferenceSupport.requireWorkflowId(job);
JSONObject params = obj.getJSONObject(JobConstant.WORKFLOW_PARAMS_KEY);
ChainExecutor executor = SpringContextUtil.getBean(ChainExecutor.class);
@@ -79,21 +81,21 @@ public class JobUtil {
try {
TenantManager.ignoreTenantCondition();
ChainDefinition chain = executor.getDefinitionRepository().getChainDefinitionById(workflowId);
if (chain != null) {
if (accountId != null) {
// 设置的归属者
SysAccount account = accountService.getById(accountId.toString());
if (account != null) {
params.put(Constants.LOGIN_USER_KEY, SaTokenUtil.getLoginAccount());
}
}
return executor.execute(workflowId, params);
ChainDefinition chain = executor.getDefinitionRepository().getChainDefinitionById(workflowId.toString());
if (chain == null) {
throw new IllegalStateException("定时任务关联的工作流不存在或已删除id=" + workflowId);
}
if (accountId != null) {
// 设置的归属者
SysAccount account = accountService.getById(accountId.toString());
if (account != null) {
params.put(Constants.LOGIN_USER_KEY, SaTokenUtil.getLoginAccount());
}
}
return executor.execute(workflowId.toString(), params);
} finally {
TenantManager.restoreTenantCondition();
}
return null;
}
public static Object execute(SysJob job) {