修改向定时任务写的接口,枚举类实现策略模式,接口层服务层实现业务分离。
This commit is contained in:
+48
-17
@@ -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;
|
||||||
|
|||||||
+9
-55
@@ -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("添加成功!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+3
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
+72
-3
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user