From 4e48664942bbefe70699ca43d25cbbd56ecc8daa Mon Sep 17 00:00:00 2001
From: xiebin <vip_xiaobin810@163.com>
Date: Mon, 19 Jan 2026 15:17:12 +0800
Subject: [PATCH] update-维修计划状态更新、新增定时器修改状态

---
 /dev/null                                                                                                   |   25 --
 drone-service/drone-fw/src/main/java/org/sxkj/fw/FwApplication.java                                         |    2 
 drone-service/drone-fw/src/main/java/org/sxkj/fw/device/service/IFwDeviceMaintainPlanService.java           |    6 
 drone-service/drone-fw/src/main/java/org/sxkj/fw/device/service/impl/FwDeviceMaintainRecordServiceImpl.java |  137 ++++++++++++++-
 drone-service/drone-fw/src/main/java/org/sxkj/fw/device/mapper/FwDeviceMaintainRecordMapper.xml             |    6 
 drone-service/drone-fw/src/main/java/org/sxkj/fw/device/service/IFwDeviceMaintainRecordService.java         |    7 
 drone-service/drone-fw/src/main/java/org/sxkj/fw/device/service/impl/FwDeviceMaintainPlanServiceImpl.java   |   16 +
 drone-service/drone-fw/src/main/java/org/sxkj/fw/device/mapper/FwDeviceMaintainRecordMapper.java            |    6 
 drone-common/src/main/java/org/sxkj/common/utils/DateUtils.java                                             |  134 ++++++++++++++
 drone-service/drone-fw/src/main/java/org/sxkj/fw/device/scheduler/MaintainPlanScheduler.java                |  156 +++++++++++++++++
 10 files changed, 459 insertions(+), 36 deletions(-)

diff --git a/drone-common/src/main/java/org/sxkj/common/utils/DataUtils.java b/drone-common/src/main/java/org/sxkj/common/utils/DataUtils.java
deleted file mode 100644
index aa0caeb..0000000
--- a/drone-common/src/main/java/org/sxkj/common/utils/DataUtils.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package org.sxkj.common.utils;
-
-import org.sxkj.common.enums.DateEnum;
-
-import java.time.LocalDateTime;
-
-public class DataUtils {
-	/**
-	 * 判断两个时间是否跨日、跨月或跨年
-	 * @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;
-		}
-	}
-}
diff --git a/drone-common/src/main/java/org/sxkj/common/utils/DateUtils.java b/drone-common/src/main/java/org/sxkj/common/utils/DateUtils.java
new file mode 100644
index 0000000..5a4f565
--- /dev/null
+++ b/drone-common/src/main/java/org/sxkj/common/utils/DateUtils.java
@@ -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; // 默认为星期日
+		}
+	}
+
+
+}
diff --git a/drone-service/drone-fw/src/main/java/org/sxkj/fw/FwApplication.java b/drone-service/drone-fw/src/main/java/org/sxkj/fw/FwApplication.java
index a975e44..b97dfe7 100644
--- a/drone-service/drone-fw/src/main/java/org/sxkj/fw/FwApplication.java
+++ b/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) {
diff --git a/drone-service/drone-fw/src/main/java/org/sxkj/fw/device/mapper/FwDeviceMaintainRecordMapper.java b/drone-service/drone-fw/src/main/java/org/sxkj/fw/device/mapper/FwDeviceMaintainRecordMapper.java
index 8907e98..62a69ba 100644
--- a/drone-service/drone-fw/src/main/java/org/sxkj/fw/device/mapper/FwDeviceMaintainRecordMapper.java
+++ b/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
diff --git a/drone-service/drone-fw/src/main/java/org/sxkj/fw/device/mapper/FwDeviceMaintainRecordMapper.xml b/drone-service/drone-fw/src/main/java/org/sxkj/fw/device/mapper/FwDeviceMaintainRecordMapper.xml
index 424fad2..76715bb 100644
--- a/drone-service/drone-fw/src/main/java/org/sxkj/fw/device/mapper/FwDeviceMaintainRecordMapper.xml
+++ b/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
diff --git a/drone-service/drone-fw/src/main/java/org/sxkj/fw/device/scheduler/MaintainPlanScheduler.java b/drone-service/drone-fw/src/main/java/org/sxkj/fw/device/scheduler/MaintainPlanScheduler.java
new file mode 100644
index 0000000..b4d5d59
--- /dev/null
+++ b/drone-service/drone-fw/src/main/java/org/sxkj/fw/device/scheduler/MaintainPlanScheduler.java
@@ -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;
+    }
+
+}
diff --git a/drone-service/drone-fw/src/main/java/org/sxkj/fw/device/service/IFwDeviceMaintainPlanService.java b/drone-service/drone-fw/src/main/java/org/sxkj/fw/device/service/IFwDeviceMaintainPlanService.java
index 1002cbc..b93b863 100644
--- a/drone-service/drone-fw/src/main/java/org/sxkj/fw/device/service/IFwDeviceMaintainPlanService.java
+++ b/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);
+
 }
diff --git a/drone-service/drone-fw/src/main/java/org/sxkj/fw/device/service/IFwDeviceMaintainRecordService.java b/drone-service/drone-fw/src/main/java/org/sxkj/fw/device/service/IFwDeviceMaintainRecordService.java
index 6da6af0..7ed4264 100644
--- a/drone-service/drone-fw/src/main/java/org/sxkj/fw/device/service/IFwDeviceMaintainRecordService.java
+++ b/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);
+
 	/**
 	 * 自定义分页
 	 *
diff --git a/drone-service/drone-fw/src/main/java/org/sxkj/fw/device/service/impl/FwDeviceMaintainPlanServiceImpl.java b/drone-service/drone-fw/src/main/java/org/sxkj/fw/device/service/impl/FwDeviceMaintainPlanServiceImpl.java
index 5e37ba6..8d2dd25 100644
--- a/drone-service/drone-fw/src/main/java/org/sxkj/fw/device/service/impl/FwDeviceMaintainPlanServiceImpl.java
+++ b/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);
+	}
 }
diff --git a/drone-service/drone-fw/src/main/java/org/sxkj/fw/device/service/impl/FwDeviceMaintainRecordServiceImpl.java b/drone-service/drone-fw/src/main/java/org/sxkj/fw/device/service/impl/FwDeviceMaintainRecordServiceImpl.java
index 7d69355..b01cf03 100644
--- a/drone-service/drone-fw/src/main/java/org/sxkj/fw/device/service/impl/FwDeviceMaintainRecordServiceImpl.java
+++ b/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 维修计划状态更新相关
+
 }

--
Gitblit v1.9.3