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(), "未知用户"));
}
}
}
}