初始化

This commit is contained in:
2026-02-22 18:56:10 +08:00
commit 26677972a6
3112 changed files with 255972 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
package tech.easyflow.job.config;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan("tech.easyflow.job.mapper")
public class JobModuleConfig {
public JobModuleConfig() {
System.out.println("启用模块 >>>>>>>>>> module-job");
}
}

View File

@@ -0,0 +1,15 @@
package tech.easyflow.job.entity;
import com.mybatisflex.annotation.Table;
import tech.easyflow.job.entity.base.SysJobBase;
/**
* 系统任务表 实体类。
*
* @author xiaoma
* @since 2025-05-20
*/
@Table(value = "tb_sys_job", comment = "系统任务表")
public class SysJob extends SysJobBase {
}

View File

@@ -0,0 +1,15 @@
package tech.easyflow.job.entity;
import com.mybatisflex.annotation.Table;
import tech.easyflow.job.entity.base.SysJobLogBase;
/**
* 系统任务日志 实体类。
*
* @author xiaoma
* @since 2025-05-20
*/
@Table(value = "tb_sys_job_log", comment = "系统任务日志")
public class SysJobLog extends SysJobLogBase {
}

View File

@@ -0,0 +1,242 @@
package tech.easyflow.job.entity.base;
import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.core.handler.FastjsonTypeHandler;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.Date;
import java.util.Map;
import tech.easyflow.common.entity.DateEntity;
public class SysJobBase extends DateEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@Id(keyType = KeyType.Generator, value = "snowFlakeId", comment = "主键")
private BigInteger id;
/**
* 部门ID
*/
@Column(comment = "部门ID")
private BigInteger deptId;
/**
* 租户ID
*/
@Column(tenantId = true, comment = "租户ID")
private BigInteger tenantId;
/**
* 任务名称
*/
@Column(comment = "任务名称")
private String jobName;
/**
* 任务类型
*/
@Column(comment = "任务类型")
private Integer jobType;
/**
* 任务参数
*/
@Column(typeHandler = FastjsonTypeHandler.class, comment = "任务参数")
private Map<String, Object> jobParams;
/**
* cron表达式
*/
@Column(comment = "cron表达式")
private String cronExpression;
/**
* 是否并发执行
*/
@Column(comment = "是否并发执行")
private Integer allowConcurrent;
/**
* 错过策略
*/
@Column(comment = "错过策略")
private Integer misfirePolicy;
/**
* 其他配置
*/
@Column(typeHandler = FastjsonTypeHandler.class, comment = "其他配置")
private Map<String, Object> options;
/**
* 数据状态
*/
@Column(comment = "数据状态")
private Integer status;
/**
* 创建时间
*/
@Column(comment = "创建时间")
private Date created;
/**
* 创建者
*/
@Column(comment = "创建者")
private BigInteger createdBy;
/**
* 修改时间
*/
@Column(comment = "修改时间")
private Date modified;
/**
* 修改者
*/
@Column(comment = "修改者")
private BigInteger modifiedBy;
/**
* 备注
*/
@Column(comment = "备注")
private String remark;
public BigInteger getId() {
return id;
}
public void setId(BigInteger id) {
this.id = id;
}
public BigInteger getDeptId() {
return deptId;
}
public void setDeptId(BigInteger deptId) {
this.deptId = deptId;
}
public BigInteger getTenantId() {
return tenantId;
}
public void setTenantId(BigInteger tenantId) {
this.tenantId = tenantId;
}
public String getJobName() {
return jobName;
}
public void setJobName(String jobName) {
this.jobName = jobName;
}
public Integer getJobType() {
return jobType;
}
public void setJobType(Integer jobType) {
this.jobType = jobType;
}
public Map<String, Object> getJobParams() {
return jobParams;
}
public void setJobParams(Map<String, Object> jobParams) {
this.jobParams = jobParams;
}
public String getCronExpression() {
return cronExpression;
}
public void setCronExpression(String cronExpression) {
this.cronExpression = cronExpression;
}
public Integer getAllowConcurrent() {
return allowConcurrent;
}
public void setAllowConcurrent(Integer allowConcurrent) {
this.allowConcurrent = allowConcurrent;
}
public Integer getMisfirePolicy() {
return misfirePolicy;
}
public void setMisfirePolicy(Integer misfirePolicy) {
this.misfirePolicy = misfirePolicy;
}
public Map<String, Object> getOptions() {
return options;
}
public void setOptions(Map<String, Object> options) {
this.options = options;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public BigInteger getCreatedBy() {
return createdBy;
}
public void setCreatedBy(BigInteger createdBy) {
this.createdBy = createdBy;
}
public Date getModified() {
return modified;
}
public void setModified(Date modified) {
this.modified = modified;
}
public BigInteger getModifiedBy() {
return modifiedBy;
}
public void setModifiedBy(BigInteger modifiedBy) {
this.modifiedBy = modifiedBy;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
}

View File

@@ -0,0 +1,171 @@
package tech.easyflow.job.entity.base;
import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.core.handler.FastjsonTypeHandler;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.Date;
import java.util.Map;
public class SysJobLogBase implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@Id(keyType = KeyType.Generator, value = "snowFlakeId", comment = "主键")
private BigInteger id;
/**
* 任务ID
*/
@Column(comment = "任务ID")
private BigInteger jobId;
/**
* 任务名称
*/
@Column(comment = "任务名称")
private String jobName;
/**
* 任务参数
*/
@Column(typeHandler = FastjsonTypeHandler.class, comment = "任务参数")
private Map<String, Object> jobParams;
/**
* 执行结果
*/
@Column(comment = "执行结果")
private String jobResult;
/**
* 错误信息
*/
@Column(comment = "错误信息")
private String errorInfo;
/**
* 数据状态
*/
@Column(comment = "数据状态")
private Integer status;
/**
* 开始时间
*/
@Column(comment = "开始时间")
private Date startTime;
/**
* 结束时间
*/
@Column(comment = "结束时间")
private Date endTime;
/**
* 创建时间
*/
@Column(comment = "创建时间")
private Date created;
/**
* 备注
*/
@Column(comment = "备注")
private String remark;
public BigInteger getId() {
return id;
}
public void setId(BigInteger id) {
this.id = id;
}
public BigInteger getJobId() {
return jobId;
}
public void setJobId(BigInteger jobId) {
this.jobId = jobId;
}
public String getJobName() {
return jobName;
}
public void setJobName(String jobName) {
this.jobName = jobName;
}
public Map<String, Object> getJobParams() {
return jobParams;
}
public void setJobParams(Map<String, Object> jobParams) {
this.jobParams = jobParams;
}
public String getJobResult() {
return jobResult;
}
public void setJobResult(String jobResult) {
this.jobResult = jobResult;
}
public String getErrorInfo() {
return errorInfo;
}
public void setErrorInfo(String errorInfo) {
this.errorInfo = errorInfo;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public Date getStartTime() {
return startTime;
}
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
public Date getEndTime() {
return endTime;
}
public void setEndTime(Date endTime) {
this.endTime = endTime;
}
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
}

View File

@@ -0,0 +1,72 @@
package tech.easyflow.job.job;
import cn.hutool.core.exceptions.ExceptionUtil;
import com.alibaba.fastjson.JSON;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tech.easyflow.common.constant.enums.EnumJobExecStatus;
import tech.easyflow.common.util.SpringContextUtil;
import tech.easyflow.job.entity.SysJob;
import tech.easyflow.job.entity.SysJobLog;
import tech.easyflow.job.service.SysJobLogService;
import java.util.Date;
public abstract class BaseQuartzJob implements Job {
protected Logger log = LoggerFactory.getLogger(getClass());
private final static ThreadLocal<Date> TIME_RECORD = new ThreadLocal<>();
@Override
public void execute(JobExecutionContext ctx) throws JobExecutionException {
SysJob job = (SysJob) ctx.getMergedJobDataMap().get(JobConstant.JOB_MAP_BEAN_NAME);
try {
beforeExecute(ctx, job);
Object result = doExecute(ctx, job);
afterExecute(ctx, job, result, null);
} catch (Exception e) {
log.error("quartz 任务执行报错:", e);
afterExecute(ctx, job, null, e);
}
}
protected void beforeExecute(JobExecutionContext ctx, SysJob job) {
TIME_RECORD.set(new Date());
}
protected void afterExecute(JobExecutionContext ctx, SysJob job, Object result, Exception e) {
Date startTime = TIME_RECORD.get();
TIME_RECORD.remove();
Date endTime = new Date();
SysJobLog sysJobLog = new SysJobLog();
sysJobLog.setJobId(job.getId());
sysJobLog.setJobName(job.getJobName());
sysJobLog.setStatus(EnumJobExecStatus.SUCCESS.getCode());
sysJobLog.setJobParams(job.getJobParams());
if (result != null) {
sysJobLog.setJobResult(JSON.toJSONString(result));
}
if (e != null) {
String message = ExceptionUtil.getRootCauseMessage(e);
if (message.length() > 1000) {
message = message.substring(0, 1000);
}
sysJobLog.setErrorInfo(message);
sysJobLog.setStatus(EnumJobExecStatus.FAIL.getCode());
}
sysJobLog.setStartTime(startTime);
sysJobLog.setEndTime(endTime);
sysJobLog.setCreated(new Date());
SysJobLogService service = SpringContextUtil.getBean(SysJobLogService.class);
service.save(sysJobLog);
}
protected abstract Object doExecute(JobExecutionContext ctx, SysJob job) throws Exception;
}

View File

@@ -0,0 +1,13 @@
package tech.easyflow.job.job;
public interface JobConstant {
String JOB_GROUP = "easyflow";
String JOB_MAP_BEAN_NAME = "jobMapBean";
String BEAN_METHOD_KEY = "beanMethod";
String JAVA_METHOD_KEY = "javaMethod";
String WORKFLOW_KEY = "workflowId";
String WORKFLOW_PARAMS_KEY = "workflowParams";
String ACCOUNT_ID = "accountId";
}

View File

@@ -0,0 +1,16 @@
package tech.easyflow.job.job;
import org.quartz.JobExecutionContext;
import tech.easyflow.job.entity.SysJob;
import tech.easyflow.job.util.JobUtil;
/**
* 可并发执行
*/
public class QuartzJob extends BaseQuartzJob {
@Override
protected Object doExecute(JobExecutionContext ctx, SysJob job) throws Exception {
return JobUtil.execute(job);
}
}

View File

@@ -0,0 +1,18 @@
package tech.easyflow.job.job;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import tech.easyflow.job.entity.SysJob;
import tech.easyflow.job.util.JobUtil;
/**
* 禁止并发执行
*/
@DisallowConcurrentExecution
public class QuartzJobNoConcurrent extends BaseQuartzJob {
@Override
protected Object doExecute(JobExecutionContext ctx, SysJob job) throws Exception {
return JobUtil.execute(job);
}
}

View File

@@ -0,0 +1,14 @@
package tech.easyflow.job.mapper;
import com.mybatisflex.core.BaseMapper;
import tech.easyflow.job.entity.SysJobLog;
/**
* 系统任务日志 映射层。
*
* @author xiaoma
* @since 2025-05-20
*/
public interface SysJobLogMapper extends BaseMapper<SysJobLog> {
}

View File

@@ -0,0 +1,14 @@
package tech.easyflow.job.mapper;
import com.mybatisflex.core.BaseMapper;
import tech.easyflow.job.entity.SysJob;
/**
* 系统任务表 映射层。
*
* @author xiaoma
* @since 2025-05-20
*/
public interface SysJobMapper extends BaseMapper<SysJob> {
}

View File

@@ -0,0 +1,14 @@
package tech.easyflow.job.service;
import com.mybatisflex.core.service.IService;
import tech.easyflow.job.entity.SysJobLog;
/**
* 系统任务日志 服务层。
*
* @author xiaoma
* @since 2025-05-20
*/
public interface SysJobLogService extends IService<SysJobLog> {
}

View File

@@ -0,0 +1,24 @@
package tech.easyflow.job.service;
import com.mybatisflex.core.service.IService;
import tech.easyflow.job.entity.SysJob;
import java.io.Serializable;
import java.util.Collection;
/**
* 系统任务表 服务层。
*
* @author xiaoma
* @since 2025-05-20
*/
public interface SysJobService extends IService<SysJob> {
void test();
void testParam(String a, Boolean b, Integer c);
void addJob(SysJob job);
void deleteJob(Collection<Serializable> ids);
}

View File

@@ -0,0 +1,18 @@
package tech.easyflow.job.service.impl;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import tech.easyflow.job.entity.SysJobLog;
import tech.easyflow.job.mapper.SysJobLogMapper;
import tech.easyflow.job.service.SysJobLogService;
/**
* 系统任务日志 服务层实现。
*
* @author xiaoma
* @since 2025-05-20
*/
@Service
public class SysJobLogServiceImpl extends ServiceImpl<SysJobLogMapper, SysJobLog> implements SysJobLogService {
}

View File

@@ -0,0 +1,95 @@
package tech.easyflow.job.service.impl;
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.EnumMisfirePolicy;
import tech.easyflow.job.entity.SysJob;
import tech.easyflow.job.job.JobConstant;
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.util.JobUtil;
import javax.annotation.Resource;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.Collection;
/**
* 系统任务表 服务层实现。
*
* @author xiaoma
* @since 2025-05-20
*/
@Service
public class SysJobServiceImpl extends ServiceImpl<SysJobMapper, SysJob> implements SysJobService {
protected Logger log = LoggerFactory.getLogger(SysJobServiceImpl.class);
@Resource
private Scheduler scheduler;
@Override
public void test() {
System.out.println("java bean 动态执行");
}
@Override
public void testParam(String a, Boolean b, Integer c) {
System.out.println("动态执行spring bean执行参数" + "a="+ a + ",b="+ b + ",c="+ c);
}
@Override
public void addJob(SysJob job) {
Integer allowConcurrent = job.getAllowConcurrent();
Class<? extends Job> jobClass = allowConcurrent == 1 ? QuartzJob.class : QuartzJobNoConcurrent.class;
JobDetail jobDetail = JobBuilder.newJob(jobClass)
.withIdentity(JobUtil.getJobKey(job))
.build();
jobDetail.getJobDataMap().put(JobConstant.JOB_MAP_BEAN_NAME, job);
CronScheduleBuilder cron = CronScheduleBuilder.cronSchedule(job.getCronExpression());
Integer misfirePolicy = job.getMisfirePolicy();
if (EnumMisfirePolicy.MISFIRE_DO_NOTHING.getCode() == misfirePolicy) {
cron.withMisfireHandlingInstructionDoNothing();
}
if (EnumMisfirePolicy.MISFIRE_FIRE_AND_PROCEED.getCode() == misfirePolicy) {
cron.withMisfireHandlingInstructionFireAndProceed();
}
if (EnumMisfirePolicy.MISFIRE_IGNORE_MISFIRES.getCode() == misfirePolicy) {
cron.withMisfireHandlingInstructionIgnoreMisfires();
}
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(JobUtil.getTriggerKey(job))
.withSchedule(cron).build();
try {
scheduler.scheduleJob(jobDetail,trigger);
} catch (SchedulerException e) {
log.error("启动任务失败:", e);
throw new RuntimeException(e);
}
}
@Override
public void deleteJob(Collection<Serializable> ids) {
try {
for (Serializable id : ids) {
SysJob sysJob = new SysJob();
sysJob.setId(new BigInteger(id.toString()));
scheduler.deleteJob(JobUtil.getJobKey(sysJob));
}
} catch (SchedulerException e) {
log.error("删除任务失败:", e);
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,171 @@
package tech.easyflow.job.util;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.mybatisflex.core.tenant.TenantManager;
import com.easyagents.flow.core.chain.ChainDefinition;
import com.easyagents.flow.core.chain.runtime.ChainExecutor;
import org.quartz.JobKey;
import org.quartz.TriggerKey;
import tech.easyflow.common.constant.Constants;
import tech.easyflow.common.constant.enums.EnumJobType;
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.system.entity.SysAccount;
import tech.easyflow.system.service.SysAccountService;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
public class JobUtil {
/**
* sysJobService.test()
*/
public static Object execSpringBean(SysJob job) {
Map<String, Object> jobParams = job.getJobParams();
if (jobParams != null) {
String beanMethod = jobParams.get(JobConstant.BEAN_METHOD_KEY).toString();
String[] strings = StrUtil.subBefore(beanMethod, "(", false).split("\\.");
Object bean = SpringContextUtil.getBean(strings[0]);
String param = StrUtil.subBetween(beanMethod, "(", ")");
try {
// 调用方法并传递参数
return invoke(bean, strings[1], getParams(param));
} catch (Exception e) {
throw new RuntimeException("执行 beanMethod 报错:", e);
}
}
return null;
}
/**
* tech.easyflow.job.util.JobUtil.execTest("test",1,0.52D,100L)
* @param job
*/
public static Object execJavaClass(SysJob job) {
Map<String, Object> jobParams = job.getJobParams();
if (jobParams != null) {
try {
String javaMethod = jobParams.get(JobConstant.JAVA_METHOD_KEY).toString();
String before = StrUtil.subBefore(javaMethod, "(", false);
String[] strings = before.split("\\.");
String className = String.join(".", Arrays.copyOf(strings, strings.length - 1));
String methodName = strings[strings.length - 1];
String param = StrUtil.subBetween(javaMethod, "(", ")");
Object obj = Class.forName(className).getDeclaredConstructor().newInstance();;
return invoke(obj, methodName, getParams(param));
} catch (Exception e) {
throw new RuntimeException("执行 javaMethod 报错: ",e);
}
}
return null;
}
public static Object execWorkFlow(SysJob job) {
Map<String, Object> jobParams = job.getJobParams();
JSONObject obj = new JSONObject(jobParams);
String workflowId = obj.getString(JobConstant.WORKFLOW_KEY);
JSONObject params = obj.getJSONObject(JobConstant.WORKFLOW_PARAMS_KEY);
ChainExecutor executor = SpringContextUtil.getBean(ChainExecutor.class);
Object accountId = obj.get(JobConstant.ACCOUNT_ID);
SysAccountService accountService = SpringContextUtil.getBean(SysAccountService.class);
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);
}
} finally {
TenantManager.restoreTenantCondition();
}
return null;
}
public static Object execute(SysJob job) {
Object res = null;
Integer jobType = job.getJobType();
if (EnumJobType.TINY_FLOW.getCode() == jobType) {
res = execWorkFlow(job);
}
if (EnumJobType.SPRING_BEAN.getCode() == jobType) {
res = execSpringBean(job);
}
if (EnumJobType.JAVA_CLASS.getCode() == jobType) {
res = execJavaClass(job);
}
return res;
}
public void execTest(String a,Integer b,Double c,Long d) {
System.out.println("动态执行方法,执行参数:" + "a="+ a + ",b="+ b + ",c="+ c + ",d="+ d);
}
private static Object[] getParams(String param) {
if (StrUtil.isEmpty(param)) {
return new Object[]{new Class<?>[]{}, new Object[]{}};
}
String[] splits = param.split(",");
Object[] res = new Object[2];
Object[] params = new Object[splits.length];
Class<?>[] paramTypes = new Class[splits.length];
for (int i = 0; i < splits.length; i++) {
String split = splits[i].trim();
if (split.startsWith("\"")) {
params[i] = split.substring(1, split.length() - 1);
paramTypes[i] = String.class;
} else if ("true".equals(split) || "false".equals(split)) {
params[i] = Boolean.valueOf(split);
paramTypes[i] = Boolean.class;
} else if (split.endsWith("L")) {
params[i] = Long.valueOf(split.substring(0, split.length() - 1));
paramTypes[i] = Long.class;
} else if (split.endsWith("D")) {
params[i] = Double.valueOf(split.substring(0, split.length() - 1));
paramTypes[i] = Double.class;
} else if (split.endsWith("F")) {
params[i] = Float.valueOf(split.substring(0, split.length() - 1));
paramTypes[i] = Float.class;
} else {
params[i] = Integer.valueOf(split);
paramTypes[i] = Integer.class;
}
}
res[0] = paramTypes;
res[1] = params;
return res;
}
private static Object invoke(Object bean, String methodName, Object[] params) throws Exception {
Object[] args = (Object[]) params[1];
if (ArrayUtil.isEmpty(params[1])) {
Method method = bean.getClass().getDeclaredMethod(methodName);
return method.invoke(bean);
} else {
Method method = bean.getClass().getDeclaredMethod(methodName, (Class<?>[]) params[0]);
return method.invoke(bean, args);
}
}
public static JobKey getJobKey(SysJob job) {
return JobKey.jobKey(job.getId().toString(), JobConstant.JOB_GROUP);
}
public static TriggerKey getTriggerKey(SysJob job) {
return TriggerKey.triggerKey(job.getId().toString(), JobConstant.JOB_GROUP);
}
}