修改向定时任务写的接口,枚举类实现策略模式,接口层服务层实现业务分离。

This commit is contained in:
wsm
2026-04-20 10:27:49 +08:00
parent 042c34653d
commit 45df7a7f15
4 changed files with 133 additions and 76 deletions
@@ -1,5 +1,13 @@
package org.jeecg.modules.tasktask.constant; package org.jeecg.modules.tasktask.constant;
import lombok.Getter;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;
public final class FlowConstants { public final class FlowConstants {
private FlowConstants() { private FlowConstants() {
} }
@@ -13,27 +21,50 @@ public final class FlowConstants {
/** 延迟发起 */ /** 延迟发起 */
public static final Integer IMMEDIATE_AND_RECURRING = 3; public static final Integer IMMEDIATE_AND_RECURRING = 3;
} }
public static final class IntervalType { @Getter
public enum IntervalType {
EVERY_DAY(1, 1, ChronoUnit.DAYS, "每天"),
EVERY_WEEK(2, 1, ChronoUnit.WEEKS, "每周"),
EVERY_TWO_WEEKS(3, 2, ChronoUnit.WEEKS, "每两周"),
EVERY_MONTH(4, 1, ChronoUnit.MONTHS, "每月"),
EVERY_QUARTER(5, 3, ChronoUnit.MONTHS, "每季");
private final Integer code;
private final int amount; // 步长数量
private final ChronoUnit unit; // 时间单位
private final String description;
IntervalType(Integer code, int amount, ChronoUnit unit, String description) {
this.code = code;
this.amount = amount;
this.unit = unit;
this.description = description;
}
// 静态缓存:优化查找性能,避免重复遍历 values()
private static final Map<Integer, IntervalType> LOOKUP = Arrays.stream(values())
.collect(Collectors.toMap(IntervalType::getCode, it -> it));
/** /**
* 每天 * 根据 code 获取枚举对象
*/ */
public static final Integer EVERY_DAY = 1; public static IntervalType of(Integer code) {
IntervalType type = LOOKUP.get(code);
if (type == null) {
throw new IllegalArgumentException("未知的周期类型代码: " + code);
}
return type;
}
/** /**
* 每周 * 计算下一次执行时间
* @param base 基准时间
* @param step 第几次循环
*/ */
public static final Integer EVERY_WEEK = 2; public LocalDateTime nextTime(LocalDateTime base, int step) {
/** // 使用 base + (步长 * 循环次数) 避免累加误差
* 每两周 return base.plus((long) step * amount, unit);
*/ }
public static final Integer EVERY_TWO_WEEKS = 3;
/**
* 每月
*/
public static final Integer EVERY_MONTH = 4;
/**
* 每季
*/
public static final Integer EVERY_QUARTER = 5;
} }
public static final class taskCount{ public static final class taskCount{
public static final Integer MAX = 24; public static final Integer MAX = 24;
@@ -10,6 +10,7 @@ import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result; import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator; import org.jeecg.common.system.query.QueryGenerator;
@@ -114,65 +115,18 @@ public class TaskTaskController extends JeecgController<TaskTask, ITaskTaskServi
@Operation(summary = "事项任务计划表(发起周期性流程)-添加") @Operation(summary = "事项任务计划表(发起周期性流程)-添加")
@RequiresPermissions("tasktask:task_task:add") @RequiresPermissions("tasktask:task_task:add")
@PostMapping(value = "/flow-schedules") @PostMapping(value = "/flow-schedules")
public Result<String> addRecurringTask(@RequestBody TaskTask taskTask, public Result<?> addRecurringTask(@RequestBody TaskTask taskTask, Integer intervalType, Integer startCount) {
@RequestParam(name = "intervalType", required = false) Integer intervalType, // 获取用户信息
@RequestParam(name = "startCount", required = false) Integer startCount) throws Exception {
if (taskTask.getTriggerType() == null) {
return Result.error("触发类型不能为空");
}
Integer triggerType = taskTask.getTriggerType();
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal(); LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
if (sysUser.getUsername() == null || sysUser.getUsername().isEmpty()) { String username = sysUser.getUsername();
return Result.error("无法获取当前流程发起人信息");
}
if (startCount > FlowConstants.taskCount.MAX) return Result.error("单词生成周期任务不能超过100个");
if (FlowConstants.Strategy.IMMEDIATE_ONLY.equals(triggerType)) { // 这里只做最基础的鉴权拦截
if (taskTaskService.singleFlowStart(taskTask, sysUser.getUsername())) { if (StringUtils.isEmpty(username)) {
return Result.ok("单次流程发起成功"); return Result.error("登录已失效,请重新登录");
}
return Result.error("立即发起流程失败");
} else if (FlowConstants.Strategy.RECURRING_ONLY.equals(triggerType)) {
//向中间表写入n个待发起流程,由定时器扫描发起
List<TaskTask> temTaskList = new ArrayList<>();
Date baseDate = Optional.ofNullable(taskTask.getStartTime())
.orElseThrow(() -> new IllegalArgumentException("周期性流程开始时间不能为空!"));
LocalDateTime currentLdt = baseDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
for (int i = 0; i < startCount; i++) {
// 计算下一次执行时间
if (FlowConstants.IntervalType.EVERY_DAY.equals(intervalType)) {
currentLdt = currentLdt.plusDays(1);
} else if (FlowConstants.IntervalType.EVERY_WEEK.equals(intervalType)) {
currentLdt = currentLdt.plusWeeks(1);
} else if (FlowConstants.IntervalType.EVERY_TWO_WEEKS.equals(intervalType)) {
currentLdt = currentLdt.plusWeeks(2);
} else if (FlowConstants.IntervalType.EVERY_MONTH.equals(intervalType)) {
currentLdt = currentLdt.plusMonths(1);
} else if (FlowConstants.IntervalType.EVERY_QUARTER.equals(intervalType)) {
currentLdt = currentLdt.plusMonths(3);
} else {
throw new IllegalArgumentException("未知的周期类型: " + intervalType);
} }
// 2. 创建新的任务对象并添加到列表 // 业务逻辑全部交给 Service
TaskTask newTask = new TaskTask(); return taskTaskService.handleFlowStart(taskTask, username, intervalType, startCount);
// 复制基础属性 (建议使用 BeanUtils.copyProperties)
BeanUtils.copyProperties(taskTask, newTask);
// 设置计算后的触发时间
Date nextExecutionDate = Date.from(currentLdt.atZone(ZoneId.systemDefault()).toInstant());
newTask.setStartTime(nextExecutionDate);
temTaskList.add(newTask);
}
taskTaskService.saveBatch(temTaskList);
} else {
// 这个 else 很重要,处理那些“意料之外”的数值
return Result.error("非法的触发类型数值: " + triggerType);
}
return Result.OK("添加成功!");
} }
/** /**
@@ -1,5 +1,6 @@
package org.jeecg.modules.tasktask.service; package org.jeecg.modules.tasktask.service;
import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.tasktask.entity.TaskTask; import org.jeecg.modules.tasktask.entity.TaskTask;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
@@ -11,4 +12,6 @@ import com.baomidou.mybatisplus.extension.service.IService;
*/ */
public interface ITaskTaskService extends IService<TaskTask> { public interface ITaskTaskService extends IService<TaskTask> {
boolean singleFlowStart(TaskTask taskTask,String userName) throws Exception; boolean singleFlowStart(TaskTask taskTask,String userName) throws Exception;
boolean multiFlowStart(TaskTask taskTask,String userName,Integer intervalType, Integer startCount) throws Exception;
Result<?> handleFlowStart(TaskTask taskTask, String username, Integer intervalType, Integer startCount);
} }
@@ -6,17 +6,20 @@ import org.apache.shiro.util.Assert;
import org.jeecg.common.api.vo.Result; import org.jeecg.common.api.vo.Result;
import org.jeecg.modules.extbpm.process.exception.BpmException; import org.jeecg.modules.extbpm.process.exception.BpmException;
import org.jeecg.modules.extbpm.process.service.impl.BpmBaseExtApiImpl; import org.jeecg.modules.extbpm.process.service.impl.BpmBaseExtApiImpl;
import org.jeecg.modules.tasktask.constant.FlowConstants;
import org.jeecg.modules.tasktask.entity.TaskTask; import org.jeecg.modules.tasktask.entity.TaskTask;
import org.jeecg.modules.tasktask.mapper.TaskTaskMapper; import org.jeecg.modules.tasktask.mapper.TaskTaskMapper;
import org.jeecg.modules.tasktask.service.ITaskTaskService; import org.jeecg.modules.tasktask.service.ITaskTaskService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays; import java.time.LocalDateTime;
import java.util.Optional; import java.time.ZoneId;
import java.util.*;
/** /**
* @Description: 事项任务计划表(发起流程) * @Description: 事项任务计划表(发起流程)
@@ -28,7 +31,6 @@ import java.util.Optional;
public class TaskTaskServiceImpl extends ServiceImpl<TaskTaskMapper, TaskTask> implements ITaskTaskService { public class TaskTaskServiceImpl extends ServiceImpl<TaskTaskMapper, TaskTask> implements ITaskTaskService {
@Autowired @Autowired
private BpmBaseExtApiImpl bpmBaseExtApiImpl; private BpmBaseExtApiImpl bpmBaseExtApiImpl;
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public boolean singleFlowStart(TaskTask taskTask, String userName) throws Exception { public boolean singleFlowStart(TaskTask taskTask, String userName) throws Exception {
if (StringUtils.isBlank(taskTask.getFlowCode())) { if (StringUtils.isBlank(taskTask.getFlowCode())) {
@@ -46,4 +48,71 @@ public class TaskTaskServiceImpl extends ServiceImpl<TaskTaskMapper, TaskTask> i
Result<String> res = bpmBaseExtApiImpl.startMutilProcess(taskTask.getFlowCode(), taskTask.getBusinessId(), taskTask.getFormUrl(), taskTask.getFormUrl(), userName, jsonString); Result<String> res = bpmBaseExtApiImpl.startMutilProcess(taskTask.getFlowCode(), taskTask.getBusinessId(), taskTask.getFormUrl(), taskTask.getFormUrl(), userName, jsonString);
return res.isSuccess(); return res.isSuccess();
} }
@Transactional(rollbackFor = Exception.class)
public boolean multiFlowStart(TaskTask taskTask,String userName,Integer intervalType, Integer startCount) throws Exception{
List<TaskTask> temTaskList = new ArrayList<>();
Date baseDate = Optional.ofNullable(taskTask.getStartTime())
.orElseThrow(() -> new IllegalArgumentException("周期性流程开始时间不能为空!"));
LocalDateTime currentLdt = baseDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
FlowConstants.IntervalType intervalTypeInstance = FlowConstants.IntervalType.of(intervalType);
for (int i = 0; i < startCount; i++) {
LocalDateTime execDateTime = intervalTypeInstance.nextTime(currentLdt,i+1);
// 2. 创建新的任务对象并添加到列表
TaskTask newTask = new TaskTask();
// 复制基础属性 (建议使用 BeanUtils.copyProperties)
BeanUtils.copyProperties(taskTask, newTask);
newTask.setId(null);
// 设置计算后的触发时间
Date nextExecutionDate = Date.from(execDateTime.atZone(ZoneId.systemDefault()).toInstant());
newTask.setStartTime(nextExecutionDate);
temTaskList.add(newTask);
}
return this.saveBatch(temTaskList);
}
public Result<?> handleFlowStart(TaskTask taskTask, String username, Integer intervalType, Integer startCount) throws Exception {
// 1. 显式校验:代替 Assert
Result<?> checkResult = validateBusinessParams(taskTask, startCount);
if (!checkResult.isSuccess()) {
return checkResult; // 校验不通过直接返回错误
}
Integer triggerType = taskTask.getTriggerType();
// 2. 策略分发(路由)
if (FlowConstants.Strategy.IMMEDIATE_ONLY.equals(triggerType)) {
boolean success = singleFlowStart(taskTask, username);
return success ? Result.ok("立即发起流程成功") : Result.error("立即发起流程失败");
}
if (FlowConstants.Strategy.RECURRING_ONLY.equals(triggerType)) {
boolean success = multiFlowStart(taskTask, username, intervalType, startCount);
return success ? Result.ok("周期性流程添加成功") : Result.error("周期性流程添加失败");
}
// 3. 保底处理
return Result.error("未知的触发类型: " + triggerType);
}
private Result<?> validateBusinessParams(TaskTask taskTask, Integer startCount) {
if (taskTask.getTriggerType() == null) {
return Result.error("触发类型不能为空");
}
// 如果是周期任务,校验次数和开始时间
if (FlowConstants.Strategy.RECURRING_ONLY.equals(taskTask.getTriggerType())) {
if (startCount == null || startCount <= 0) {
return Result.error("周期任务生成数量必须大于0");
}
if (startCount > FlowConstants.taskCount.MAX) {
return Result.error("单次生成周期任务不能超过24个");
}
if (taskTask.getStartTime() == null) {
return Result.error("周期性流程开始时间不能为空");
}
}
return Result.ok();
}
} }