| ¶Ô±ÈÐÂÎļþ |
| | |
| | | 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.time.format.DateTimeFormatter; |
| | | 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((new ArrayList<>(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 = new ArrayList<>(); |
| | | for (String idStr : task.getInspectorIds().split(",")) { |
| | | if (StringUtils.isNotBlank(idStr)) { |
| | | Long id = Long.valueOf(idStr); |
| | | inspectorNickNames.add(userNickNameMap.getOrDefault(id, "æªç¥ç¨æ·")); |
| | | } |
| | | } |
| | | 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) { |
| | | // æ ¹æ®é¢çç±»åå详æ
计ç®é¦æ¬¡æ§è¡æ¶é´ |
| | | String frequencyType = task.getFrequencyType(); |
| | | if ("DAILY".equals(frequencyType)) { |
| | | // å¦ææ¯æ¯å¤©æ§è¡ï¼è®¡ç®ä»å¤©ææå¤©çå
·ä½æ¶é´ |
| | | return calculateDailyFirstExecution(task.getFrequencyDetail()); |
| | | } else if ("WEEKLY".equals(frequencyType)) { |
| | | // å¦ææ¯æ¯å¨æ§è¡ï¼è®¡ç®ä¸å¨çå
·ä½ææå |
| | | return calculateWeeklyFirstExecution(task.getFrequencyDetail()); |
| | | } else if ("MONTHLY".equals(frequencyType)) { |
| | | // å¦ææ¯æ¯ææ§è¡ï¼è®¡ç®ä¸ä¸ªæçå
·ä½æ¥æ |
| | | return calculateMonthlyFirstExecution(task.getFrequencyDetail()); |
| | | } else if ("QUARTERLY".equals(frequencyType)) { |
| | | // èªå®ä¹é¢çï¼å¦æ¯å°æ¶ãæ¯30åéç |
| | | return calculateCustomFirstExecution(task.getFrequencyDetail()); |
| | | } else { |
| | | 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); |
| | | } |
| | | |
| | | // æ å°ææç®åä¸DayOfWeek |
| | | private static final Map<String, DayOfWeek> WEEK_DAY_MAP = new HashMap<>(); |
| | | static { |
| | | WEEK_DAY_MAP.put("MON", DayOfWeek.MONDAY); |
| | | WEEK_DAY_MAP.put("TUE", DayOfWeek.TUESDAY); |
| | | WEEK_DAY_MAP.put("WED", DayOfWeek.WEDNESDAY); |
| | | WEEK_DAY_MAP.put("THU", DayOfWeek.THURSDAY); |
| | | WEEK_DAY_MAP.put("FRI", DayOfWeek.FRIDAY); |
| | | WEEK_DAY_MAP.put("SAT", DayOfWeek.SATURDAY); |
| | | WEEK_DAY_MAP.put("SUN", DayOfWeek.SUNDAY); |
| | | } |
| | | |
| | | private LocalDateTime calculateWeeklyFirstExecution(String frequencyDetail) { |
| | | // è§£æè¾å
¥åæ° |
| | | String[] parts = frequencyDetail.split(","); |
| | | if (parts.length != 2) { |
| | | throw new IllegalArgumentException("åæ°æ ¼å¼é误ï¼åºä¸º'MON,13:43'æ ¼å¼"); |
| | | } |
| | | |
| | | String weekDayStr = parts[0].trim(); |
| | | String timeStr = parts[1].trim(); |
| | | |
| | | // è·å对åºçææå |
| | | DayOfWeek targetDay = WEEK_DAY_MAP.get(weekDayStr); |
| | | if (targetDay == null) { |
| | | throw new IllegalArgumentException("æ æçææç®å: " + weekDayStr); |
| | | } |
| | | |
| | | // è§£ææ¶é´ |
| | | LocalTime targetTime = LocalTime.parse(timeStr, DateTimeFormatter.ofPattern("HH:mm")); |
| | | |
| | | // è·åå½åæ¶é´ |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | LocalDateTime targetDateTime = now.with(targetDay).with(targetTime); |
| | | |
| | | // å¦æè®¡ç®åºçæ¶é´å¨å½åæ¶é´ä¹åï¼åå ä¸å¨ |
| | | if (targetDateTime.isBefore(now)) { |
| | | targetDateTime = targetDateTime.plusWeeks(1); |
| | | } |
| | | |
| | | return targetDateTime; |
| | | } |
| | | |
| | | private LocalDateTime calculateMonthlyFirstExecution(String frequencyDetail) { |
| | | // è§£æè¾å
¥åæ° |
| | | String[] parts = frequencyDetail.split(","); |
| | | if (parts.length != 2) { |
| | | throw new IllegalArgumentException("åæ°æ ¼å¼é误ï¼åºä¸º'03,17:00'æ ¼å¼"); |
| | | } |
| | | |
| | | String dayStr = parts[0].trim(); |
| | | String timeStr = parts[1].trim(); |
| | | |
| | | // è§£ææ¥æ |
| | | int dayOfMonth; |
| | | try { |
| | | dayOfMonth = Integer.parseInt(dayStr); |
| | | } catch (NumberFormatException e) { |
| | | throw new IllegalArgumentException("æ æçæ¥ææ ¼å¼: " + dayStr, e); |
| | | } |
| | | |
| | | // éªè¯æ¥ææææ§ï¼1-31ä¹é´ï¼ |
| | | if (dayOfMonth < 1 || dayOfMonth > 31) { |
| | | throw new IllegalArgumentException("æ¥æå¿
é¡»å¨1-31ä¹é´: " + dayOfMonth); |
| | | } |
| | | |
| | | // è§£ææ¶é´ |
| | | LocalTime targetTime; |
| | | try { |
| | | targetTime = LocalTime.parse(timeStr, DateTimeFormatter.ofPattern("HH:mm")); |
| | | } catch (DateTimeException e) { |
| | | throw new IllegalArgumentException("æ æçæ¶é´æ ¼å¼: " + timeStr, e); |
| | | } |
| | | |
| | | // è·åå½åæ¶é´ |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | LocalDateTime targetDateTime = now.withDayOfMonth(dayOfMonth).with(targetTime); |
| | | |
| | | // æ£æ¥æ¥ææ¯å¦è¢«èªå¨è°æ´ï¼å¦31æ¥å¨å°æä¼è¢«è°æ´ï¼ |
| | | boolean isDateAdjusted = targetDateTime.getDayOfMonth() != dayOfMonth; |
| | | |
| | | // å¦æç®æ æ¶é´å¨å½åæ¶é´ä¹åï¼æè
æ¥æè¢«ç³»ç»èªå¨è°æ´äº |
| | | if (targetDateTime.isBefore(now) || isDateAdjusted) { |
| | | // 计ç®ä¸ä¸ªæçæ¥æ |
| | | LocalDateTime nextMonth = now.plusMonths(1); |
| | | // å°è¯è®¾ç½®ä¸ä¸ªæçç®æ æ¥æ |
| | | LocalDateTime nextMonthTarget = nextMonth.withDayOfMonth(dayOfMonth).with(targetTime); |
| | | |
| | | // 妿ä¸ä¸ªæçæ¥æä¹è¢«è°æ´äºï¼å°±ç¨ä¸ä¸ªæçæåä¸å¤© |
| | | if (nextMonthTarget.getDayOfMonth() != dayOfMonth) { |
| | | // æ£ç¡®è·åä¸ä¸ªæçæåä¸å¤©ï¼ä¿®å¤isLeapYearè°ç¨é®é¢ï¼ |
| | | int lastDayOfMonth = nextMonth.getMonth().length( |
| | | Year.of(nextMonth.getYear()).isLeap() |
| | | ); |
| | | nextMonthTarget = nextMonth.withDayOfMonth(lastDayOfMonth).with(targetTime); |
| | | } |
| | | |
| | | targetDateTime = nextMonthTarget; |
| | | } |
| | | |
| | | return targetDateTime; |
| | | } |
| | | |
| | | 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 { |
| | | switch (frequencyType) { |
| | | case "DAILY": |
| | | return calculateDailyNextTime(frequencyDetail, currentTime); |
| | | case "WEEKLY": |
| | | return calculateWeeklyNextTime(frequencyDetail, currentTime); |
| | | case "MONTHLY": |
| | | return calculateMonthlyNextTime(frequencyDetail, currentTime); |
| | | case "QUARTERLY": |
| | | return 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)); |
| | | } |
| | | |
| | | } |