package com.ruoyi.safe.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 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.project.system.domain.SysUser; import com.ruoyi.project.system.mapper.SysUserMapper; import com.ruoyi.safe.mapper.SafeFacilityInspectionTaskMapper; import com.ruoyi.safe.pojo.SafeFacilityInspectionTask; import com.ruoyi.safe.pojo.SafeFacilityLedger; import com.ruoyi.safe.service.SafeFacilityInspectionTaskService; import com.ruoyi.safe.service.SafeFacilityLedgerService; import lombok.RequiredArgsConstructor; import org.quartz.SchedulerException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.DayOfWeek; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.YearMonth; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; /** *

* 安全生产--安全设施定时巡检任务 服务实现类 *

*/ @Service @RequiredArgsConstructor public class SafeFacilityInspectionTaskServiceImpl extends ServiceImpl implements SafeFacilityInspectionTaskService { private static final int ENABLED = 1; private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm"); private static final Map 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 final SafeFacilityInspectionTaskMapper safeFacilityInspectionTaskMapper; private final SafeFacilityInspectionTaskScheduler safeFacilityInspectionTaskScheduler; private final SafeFacilityLedgerService safeFacilityLedgerService; private final SysUserMapper sysUserMapper; @Override public IPage pageSafeFacilityInspectionTask(Page page, SafeFacilityInspectionTask task) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.like(StringUtils.isNotBlank(task.getInspectionName()), SafeFacilityInspectionTask::getInspectionName, task.getInspectionName()) .like(StringUtils.isNotBlank(task.getFacilityName()), SafeFacilityInspectionTask::getFacilityName, task.getFacilityName()) .like(StringUtils.isNotBlank(task.getFacilityCode()), SafeFacilityInspectionTask::getFacilityCode, task.getFacilityCode()) .eq(task.getFacilityId() != null, SafeFacilityInspectionTask::getFacilityId, task.getFacilityId()) .eq(task.getIsEnabled() != null, SafeFacilityInspectionTask::getIsEnabled, task.getIsEnabled()) .orderByDesc(SafeFacilityInspectionTask::getId); IPage result = safeFacilityInspectionTaskMapper.selectPage(page, queryWrapper); fillInspectorName(result.getRecords()); return result; } @Override @Transactional public boolean saveOrUpdateTask(SafeFacilityInspectionTask task) throws SchedulerException { SafeFacilityInspectionTask oldTask = null; if (task.getId() != null) { oldTask = safeFacilityInspectionTaskMapper.selectById(task.getId()); if (oldTask == null) { throw new IllegalArgumentException("定时巡检任务不存在"); } } normalizeTask(task, oldTask); task.setNextExecutionTime(calculateFirstExecutionTime(task.getFrequencyType(), task.getFrequencyDetail())); boolean result; if (task.getId() == null) { result = safeFacilityInspectionTaskMapper.insert(task) > 0; if (result && isEnabled(task.getIsEnabled())) { safeFacilityInspectionTaskScheduler.scheduleTask(task); } return result; } result = safeFacilityInspectionTaskMapper.updateById(task) > 0; if (result) { boolean oldEnabled = oldTask != null && isEnabled(oldTask.getIsEnabled()); boolean newEnabled = isEnabled(task.getIsEnabled()); if (!newEnabled) { safeFacilityInspectionTaskScheduler.unscheduleTask(task.getId()); } else if (oldEnabled) { safeFacilityInspectionTaskScheduler.rescheduleTask(task); } else { safeFacilityInspectionTaskScheduler.scheduleTask(task); } } return result; } @Override @Transactional public boolean removeTaskByIds(List ids) { boolean removed = removeBatchByIds(ids); if (removed) { for (Long id : ids) { safeFacilityInspectionTaskScheduler.unscheduleTask(id); } } return removed; } @Override public LocalDateTime calculateFirstExecutionTime(String frequencyType, String frequencyDetail) { LocalDateTime now = LocalDateTime.now(); return calculateNextExecutionTime(frequencyType, frequencyDetail, now); } @Override public LocalDateTime calculateNextExecutionTime(String frequencyType, String frequencyDetail, LocalDateTime currentTime) { validateFrequency(frequencyType, frequencyDetail); return switch (frequencyType) { case "DAILY" -> calculateDailyNextTime(frequencyDetail, currentTime); case "WEEKLY" -> calculateWeeklyNextTime(frequencyDetail, currentTime); case "MONTHLY" -> calculateMonthlyNextTime(frequencyDetail, currentTime); default -> throw new IllegalArgumentException("不支持的频率类型: " + frequencyType); }; } private void normalizeTask(SafeFacilityInspectionTask task, SafeFacilityInspectionTask oldTask) { if (StringUtils.isBlank(task.getInspectionName())) { throw new IllegalArgumentException("巡检任务名称不能为空"); } if (task.getFacilityId() == null) { throw new IllegalArgumentException("请选择安全设施"); } if (StringUtils.isBlank(task.getInspectionProject())) { throw new IllegalArgumentException("巡检项目不能为空"); } if (task.getInspectorId() == null) { throw new IllegalArgumentException("巡检人不能为空"); } SafeFacilityLedger ledger = safeFacilityLedgerService.getById(task.getFacilityId()); if (ledger == null) { throw new IllegalArgumentException("选择的安全设施不存在"); } if ("报废".equals(ledger.getStatus())) { throw new IllegalArgumentException("报废设施不能创建巡检任务"); } task.setFacilityCode(ledger.getFacilityCode()); task.setFacilityName(ledger.getFacilityName()); task.setIsEnabled(resolveEnabledValue(task.getIsEnabled(), oldTask)); task.setFrequencyType(task.getFrequencyType() == null ? null : task.getFrequencyType().trim().toUpperCase()); task.setFrequencyDetail(task.getFrequencyDetail() == null ? null : task.getFrequencyDetail().trim()); validateFrequency(task.getFrequencyType(), task.getFrequencyDetail()); } private void validateFrequency(String frequencyType, String frequencyDetail) { if (StringUtils.isBlank(frequencyType) || StringUtils.isBlank(frequencyDetail)) { throw new IllegalArgumentException("任务频率不能为空"); } switch (frequencyType) { case "DAILY" -> parseTime(frequencyDetail); case "WEEKLY" -> { String[] parts = splitFrequencyDetail(frequencyDetail); if (!WEEK_DAY_MAP.containsKey(parts[0])) { throw new IllegalArgumentException("无效的星期: " + parts[0]); } parseTime(parts[1]); } case "MONTHLY" -> { String[] parts = splitFrequencyDetail(frequencyDetail); validateDayOfMonth(parts[0]); parseTime(parts[1]); } default -> throw new IllegalArgumentException("不支持的频率类型: " + frequencyType); } } private LocalDateTime calculateDailyNextTime(String timeStr, LocalDateTime currentTime) { LocalTime executionTime = parseTime(timeStr); LocalDateTime candidate = LocalDateTime.of(currentTime.toLocalDate(), executionTime); return currentTime.isBefore(candidate) ? candidate : candidate.plusDays(1); } private LocalDateTime calculateWeeklyNextTime(String detail, LocalDateTime currentTime) { String[] parts = splitFrequencyDetail(detail); DayOfWeek targetDay = WEEK_DAY_MAP.get(parts[0]); LocalTime targetTime = parseTime(parts[1]); int daysUntil = targetDay.getValue() - currentTime.getDayOfWeek().getValue(); if (daysUntil < 0 || (daysUntil == 0 && !currentTime.toLocalTime().isBefore(targetTime))) { daysUntil += 7; } return LocalDateTime.of(currentTime.toLocalDate().plusDays(daysUntil), targetTime); } private LocalDateTime calculateMonthlyNextTime(String detail, LocalDateTime currentTime) { String[] parts = splitFrequencyDetail(detail); int dayOfMonth = validateDayOfMonth(parts[0]); LocalTime targetTime = parseTime(parts[1]); YearMonth currentMonth = YearMonth.from(currentTime); for (int i = 0; i < 24; i++) { YearMonth targetMonth = currentMonth.plusMonths(i); if (dayOfMonth > targetMonth.lengthOfMonth()) { continue; } LocalDateTime candidate = LocalDateTime.of( targetMonth.getYear(), targetMonth.getMonthValue(), dayOfMonth, targetTime.getHour(), targetTime.getMinute() ); if (currentTime.isBefore(candidate)) { return candidate; } } throw new IllegalArgumentException("无法计算下次执行时间"); } private String[] splitFrequencyDetail(String detail) { String[] parts = detail.split(","); if (parts.length != 2) { throw new IllegalArgumentException("频率详情格式错误"); } return Arrays.stream(parts).map(String::trim).toArray(String[]::new); } private LocalTime parseTime(String timeStr) { try { return LocalTime.parse(timeStr, TIME_FORMATTER); } catch (DateTimeParseException e) { throw new IllegalArgumentException("时间格式必须为HH:mm", e); } } private int validateDayOfMonth(String dayStr) { try { int day = Integer.parseInt(dayStr); if (day < 1 || day > 31) { throw new IllegalArgumentException("日期必须在1-31之间"); } return day; } catch (NumberFormatException e) { throw new IllegalArgumentException("日期必须在1-31之间", e); } } private Integer resolveEnabledValue(Integer requestEnabled, SafeFacilityInspectionTask oldTask) { if (requestEnabled != null) { return requestEnabled; } if (oldTask != null && oldTask.getIsEnabled() != null) { return oldTask.getIsEnabled(); } return ENABLED; } private boolean isEnabled(Integer enabledValue) { return Objects.equals(enabledValue, ENABLED); } private void fillInspectorName(List records) { if (records == null || records.isEmpty()) { return; } List userIds = records.stream() .map(SafeFacilityInspectionTask::getInspectorId) .filter(Objects::nonNull) .map(Integer::longValue) .distinct() .collect(Collectors.toList()); if (userIds.isEmpty()) { return; } Map userNameMap = sysUserMapper.selectUserByIds(userIds).stream() .collect(Collectors.toMap(SysUser::getUserId, SysUser::getNickName, (v1, v2) -> v1)); for (SafeFacilityInspectionTask record : records) { if (record.getInspectorId() != null) { record.setInspectorName(userNameMap.getOrDefault(record.getInspectorId().longValue(), "未知用户")); } } } }