drone-common/src/main/java/org/sxkj/common/utils/DataUtils.java
File was deleted drone-common/src/main/java/org/sxkj/common/utils/DateUtils.java
New file @@ -0,0 +1,134 @@ package org.sxkj.common.utils; import org.sxkj.common.enums.DateEnum; import java.time.DayOfWeek; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.Date; public class DateUtils { /** * 判断两个时间是否跨日、跨月或跨年 * @param start 开始时间 * @param end 结束时间 * @return 返回跨日、跨月或跨年的枚举值 */ public static DateEnum compareDate(LocalDateTime start, LocalDateTime end) { if (start.getYear() != end.getYear()) { return DateEnum.YEARS; } else if (start.getMonthValue() != end.getMonthValue()) { return DateEnum.MONTHS; } else if (start.getDayOfYear() != end.getDayOfYear()) { return DateEnum.DAYS; } else { return DateEnum.DAYS; } } /** * 计算年度周期边界 * @param maintainTime 维护时间 * @param ruleMonth 规则月份 * @param ruleDay 规则日期 * @return 周期边界 [start, end] */ public static LocalDateTime[] calculateAnnualCycle(Date maintainTime, int ruleMonth, int ruleDay) { LocalDateTime maintainDateTime = maintainTime.toInstant() .atZone(ZoneId.systemDefault()) .toLocalDateTime(); LocalDateTime currentYearStart = LocalDateTime.of( maintainDateTime.getYear(), ruleMonth, ruleDay, 0, 0, 0); LocalDateTime currentYearEnd = LocalDateTime.of( maintainDateTime.getYear() + 1, ruleMonth, ruleDay, 0, 0, 0); // 如果当前年份的规则日期已经过去,则使用下一年的周期 if (maintainDateTime.isBefore(currentYearStart)) { currentYearStart = currentYearStart.minusYears(1); currentYearEnd = currentYearEnd.minusYears(1); } return new LocalDateTime[]{currentYearStart, currentYearEnd}; } /** * 计算月度周期边界 * @param maintainTime 维护时间 * @param ruleDay 规则日期 * @return 周期边界 [start, end] */ public static LocalDateTime[] calculateMonthlyCycle(Date maintainTime, int ruleDay) { LocalDateTime maintainDateTime = maintainTime.toInstant() .atZone(ZoneId.systemDefault()) .toLocalDateTime(); LocalDateTime currentMonthStart = LocalDateTime.of( maintainDateTime.getYear(), maintainDateTime.getMonthValue(), ruleDay, 0, 0, 0); LocalDateTime currentMonthEnd = currentMonthStart.plusMonths(1); // 如果当前月份的规则日期已经过去,则使用下个月的周期 if (maintainDateTime.isBefore(currentMonthStart)) { currentMonthStart = currentMonthStart.minusMonths(1); currentMonthEnd = currentMonthEnd.minusMonths(1); } return new LocalDateTime[]{currentMonthStart, currentMonthEnd}; } /** * 计算周度周期边界 * @param maintainTime 维护时间 * @param ruleDayOfWeek 规则星期几(1-7) * @return 周期边界 [start, end] */ public static LocalDateTime[] calculateWeeklyCycle(Date maintainTime, int ruleDayOfWeek) { LocalDateTime maintainDateTime = maintainTime.toInstant() .atZone(ZoneId.systemDefault()) .toLocalDateTime(); DayOfWeek currentDayOfWeek = maintainDateTime.getDayOfWeek(); int daysDiff = currentDayOfWeek.getValue() - ruleDayOfWeek; if (daysDiff < 0) daysDiff += 7; // 确保差值为正 LocalDateTime weekStart = maintainDateTime.minusDays(daysDiff); LocalDateTime weekEnd = weekStart.plusWeeks(1); return new LocalDateTime[]{weekStart, weekEnd}; } /** * 判断维护时间是否在周期内 * @param maintainTime 维护时间 * @param cycleStart 周期开始时间 * @param cycleEnd 周期结束时间 * @return 是否在周期内 */ public static boolean isMaintainTimeInCycle(Date maintainTime, LocalDateTime cycleStart, LocalDateTime cycleEnd) { LocalDateTime maintainDateTime = maintainTime.toInstant() .atZone(ZoneId.systemDefault()) .toLocalDateTime(); return !maintainDateTime.isBefore(cycleStart) && maintainDateTime.isBefore(cycleEnd); } /** * 获取星期几的数字表示 * @param weekday 星期几字符串 * @return 数字表示(1-7) */ public static int getDayOfWeekNumber(String weekday) { switch (weekday) { case "星期日": return 1; case "星期一": return 2; case "星期二": return 3; case "星期三": return 4; case "星期四": return 5; case "星期五": return 6; case "星期六": return 7; default: return 1; // 默认为星期日 } } } drone-service/drone-fw/src/main/java/org/sxkj/fw/FwApplication.java
@@ -21,6 +21,7 @@ import org.springblade.core.launch.BladeApplication; import org.springblade.core.launch.constant.AppConstant; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.scheduling.annotation.EnableScheduling; /** * 系统模块启动器 @@ -29,6 +30,7 @@ @BladeCloudApplication @EnableFeignClients({"org.sxkj","org.springblade","com.dji.sample"}) @MapperScan({"org.springblade.**.mapper.**","org.sxkj.**.mapper.**"}) @EnableScheduling public class FwApplication { public static void main(String[] args) { drone-service/drone-fw/src/main/java/org/sxkj/fw/device/mapper/FwDeviceMaintainRecordMapper.java
@@ -35,6 +35,12 @@ public interface FwDeviceMaintainRecordMapper extends BaseMapper<FwDeviceMaintainRecordEntity> { /** * 查询最新一条维修记录 * @param planId 设备维护计划ID */ FwDeviceMaintainRecordVO selectLastOne(@Param("planId") Long planId); /** * 自定义分页 * * @param page drone-service/drone-fw/src/main/java/org/sxkj/fw/device/mapper/FwDeviceMaintainRecordMapper.xml
@@ -21,8 +21,12 @@ <result column="is_deleted" property="isDeleted"/> </resultMap> <select id="selectLastOne" resultType="org.sxkj.fw.device.vo.FwDeviceMaintainRecordVO"> select * from ja_fw_device_maintain_record where is_deleted = 0 and plan_id = #{planId} </select> <select id="selectFwDeviceMaintainRecordPage" resultMap="fwDeviceMaintainRecordResultMap"> <select id="selectFwDeviceMaintainRecordPage" resultType="org.sxkj.fw.device.vo.FwDeviceMaintainRecordVO"> select * from ja_fw_device_maintain_record <where> is_deleted = 0 drone-service/drone-fw/src/main/java/org/sxkj/fw/device/scheduler/MaintainPlanScheduler.java
New file @@ -0,0 +1,156 @@ package org.sxkj.fw.device.scheduler; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.sxkj.common.utils.DateUtils; import org.sxkj.fw.device.entity.FwDeviceMaintainPlanEntity; import org.sxkj.fw.device.service.IFwDeviceMaintainPlanService; import org.sxkj.fw.device.service.IFwDeviceMaintainRecordService; import java.time.DayOfWeek; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * @Description 设备维护计划状态更新定时器 * @Author AIX * @Date 2026/1/19 11:34 * @Version 1.0 */ @Component @Slf4j @AllArgsConstructor public class MaintainPlanScheduler { private final IFwDeviceMaintainPlanService fwDeviceMaintainPlanService; @Scheduled(cron = "0 0 0 * * ?") // 每天凌晨0点执行 // 每10秒执行一次 // @Scheduled(cron = "*/10 * * * * ?") // 每10秒执行一次,用于本地调试 public void refreshMaintainStatus() { log.info("开始执行设备维护计划状态刷新任务"); try { // 获取所有有效的维护计划 List<FwDeviceMaintainPlanEntity> plans = fwDeviceMaintainPlanService.list(); Date currentTime = new Date(); List<Long> expiredPlanIds = new ArrayList<>(); // 先收集需要更新的计划ID for (FwDeviceMaintainPlanEntity plan : plans) { boolean isExpired = checkPlanExpired(plan, currentTime); if (isExpired && plan.getMaintainStatus() != null && plan.getMaintainStatus() == 1) { expiredPlanIds.add(plan.getId()); } } // 批量更新数据库 if (!expiredPlanIds.isEmpty()) { fwDeviceMaintainPlanService.batchUpdateExpiredPlans(expiredPlanIds); log.info("批量更新 {} 个过期计划状态为未维护", expiredPlanIds.size()); } log.info("设备维护计划状态刷新任务完成,共处理 {} 条计划,更新 {} 条过期计划", plans.size(), expiredPlanIds.size()); } catch (Exception e) { log.error("设备维护计划状态刷新任务执行失败", e); } } /** * 检查维护计划是否已过期 */ private boolean checkPlanExpired(FwDeviceMaintainPlanEntity plan, Date currentTime) { String planCycleType = plan.getPlanCycleType(); // 如果从未维护过,且计划周期已过,则认为已过期 if (plan.getLastMaintainTime() == null) { return true; } LocalDateTime currentDateTime = currentTime.toInstant() .atZone(ZoneId.of("Asia/Shanghai")) .toLocalDateTime(); switch (planCycleType) { case "1": return checkAnnualPlanExpired(plan, currentDateTime); case "2": return checkMonthlyPlanExpired(plan, currentDateTime); case "3": return checkWeeklyPlanExpired(plan, currentDateTime); default: return false; } } /** * 检查年度计划是否过期 */ private boolean checkAnnualPlanExpired(FwDeviceMaintainPlanEntity plan, LocalDateTime currentDateTime) { List<String> planCycleValue = plan.getPlanCycleValue(); Date lastMaintainTime = plan.getLastMaintainTime(); for (String ruleValue : planCycleValue) { String[] parts = ruleValue.split("月"); int ruleMonth = Integer.parseInt(parts[0]); int ruleDay = Integer.parseInt(parts[1].replace("号", "")); LocalDateTime[] cycleBounds = DateUtils.calculateAnnualCycle(lastMaintainTime, ruleMonth, ruleDay); LocalDateTime nextCycleStart = cycleBounds[1]; // 下一周期开始时间 if (currentDateTime.isAfter(nextCycleStart)) { return true; } } return false; } /** * 检查月度计划是否过期 */ private boolean checkMonthlyPlanExpired(FwDeviceMaintainPlanEntity plan, LocalDateTime currentDateTime) { List<String> planCycleValue = plan.getPlanCycleValue(); Date lastMaintainTime = plan.getLastMaintainTime(); for (String ruleValue : planCycleValue) { int ruleDay = Integer.parseInt(ruleValue.replace("号", "")); LocalDateTime[] cycleBounds = DateUtils.calculateMonthlyCycle(lastMaintainTime, ruleDay); LocalDateTime nextCycleStart = cycleBounds[1]; // 下一周期开始时间 if (currentDateTime.isAfter(nextCycleStart)) { return true; } } return false; } /** * 检查周度计划是否过期 */ private boolean checkWeeklyPlanExpired(FwDeviceMaintainPlanEntity plan, LocalDateTime currentDateTime) { List<String> planCycleValue = plan.getPlanCycleValue(); Date lastMaintainTime = plan.getLastMaintainTime(); for (String ruleValue : planCycleValue) { int ruleDayOfWeek = DateUtils.getDayOfWeekNumber(ruleValue); LocalDateTime[] cycleBounds = DateUtils.calculateWeeklyCycle(lastMaintainTime, ruleDayOfWeek); LocalDateTime nextCycleStart = cycleBounds[1]; // 下一周期开始时间 if (currentDateTime.isAfter(nextCycleStart)) { return true; } } return false; } } drone-service/drone-fw/src/main/java/org/sxkj/fw/device/service/IFwDeviceMaintainPlanService.java
@@ -58,4 +58,10 @@ */ List<FwDeviceMaintainPlanExcel> exportFwDeviceMaintainPlan(Wrapper<FwDeviceMaintainPlanEntity> queryWrapper); /** * 批量更新已过期的维护计划 * @param expiredPlanIds 已过期的维护计划ID列表 */ void batchUpdateExpiredPlans(List<Long> expiredPlanIds); } drone-service/drone-fw/src/main/java/org/sxkj/fw/device/service/IFwDeviceMaintainRecordService.java
@@ -32,6 +32,13 @@ * @since 2026-01-08 */ public interface IFwDeviceMaintainRecordService extends BaseService<FwDeviceMaintainRecordEntity> { /** * 查询最新一条维修记录 * @param planId 设备维护计划ID */ FwDeviceMaintainRecordVO selectLastOne(Long planId); /** * 自定义分页 * drone-service/drone-fw/src/main/java/org/sxkj/fw/device/service/impl/FwDeviceMaintainPlanServiceImpl.java
@@ -16,6 +16,8 @@ */ package org.sxkj.fw.device.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import org.sxkj.fw.device.dto.FwDeviceMaintainPlanDTO; import org.sxkj.fw.device.entity.FwDeviceMaintainPlanEntity; import org.sxkj.fw.device.vo.FwDeviceMaintainPlanVO; @@ -58,4 +60,18 @@ return fwDeviceMaintainPlanList; } @Override public void batchUpdateExpiredPlans(List<Long> planIds) { if (planIds == null || planIds.isEmpty()) { return; } // 构建更新条件 LambdaUpdateWrapper<FwDeviceMaintainPlanEntity> updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.in(FwDeviceMaintainPlanEntity::getId, planIds) .set(FwDeviceMaintainPlanEntity::getMaintainStatus, 0); // 未维护状态 // 执行批量更新 update(updateWrapper); } } drone-service/drone-fw/src/main/java/org/sxkj/fw/device/service/impl/FwDeviceMaintainRecordServiceImpl.java
@@ -16,16 +16,26 @@ */ package org.sxkj.fw.device.service.impl; import org.sxkj.fw.device.dto.FwDeviceMaintainRecordDTO; import org.sxkj.fw.device.entity.FwDeviceMaintainRecordEntity; import org.sxkj.fw.device.vo.FwDeviceMaintainRecordVO; import org.sxkj.fw.device.excel.FwDeviceMaintainRecordExcel; import org.sxkj.fw.device.mapper.FwDeviceMaintainRecordMapper; import org.sxkj.fw.device.service.IFwDeviceMaintainRecordService; import org.springframework.stereotype.Service; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import lombok.AllArgsConstructor; import org.springblade.core.mp.base.BaseServiceImpl; import org.springframework.stereotype.Service; import org.sxkj.common.utils.DateUtils; import org.sxkj.fw.device.dto.FwDeviceMaintainRecordDTO; import org.sxkj.fw.device.entity.FwDeviceMaintainPlanEntity; import org.sxkj.fw.device.entity.FwDeviceMaintainRecordEntity; import org.sxkj.fw.device.excel.FwDeviceMaintainRecordExcel; import org.sxkj.fw.device.mapper.FwDeviceMaintainRecordMapper; import org.sxkj.fw.device.service.IFwDeviceMaintainPlanService; import org.sxkj.fw.device.service.IFwDeviceMaintainRecordService; import org.sxkj.fw.device.vo.FwDeviceMaintainRecordVO; import java.time.DayOfWeek; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Date; import java.util.List; /** @@ -35,21 +45,128 @@ * @since 2026-01-08 */ @Service @AllArgsConstructor public class FwDeviceMaintainRecordServiceImpl extends BaseServiceImpl<FwDeviceMaintainRecordMapper, FwDeviceMaintainRecordEntity> implements IFwDeviceMaintainRecordService { private final IFwDeviceMaintainPlanService fwDeviceMaintainPlanService; @Override public FwDeviceMaintainRecordVO selectLastOne(Long planId) { return baseMapper.selectLastOne(planId); } @Override public IPage<FwDeviceMaintainRecordVO> selectFwDeviceMaintainRecordPage(IPage<FwDeviceMaintainRecordVO> page, FwDeviceMaintainRecordDTO fwDeviceMaintainRecord) { return page.setRecords(baseMapper.selectFwDeviceMaintainRecordPage(page, fwDeviceMaintainRecord)); } @Override public boolean saveOrUpdate(FwDeviceMaintainRecordEntity entity) { // 维护时间 Date maintainTime = entity.getMaintainTime(); // 查询计划的规则 FwDeviceMaintainPlanEntity fwDeviceMaintainPlan = fwDeviceMaintainPlanService.getById(entity.getPlanId()); if (null == fwDeviceMaintainPlan) { return false; } // 验证维护时间是否符合计划周期 boolean isInCycle = validateMaintainTimeInCycle(maintainTime, fwDeviceMaintainPlan); // 在当前周期内修改计划维修状态 if (isInCycle) { updatePlanMaintainStatus(fwDeviceMaintainPlan, maintainTime); } return super.saveOrUpdate(entity); } @Override public List<FwDeviceMaintainRecordExcel> exportFwDeviceMaintainRecord(Wrapper<FwDeviceMaintainRecordEntity> queryWrapper) { List<FwDeviceMaintainRecordExcel> fwDeviceMaintainRecordList = baseMapper.exportFwDeviceMaintainRecord(queryWrapper); //fwDeviceMaintainRecordList.forEach(fwDeviceMaintainRecord -> { // fwDeviceMaintainRecord.setTypeName(DictCache.getValue(DictEnum.YES_NO, FwDeviceMaintainRecord.getType())); //}); return fwDeviceMaintainRecordList; } // region 维修计划状态更新相关 /** * 验证维护时间是否在计划周期内 */ private boolean validateMaintainTimeInCycle(Date maintainTime, FwDeviceMaintainPlanEntity plan) { String planCycleType = plan.getPlanCycleType(); List<String> planCycleValue = plan.getPlanCycleValue(); for (String ruleValue : planCycleValue) { boolean isValid = false; switch (planCycleType) { case "1": // 年度规则 isValid = validateAnnualCycle(maintainTime, ruleValue); break; case "2": // 月度规则 isValid = validateMonthlyCycle(maintainTime, ruleValue); break; case "3": // 周度规则 isValid = validateWeeklyCycle(maintainTime, ruleValue); break; } if (isValid) { return true; } } return false; } /** * 验证年度周期 */ private boolean validateAnnualCycle(Date maintainTime, String ruleValue) { String[] parts = ruleValue.split("月"); int ruleMonth = Integer.parseInt(parts[0]); int ruleDay = Integer.parseInt(parts[1].replace("号", "")); LocalDateTime[] cycleBounds = DateUtils.calculateAnnualCycle(new Date(), ruleMonth, ruleDay); return DateUtils.isMaintainTimeInCycle(maintainTime, cycleBounds[0], cycleBounds[1]); } /** * 验证月度周期 */ private boolean validateMonthlyCycle(Date maintainTime, String ruleValue) { int ruleDay = Integer.parseInt(ruleValue.replace("号", "")); LocalDateTime[] cycleBounds = DateUtils.calculateMonthlyCycle(new Date(), ruleDay); return DateUtils.isMaintainTimeInCycle(maintainTime, cycleBounds[0], cycleBounds[1]); } /** * 验证周度周期 */ private boolean validateWeeklyCycle(Date maintainTime, String ruleValue) { int ruleDayOfWeek = DateUtils.getDayOfWeekNumber(ruleValue); LocalDateTime[] cycleBounds = DateUtils.calculateWeeklyCycle(new Date(), ruleDayOfWeek); // String dateStart = cycleBounds[0].atZone(ZoneId.of("Asia/Shanghai")).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); // String dateEnd = cycleBounds[1].atZone(ZoneId.of("Asia/Shanghai")).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); // String dateMaintain = maintainTime.toInstant().atZone(ZoneId.of("Asia/Shanghai")).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); // // log.debug("维护时间: " + dateMaintain + ", 周期开始: " + dateStart + ", 周期结束: " + dateEnd); return DateUtils.isMaintainTimeInCycle(maintainTime, cycleBounds[0], cycleBounds[1]); } /** * 更新计划维护状态 */ private void updatePlanMaintainStatus(FwDeviceMaintainPlanEntity plan, Date maintainTime) { FwDeviceMaintainPlanEntity updateEntity = new FwDeviceMaintainPlanEntity(); updateEntity.setId(plan.getId()); updateEntity.setMaintainStatus(1); // 维护状态 updateEntity.setLastMaintainTime(maintainTime); // 最后一次维护时间 fwDeviceMaintainPlanService.updateById(updateEntity); } // endregion 维修计划状态更新相关 }