package com.ruoyi.inspectiontask.service.impl;
|
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.bean.BeanUtils;
|
import com.ruoyi.inspectiontask.dto.TimingTaskDto;
|
import com.ruoyi.inspectiontask.mapper.InspectionTaskMapper;
|
import com.ruoyi.inspectiontask.mapper.TimingTaskMapper;
|
import com.ruoyi.inspectiontask.pojo.TimingTask;
|
import com.ruoyi.inspectiontask.service.TimingTaskService;
|
import com.ruoyi.project.system.domain.SysUser;
|
import com.ruoyi.project.system.mapper.SysUserMapper;
|
import lombok.extern.slf4j.Slf4j;
|
import org.quartz.SchedulerException;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
|
import java.time.*;
|
import java.util.*;
|
import java.util.stream.Collectors;
|
|
/**
|
* @author :yys
|
* @date : 2025/9/19 10:55
|
*/
|
@Service
|
@Slf4j
|
public class TimingTaskServiceImpl extends ServiceImpl<TimingTaskMapper, TimingTask> implements TimingTaskService {
|
|
@Autowired
|
private TimingTaskMapper timingTaskMapper;
|
|
@Autowired
|
private InspectionTaskMapper inspectionTaskMapper;
|
|
@Autowired
|
private TimingTaskScheduler timingTaskScheduler;
|
|
@Autowired
|
private SysUserMapper sysUserMapper;
|
|
|
@Override
|
public IPage<TimingTaskDto> selectTimingTaskList(Page<TimingTask> page, TimingTask timingTask) {
|
// 1. 先分页查询定时任务数据
|
IPage<TimingTask> taskPage = timingTaskMapper.selectPage(page, null);
|
|
// 2. 如果没有数据,直接返回空分页
|
if (taskPage.getRecords().isEmpty()) {
|
return new Page<>(taskPage.getCurrent(), taskPage.getSize(), taskPage.getTotal());
|
}
|
|
// 3. 收集所有需要查询的用户ID
|
Set<Long> userIds = new HashSet<>();
|
|
// 收集登记人ID
|
taskPage.getRecords().forEach(task -> {
|
if (task.getRegistrantId() != null) {
|
userIds.add(task.getRegistrantId());
|
}
|
});
|
|
// 收集巡检人ID(多个ID以逗号分隔)
|
taskPage.getRecords().forEach(task -> {
|
if (StringUtils.isNotBlank(task.getInspectorIds())) {
|
Arrays.stream(task.getInspectorIds().split(","))
|
.filter(StringUtils::isNotBlank)
|
.map(Long::valueOf)
|
.forEach(userIds::add);
|
}
|
});
|
|
// 4. 批量查询用户信息
|
Map<Long, String> userNickNameMap = new HashMap<>();
|
if (!userIds.isEmpty()) {
|
List<SysUser> users = sysUserMapper.selectUserByIds((List<Long>) userIds);
|
users.forEach(user -> userNickNameMap.put(user.getUserId(), user.getNickName()));
|
}
|
|
// 5. 转换为DTO
|
List<TimingTaskDto> dtoList = taskPage.getRecords().stream().map(task -> {
|
TimingTaskDto dto = new TimingTaskDto();
|
// 复制基本属性
|
BeanUtils.copyProperties(task, dto);
|
|
// 设置登记人昵称
|
if (task.getRegistrantId() != null) {
|
dto.setRegistrant(userNickNameMap.getOrDefault(task.getRegistrantId(), "未知用户"));
|
}
|
|
// 设置巡检人昵称列表
|
if (StringUtils.isNotBlank(task.getInspectorIds())) {
|
List<String> inspectorNickNames = Arrays.stream(task.getInspectorIds().split(","))
|
.filter(StringUtils::isNotBlank)
|
.map(idStr -> {
|
Long id = Long.valueOf(idStr);
|
return userNickNameMap.getOrDefault(id, "未知用户");
|
})
|
.toList();
|
dto.setInspector(inspectorNickNames);
|
}
|
|
return dto;
|
}).collect(Collectors.toList());
|
|
// 6. 构建返回的分页对象
|
Page<TimingTaskDto> resultPage = new Page<>(taskPage.getCurrent(), taskPage.getSize(), taskPage.getTotal());
|
resultPage.setRecords(dtoList);
|
return resultPage;
|
}
|
|
@Override
|
@Transactional
|
public int addOrEditTimingTask(TimingTaskDto timingTaskDto) throws SchedulerException {
|
TimingTask timingTask = new TimingTask();
|
BeanUtils.copyProperties(timingTaskDto, timingTask);
|
|
// 设置创建人信息和默认值
|
if (Objects.isNull(timingTaskDto.getId())) {
|
timingTask.setRegistrationDate(LocalDate.now());
|
timingTask.setActive(true);
|
|
// 计算首次执行时间
|
LocalDateTime firstExecutionTime = calculateFirstExecutionTime(timingTask);
|
timingTask.setNextExecutionTime(firstExecutionTime);
|
|
int result = timingTaskMapper.insert(timingTask);
|
if (result > 0) {
|
// 新增成功后添加到调度器
|
timingTaskScheduler.scheduleTimingTask(timingTask);
|
}
|
return result;
|
} else {
|
int result = timingTaskMapper.updateById(timingTask);
|
if (result > 0) {
|
// 更新成功后重新调度任务
|
timingTaskScheduler.rescheduleTimingTask(timingTask);
|
}
|
return result;
|
}
|
}
|
|
private LocalDateTime calculateFirstExecutionTime(TimingTask task) {
|
// 根据频率类型和详情计算首次执行时间
|
return switch (task.getFrequencyType()) {
|
case "DAILY" ->
|
// 如果是每天执行,计算今天或明天的具体时间
|
calculateDailyFirstExecution(task.getFrequencyDetail());
|
case "WEEKLY" ->
|
// 如果是每周执行,计算下周的具体星期几
|
calculateWeeklyFirstExecution(task.getFrequencyDetail());
|
case "MONTHLY" ->
|
// 如果是每月执行,计算下个月的具体日期
|
calculateMonthlyFirstExecution(task.getFrequencyDetail());
|
case "QUARTERLY" ->
|
// 自定义频率,如每小时、每30分钟等
|
calculateCustomFirstExecution(task.getFrequencyDetail());
|
default -> throw new IllegalArgumentException("不支持的频率类型: " + task.getFrequencyType());
|
};
|
}
|
|
private LocalDateTime calculateDailyFirstExecution(String frequencyDetail) {
|
// frequencyDetail可能是具体时间,如 "14:30"
|
LocalTime executionTime = LocalTime.parse(frequencyDetail);
|
LocalDateTime now = LocalDateTime.now();
|
LocalDateTime todayExecution = LocalDateTime.of(now.toLocalDate(), executionTime);
|
|
// 如果今天的时间已过,则安排明天执行
|
return now.isBefore(todayExecution) ? todayExecution : todayExecution.plusDays(1);
|
}
|
|
private LocalDateTime calculateWeeklyFirstExecution(String frequencyDetail) {
|
return null;
|
}
|
|
private LocalDateTime calculateMonthlyFirstExecution(String frequencyDetail) {
|
return null;
|
}
|
|
private LocalDateTime calculateCustomFirstExecution(String frequencyDetail) {
|
return null;
|
}
|
|
@Override
|
@Transactional
|
public void updateTaskExecutionTime(Long taskId) {
|
TimingTask task = timingTaskMapper.selectById(taskId);
|
if (task == null) {
|
throw new RuntimeException("定时任务不存在,ID: " + taskId);
|
}
|
|
// 更新最后执行时间为当前时间
|
task.setLastExecutionTime(LocalDateTime.now());
|
|
// 计算下次执行时间
|
LocalDateTime nextExecutionTime = calculateNextExecutionTime(
|
task.getFrequencyType(),
|
task.getFrequencyDetail(),
|
LocalDateTime.now()
|
);
|
task.setNextExecutionTime(nextExecutionTime);
|
|
// 更新数据库
|
timingTaskMapper.updateById(task);
|
}
|
|
/**
|
* 计算下次执行时间
|
*/
|
private LocalDateTime calculateNextExecutionTime(String frequencyType,
|
String frequencyDetail,
|
LocalDateTime currentTime) {
|
try {
|
return switch (frequencyType) {
|
case "DAILY" -> calculateDailyNextTime(frequencyDetail, currentTime);
|
case "WEEKLY" -> calculateWeeklyNextTime(frequencyDetail, currentTime);
|
case "MONTHLY" -> calculateMonthlyNextTime(frequencyDetail, currentTime);
|
case "QUARTERLY" -> calculateQuarterlyNextTime(frequencyDetail, currentTime);
|
default -> throw new IllegalArgumentException("不支持的频率类型: " + frequencyType);
|
};
|
} catch (Exception e) {
|
throw new RuntimeException("计算下次执行时间失败: " + e.getMessage(), e);
|
}
|
}
|
|
/**
|
* 计算每日任务的下次执行时间
|
*/
|
private LocalDateTime calculateDailyNextTime(String timeStr, LocalDateTime current) {
|
LocalTime executionTime = LocalTime.parse(timeStr); // 解析格式 "HH:mm"
|
LocalDateTime nextTime = LocalDateTime.of(current.toLocalDate(), executionTime);
|
|
// 如果今天的时间已过,则安排明天
|
return current.isBefore(nextTime) ? nextTime : nextTime.plusDays(1);
|
}
|
|
/**
|
* 计算每周任务的下次执行时间
|
*/
|
private LocalDateTime calculateWeeklyNextTime(String detail, LocalDateTime current) {
|
String[] parts = detail.split(",");
|
String dayOfWeekStr = parts[0]; // 如 "MON" 或 "MON|WED|FRI"
|
LocalTime time = LocalTime.parse(parts[1]); // 时间部分
|
|
// 解析星期几(支持多个星期)
|
Set<DayOfWeek> targetDays = parseDayOfWeeks(dayOfWeekStr);
|
|
// 从当前时间开始找下一个符合条件的星期几
|
LocalDateTime nextTime = current;
|
while (true) {
|
nextTime = nextTime.plusDays(1);
|
if (targetDays.contains(nextTime.getDayOfWeek())) {
|
return LocalDateTime.of(nextTime.toLocalDate(), time);
|
}
|
|
// 防止无限循环(理论上不会发生)
|
if (nextTime.isAfter(current.plusYears(1))) {
|
throw new RuntimeException("无法找到下次执行时间");
|
}
|
}
|
}
|
|
/**
|
* 计算每月任务的下次执行时间
|
*/
|
private LocalDateTime calculateMonthlyNextTime(String detail, LocalDateTime current) {
|
String[] parts = detail.split(",");
|
int dayOfMonth = Integer.parseInt(parts[0]);
|
LocalTime time = LocalTime.parse(parts[1]);
|
|
// 从下个月开始计算
|
LocalDateTime nextTime = current.plusMonths(1)
|
.withDayOfMonth(Math.min(dayOfMonth, current.plusMonths(1).toLocalDate().lengthOfMonth()))
|
.with(time);
|
|
return nextTime;
|
}
|
|
/**
|
* 计算每季度任务的下次执行时间
|
*/
|
private LocalDateTime calculateQuarterlyNextTime(String detail, LocalDateTime current) {
|
String[] parts = detail.split(",");
|
int quarterMonth = Integer.parseInt(parts[0]); // 1=第1个月,2=第2个月,3=第3个月
|
int dayOfMonth = Integer.parseInt(parts[1]);
|
LocalTime time = LocalTime.parse(parts[2]);
|
|
// 计算当前季度
|
int currentQuarter = (current.getMonthValue() - 1) / 3 + 1;
|
int currentMonthInQuarter = (current.getMonthValue() - 1) % 3 + 1;
|
|
YearMonth targetYearMonth;
|
if (currentMonthInQuarter < quarterMonth) {
|
// 本季度内还有执行机会
|
targetYearMonth = YearMonth.from(current)
|
.plusMonths(quarterMonth - currentMonthInQuarter);
|
} else {
|
// 需要到下个季度
|
targetYearMonth = YearMonth.from(current)
|
.plusMonths(3 - currentMonthInQuarter + quarterMonth);
|
}
|
|
// 处理月末日期
|
int adjustedDay = Math.min(dayOfMonth, targetYearMonth.lengthOfMonth());
|
|
return LocalDateTime.of(
|
targetYearMonth.getYear(),
|
targetYearMonth.getMonthValue(),
|
adjustedDay,
|
time.getHour(),
|
time.getMinute()
|
);
|
}
|
|
/**
|
* 解析星期几字符串
|
*/
|
private Set<DayOfWeek> parseDayOfWeeks(String dayOfWeekStr) {
|
Set<DayOfWeek> days = new HashSet<>();
|
String[] dayStrs = dayOfWeekStr.split("\\|");
|
|
for (String dayStr : dayStrs) {
|
switch (dayStr) {
|
case "MON": days.add(DayOfWeek.MONDAY); break;
|
case "TUE": days.add(DayOfWeek.TUESDAY); break;
|
case "WED": days.add(DayOfWeek.WEDNESDAY); break;
|
case "THU": days.add(DayOfWeek.THURSDAY); break;
|
case "FRI": days.add(DayOfWeek.FRIDAY); break;
|
case "SAT": days.add(DayOfWeek.SATURDAY); break;
|
case "SUN": days.add(DayOfWeek.SUNDAY); break;
|
default: throw new IllegalArgumentException("无效的星期几: " + dayStr);
|
}
|
}
|
|
return days;
|
}
|
|
|
|
@Override
|
public int delByIds(Long[] ids) {
|
return timingTaskMapper.deleteBatchIds(Arrays.asList(ids));
|
}
|
|
}
|