From c21cb3d8cebd9cdef4df2efa372c5b9257c713e4 Mon Sep 17 00:00:00 2001
From: 吴羡 <16976746+Xian-TT3@user.noreply.gitee.com>
Date: 星期三, 28 十月 2026 17:57:40 +0800
Subject: [PATCH] feat(device,supply,satisfaction): 设备计划后端改造、耗材存放位置与满意度照片支持

---
 cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceMaintenancePlanServiceImpl.java |  387 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 385 insertions(+), 2 deletions(-)

diff --git a/cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceMaintenancePlanServiceImpl.java b/cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceMaintenancePlanServiceImpl.java
index 81c5582..1b680fb 100644
--- a/cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceMaintenancePlanServiceImpl.java
+++ b/cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceMaintenancePlanServiceImpl.java
@@ -2,6 +2,7 @@
 
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -16,12 +17,17 @@
 import com.ruoyi.common.utils.WxCpUtils;
 import com.ruoyi.device.dto.DeviceMaintenancePlanDetailsDto;
 import com.ruoyi.device.dto.DeviceMaintenancePlanDto;
+import com.ruoyi.device.mapper.DeviceMaintenanceMapper;
 import com.ruoyi.device.mapper.DeviceMaintenancePlanDetailsMapper;
 import com.ruoyi.device.mapper.DeviceMaintenancePlanMapper;
+import com.ruoyi.device.mapper.DeviceMapper;
+import com.ruoyi.device.pojo.Device;
+import com.ruoyi.device.pojo.DeviceMaintenance;
 import com.ruoyi.device.pojo.DeviceMaintenancePlan;
 import com.ruoyi.device.pojo.DeviceMaintenancePlanDetails;
 import com.ruoyi.device.service.DeviceMaintenancePlanDetailsService;
 import com.ruoyi.device.service.DeviceMaintenancePlanService;
+import com.ruoyi.framework.exception.ErrorException;
 import com.ruoyi.inspect.util.HackLoopTableRenderPolicy;
 import com.ruoyi.inspect.util.UserUtils;
 import com.ruoyi.system.mapper.UserMapper;
@@ -30,15 +36,21 @@
 import org.springframework.beans.BeanUtils;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 import jakarta.annotation.Resource;
 import jakarta.servlet.http.HttpServletResponse;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.URLEncoder;
+import java.time.LocalDate;
 import java.time.LocalDateTime;
+import java.time.YearMonth;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 
 /**
@@ -50,6 +62,7 @@
  * @since 2024-12-16 06:10:52
  */
 @Service
+@Transactional(rollbackFor = Exception.class)
 public class DeviceMaintenancePlanServiceImpl extends ServiceImpl<DeviceMaintenancePlanMapper, DeviceMaintenancePlan> implements DeviceMaintenancePlanService {
 
     @Resource
@@ -62,6 +75,10 @@
     private InformationNotificationService informationNotificationService;
     @Resource
     private UserMapper userMapper;
+    @Resource
+    private DeviceMapper deviceMapper;
+    @Resource
+    private DeviceMaintenanceMapper deviceMaintenanceMapper;
 
     /**
      * 鍒嗛〉鏌ヨ璁惧淇濆吇璁″垝
@@ -181,9 +198,9 @@
                     put("deviceMaintenancePlan", deviceMaintenancePlan);
                     put("deviceMaintenancePlanDetailsDtoList", deviceMaintenancePlanDetailsDtoList);
                     // 缂栧埗浜虹鍚嶅湴鍧�
-                    put("compilerUrl", UserUtils.getFinalUserSignatureUrl(deviceMaintenancePlan.getCompilerId()));
+                    put("compilerUrl", UserUtils.getFinalUserSignatureOrTransparent(deviceMaintenancePlan.getCompilerId()));
                     // 瀹℃牳浜虹鍚嶅湴鍧�
-                    put("auditUrl", UserUtils.getFinalUserSignatureUrl(deviceMaintenancePlan.getAuditId()));
+                    put("auditUrl", UserUtils.getFinalUserSignatureOrTransparent(deviceMaintenancePlan.getAuditId()));
                 }});
 
         try {
@@ -288,5 +305,371 @@
         return Result.success();
     }
 
+    /**
+     * 鎸夊勾浠芥煡璇㈣澶囦繚鍏昏鍒掞紙涓嶅瓨鍦ㄥ垯鑷姩鍒涘缓锛夛紝骞惰嚜鍔ㄨˉ鍏ㄥ凡鍒扮偣鐨勮澶囨槑缁�
+     */
+    @Override
+    public DeviceMaintenancePlanDto getOrCreatePlanByYear(String planYear) {
+        if (StringUtils.isBlank(planYear)) {
+            planYear = String.valueOf(LocalDate.now().getYear());
+        }
+        DeviceMaintenancePlan plan = getOrCreatePlanEntity(planYear);
+        ensureMaintenanceDetails(plan);
+        DeviceMaintenancePlanDto planDto = new DeviceMaintenancePlanDto();
+        BeanUtils.copyProperties(plan, planDto);
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+        planDto.setDatePreparationStr(plan.getDatePreparation() == null ? null : plan.getDatePreparation().format(formatter));
+        planDto.setAuditDateStr(plan.getAuditDate() == null ? null : plan.getAuditDate().format(formatter));
+        if (StringUtils.isBlank(planDto.getCompiler()) && plan.getCompilerId() != null) {
+            User compiler = userMapper.selectById(plan.getCompilerId());
+            if (compiler != null) {
+                planDto.setCompiler(compiler.getName());
+            }
+        }
+        if (StringUtils.isBlank(planDto.getAudit()) && plan.getAuditId() != null) {
+            User audit = userMapper.selectById(plan.getAuditId());
+            if (audit != null) {
+                planDto.setAudit(audit.getName());
+            }
+        }
+        return planDto;
+    }
+
+    /**
+     * 淇濆吇璁″垝骞翠唤鍒楄〃锛氬垹闄ゆ病鏈変换浣曟槑缁嗙殑绌哄勾浠借鍒掑悗锛岃繑鍥炴湁鏄庣粏鐨勫勾浠斤紙鍊掑簭锛�
+     */
+    @Override
+    public List<String> getMaintenancePlanYears() {
+        // 鍘嗗彶鏁版嵁鑷剤锛氬厛璁╁凡瀹℃牳鐨勬槑缁嗘寜瀹℃牳瀹屾垚骞翠唤褰掍綅锛屽啀娓呯悊绌哄勾浠借鍒�
+        reclassifyReviewedDetails();
+        List<DeviceMaintenancePlan> plans = baseMapper.selectList(null);
+        for (DeviceMaintenancePlan plan : plans) {
+            Long detailCount = deviceMaintenancePlanDetailsMapper.selectCount(Wrappers.<DeviceMaintenancePlanDetails>lambdaQuery()
+                    .eq(DeviceMaintenancePlanDetails::getMaintenancePlanId, plan.getMaintenancePlanId()));
+            if (detailCount == null || detailCount == 0) {
+                removeById(plan.getMaintenancePlanId());
+            }
+        }
+        return baseMapper.selectPlanYears();
+    }
+
+    /**
+     * 璁惧妗f鍙樻洿鍚庯紝绔嬪嵆鎶婅澶囩櫥璁板埌涓嬫淇濆吇鏃ユ湡鎵�鍦ㄥ勾浠界殑璁惧淇濆吇璁″垝鏄庣粏
+     */
+    @Override
+    public void syncDeviceMaintenanceDetail(Device device) {
+        LocalDate due = calcMaintenanceDueDate(device);
+        if (due == null) {
+            return;
+        }
+        DeviceMaintenancePlan plan = getOrCreatePlanEntity(String.valueOf(due.getYear()));
+        fillMaintenanceDetailIfAbsent(plan, device);
+    }
+
+    /**
+     * 瀹℃牳閫氳繃鍚庯紝鎸夊鏍搁�氳繃褰撳ぉ婊氬姩璁惧妗f鐨勬渶杩�/涓嬫淇濆吇鏃ユ湡
+     */
+    @Override
+    public void updateDeviceMaintenanceDate(Integer deviceId, LocalDate maintenanceDate) {
+        if (deviceId == null || maintenanceDate == null) {
+            return;
+        }
+        Device device = deviceMapper.selectById(deviceId);
+        if (device == null) {
+            return;
+        }
+        LocalDateTime recordDate = maintenanceDate.atStartOfDay();
+        // 鍙厑璁稿悜鍓嶆粴鍔紝閬垮厤琛ュ綍鏃ц褰曟椂鎶婃棩鏈熸敼鍥炲幓
+        if (device.getLastMaintenanceDate() != null && !recordDate.isAfter(device.getLastMaintenanceDate())) {
+            return;
+        }
+        device.setLastMaintenanceDate(recordDate);
+        if (StringUtils.isNotBlank(device.getMaintenanceCycle())) {
+            try {
+                device.setNextMaintenanceDate(recordDate.plusMonths(Long.parseLong(device.getMaintenanceCycle().trim())));
+            } catch (NumberFormatException ignored) {
+            }
+        }
+        deviceMapper.updateById(device);
+        // 鏈疆淇濆吇瀹屾垚鍚庣珛鍗崇櫥璁颁笅涓�杞紝鍘嗗彶鏄庣粏淇濇寔涓嶅姩
+        syncDeviceMaintenanceDetail(device);
+    }
+
+    /**
+     * 瀹℃牳閫氳繃鍚庯紝鎶婅鍒掓槑缁嗗綊鍒板鏍稿畬鎴愬勾浠界殑璁″垝涓嬶紱鎻愬墠瀹屾垚鐨勬槑缁嗕笉鍐嶇暀鍦ㄥ師璁″垝骞翠唤
+     */
+    @Override
+    public void reclassifyMaintenanceDetailByReviewDate(Integer maintenancePlanDetailId, LocalDate reviewDate) {
+        if (maintenancePlanDetailId == null || reviewDate == null) {
+            return;
+        }
+        DeviceMaintenancePlanDetails detail = deviceMaintenancePlanDetailsMapper.selectById(maintenancePlanDetailId);
+        if (detail == null || detail.getMaintenancePlanId() == null) {
+            return;
+        }
+        DeviceMaintenancePlan sourcePlan = baseMapper.selectById(detail.getMaintenancePlanId());
+        if (sourcePlan == null) {
+            return;
+        }
+        String targetYear = String.valueOf(reviewDate.getYear());
+        if (StringUtils.isNotBlank(sourcePlan.getPlanYear()) && targetYear.equals(sourcePlan.getPlanYear().trim())) {
+            return;
+        }
+        moveMaintenanceDetailToYear(detail, reviewDate);
+    }
+
+    /**
+     * 鎶婅鍒掓槑缁嗘尓鍒板鏍稿畬鎴愬勾浠界殑璁″垝涓嬶紝璁″垝鏃ユ湡鎸夊疄闄呭畬鎴愭棩鏈熻褰�
+     */
+    private void moveMaintenanceDetailToYear(DeviceMaintenancePlanDetails detail, LocalDate reviewDate) {
+        DeviceMaintenancePlan targetPlan = getOrCreatePlanEntity(String.valueOf(reviewDate.getYear()));
+        DeviceMaintenancePlanDetails update = new DeviceMaintenancePlanDetails();
+        update.setMaintenancePlanDetailId(detail.getMaintenancePlanDetailId());
+        update.setMaintenancePlanId(targetPlan.getMaintenancePlanId());
+        // 璁″垝鏃ユ湡鎸夊疄闄呭畬鎴愭棩鏈熻褰曪紝鍒楄〃鎸夊畬鎴愬勾浠芥帓搴忥紝涓嶅啀鎸夊師璁″垝鏃ユ湡鏍囩孩
+        update.setMaintenanceDate(reviewDate);
+        deviceMaintenancePlanDetailsMapper.updateById(update);
+    }
+
+    /**
+     * 鍘嗗彶鏁版嵁鑷剤锛氬凡瀹℃牳閫氳繃浣嗚鍒掑勾浠戒笌瀹℃牳瀹屾垚骞翠唤涓嶄竴鑷寸殑鏄庣粏锛屾寜瀹℃牳瀹屾垚骞翠唤褰掍綅
+     */
+    private void reclassifyReviewedDetails() {
+        List<DeviceMaintenance> records = deviceMaintenanceMapper.selectList(Wrappers.<DeviceMaintenance>lambdaQuery()
+                .eq(DeviceMaintenance::getReviewStatus, 1)
+                .isNotNull(DeviceMaintenance::getMaintenancePlanDetailId)
+                .isNotNull(DeviceMaintenance::getReviewTime));
+        if (CollectionUtils.isEmpty(records)) {
+            return;
+        }
+        List<Integer> detailIds = records.stream().map(DeviceMaintenance::getMaintenancePlanDetailId)
+                .distinct().collect(Collectors.toList());
+        Map<Integer, DeviceMaintenancePlanDetails> detailMap = deviceMaintenancePlanDetailsMapper.selectBatchIds(detailIds)
+                .stream().collect(Collectors.toMap(DeviceMaintenancePlanDetails::getMaintenancePlanDetailId, item -> item, (a, b) -> a));
+        Map<Integer, String> planYears = baseMapper.selectList(null).stream()
+                .collect(Collectors.toMap(DeviceMaintenancePlan::getMaintenancePlanId,
+                        item -> item.getPlanYear() == null ? "" : item.getPlanYear().trim(), (a, b) -> a));
+        for (DeviceMaintenance record : records) {
+            DeviceMaintenancePlanDetails detail = detailMap.get(record.getMaintenancePlanDetailId());
+            if (detail == null || detail.getMaintenancePlanId() == null) {
+                continue;
+            }
+            String reviewYear = String.valueOf(record.getReviewTime().getYear());
+            if (reviewYear.equals(planYears.get(detail.getMaintenancePlanId()))) {
+                continue;
+            }
+            moveMaintenanceDetailToYear(detail, record.getReviewTime().toLocalDate());
+        }
+    }
+
+    /**
+     * 鍒嗛〉鏌ヨ璁惧淇濆吇璁″垝璇︽儏锛堝惈鍒扮偣鏍囩孩锛�
+     */
+    @Override
+    public IPage<DeviceMaintenancePlanDetailsDto> pageMaintenancePlanDetail(Page page, Integer planId, String deviceName, String deviceNumber) {
+        if (planId == null) {
+            return new Page<>();
+        }
+        return deviceMaintenancePlanDetailsMapper.pageMaintenancePlanDetail(page, planId, deviceName, deviceNumber);
+    }
+
+    /**
+     * 鏌ヨ骞翠唤瀵瑰簲鐨勮鍒掞紝涓嶅瓨鍦ㄥ垯鍒涘缓
+     */
+    private DeviceMaintenancePlan getOrCreatePlanEntity(String planYear) {
+        DeviceMaintenancePlan plan = baseMapper.selectOne(Wrappers.<DeviceMaintenancePlan>lambdaQuery()
+                .eq(DeviceMaintenancePlan::getPlanYear, planYear)
+                .orderByDesc(DeviceMaintenancePlan::getMaintenancePlanId)
+                .last("limit 1"));
+        if (plan == null) {
+            plan = new DeviceMaintenancePlan();
+            plan.setPlanName(planYear + "骞村害璁惧淇濆吇璁″垝");
+            plan.setPlanYear(planYear);
+            Integer userId = SecurityUtils.getUserId().intValue();
+            User user = userMapper.selectById(userId);
+            plan.setCompilerId(userId);
+            plan.setCompiler(user == null ? null : user.getName());
+            plan.setDatePreparation(LocalDateTime.now());
+            baseMapper.insert(plan);
+        }
+        return plan;
+    }
+
+    /**
+     * 鎵弿鎵�鏈夎澶囷紝鎶婂簲淇濆吇鏃ユ湡钀藉湪璇ヨ鍒掑勾浠界殑璁惧琛ヨ繘璁″垝锛堝綋鍓嶅勾鍙婁互寰�骞村害鍖呭惈閫炬湡鏈繚鍏荤殑璁惧锛�
+     */
+    private void ensureMaintenanceDetails(DeviceMaintenancePlan plan) {
+        List<Device> devices = deviceMapper.selectList(null);
+        for (Device device : devices) {
+            fillMaintenanceDetailIfAbsent(plan, device);
+        }
+    }
+
+    /**
+     * 璁惧鏈疆搴斾繚鍏绘棩鏈燂紙娌″~涓嬫鏃ユ湡鏃剁敤鏈�杩戞棩鏈�+淇濆吇鍛ㄦ湡鍏滃簳锛�
+     */
+    private LocalDate calcMaintenanceDueDate(Device device) {
+        LocalDateTime base = device.getNextMaintenanceDate();
+        if (base == null && device.getLastMaintenanceDate() != null && StringUtils.isNotBlank(device.getMaintenanceCycle())) {
+            try {
+                base = device.getLastMaintenanceDate().plusMonths(Long.parseLong(device.getMaintenanceCycle().trim()));
+            } catch (NumberFormatException ignored) {
+            }
+        }
+        return base == null ? null : base.toLocalDate();
+    }
+
+    /**
+     * 璁″垝涓皻鏃犺璁惧鏈疆鏄庣粏鏃惰嚜鍔ㄨˉ涓�鏉★紱淇濆吇鏃堕棿鍙栧簲淇濆吇鏃ユ湡
+     */
+    private void fillMaintenanceDetailIfAbsent(DeviceMaintenancePlan plan, Device device) {
+        LocalDate due = calcMaintenanceDueDate(device);
+        if (due == null) {
+            return;
+        }
+        if (StringUtils.isBlank(plan.getPlanYear())) {
+            return;
+        }
+        int planYearValue;
+        try {
+            planYearValue = Integer.parseInt(plan.getPlanYear().trim());
+        } catch (NumberFormatException e) {
+            return;
+        }
+        int dueYear = due.getYear();
+        // 鏈潵骞翠唤鐨勮鍒掑彧鐧昏搴斾繚鍏绘棩鏈熷氨鍦ㄨ骞翠唤鐨勮澶囷紝閬垮厤鎶婂線骞寸殑鏃ф棩鏈熻ˉ杩涙湭鏉ョ殑璁″垝
+        if (planYearValue > LocalDate.now().getYear()) {
+            if (dueYear != planYearValue) {
+                return;
+            }
+        } else if (dueYear > planYearValue) {
+            return;
+        }
+        // 鍚屼竴璁惧鍚屼竴搴斾繚鍏绘湀浠藉彧淇濈暀涓�鏉★紝閬垮厤鎵嬫敼杩囪鍐呮棩鏈熷悗鍒锋柊閲嶅鐢熸垚
+        LocalDate monthStart = due.withDayOfMonth(1);
+        Long exist = deviceMaintenancePlanDetailsMapper.selectCount(Wrappers.<DeviceMaintenancePlanDetails>lambdaQuery()
+                .eq(DeviceMaintenancePlanDetails::getMaintenancePlanId, plan.getMaintenancePlanId())
+                .eq(DeviceMaintenancePlanDetails::getDeviceId, device.getId())
+                .ge(DeviceMaintenancePlanDetails::getMaintenanceDate, monthStart)
+                .lt(DeviceMaintenancePlanDetails::getMaintenanceDate, monthStart.plusMonths(1)));
+        if (exist != null && exist > 0) {
+            return;
+        }
+        DeviceMaintenancePlanDetails detail = new DeviceMaintenancePlanDetails();
+        detail.setMaintenancePlanId(plan.getMaintenancePlanId());
+        detail.setDeviceId(device.getId());
+        detail.setMaintenanceIntervals(device.getMaintenanceCycle());
+        detail.setMaintenanceDate(due);
+        deviceMaintenancePlanDetailsMapper.insert(detail);
+    }
+
+    /**
+     * 涓轰繚鍏昏褰曟壘涓�鏉″彲鍏宠仈鐨勮鍒掓槑缁嗭紱娌℃湁鍙敤鏄庣粏鏃跺湪褰撳墠骞村害璁″垝鏂板鏈疆鏄庣粏
+     */
+    @Override
+    public DeviceMaintenancePlanDetails resolveMaintenancePlanDetail(Integer deviceId, Integer maintenancePlanDetailId, LocalDate maintenanceDate) {
+        if (maintenancePlanDetailId != null) {
+            DeviceMaintenancePlanDetails detail = deviceMaintenancePlanDetailsMapper.selectById(maintenancePlanDetailId);
+            if (detail == null) {
+                throw new ErrorException("淇濆吇璁″垝鏄庣粏涓嶅瓨鍦�");
+            }
+            return detail;
+        }
+        if (deviceId == null) {
+            throw new ErrorException("缂哄皯璁惧id");
+        }
+        Device device = deviceMapper.selectById(deviceId);
+        if (device == null) {
+            throw new ErrorException("淇濆吇璁惧涓嶅瓨鍦�");
+        }
+        LocalDate planDate = maintenanceDate == null ? LocalDate.now() : maintenanceDate;
+        DeviceMaintenancePlan plan = getOrCreatePlanEntity(String.valueOf(planDate.getYear()));
+        List<DeviceMaintenancePlanDetails> details = deviceMaintenancePlanDetailsMapper.selectList(
+                Wrappers.<DeviceMaintenancePlanDetails>lambdaQuery()
+                        .eq(DeviceMaintenancePlanDetails::getDeviceId, deviceId)
+                        .orderByAsc(DeviceMaintenancePlanDetails::getMaintenanceDate));
+        DeviceMaintenancePlanDetails nearest = null;
+        long nearestDistance = Long.MAX_VALUE;
+        for (DeviceMaintenancePlanDetails detail : details) {
+            // 宸叉湁淇濆吇璁板綍鐨勬槑缁嗕唬琛ㄥ凡瀹屾垚鐨勪竴杞紝鍚庣画璁板綍姘歌繙涓嶅鐢ㄣ�佷笉瑕嗙洊
+            Long recordCount = deviceMaintenanceMapper.selectCount(Wrappers.<DeviceMaintenance>lambdaQuery()
+                    .eq(DeviceMaintenance::getMaintenancePlanDetailId, detail.getMaintenancePlanDetailId()));
+            if (recordCount != null && recordCount > 0) {
+                continue;
+            }
+            if (detail.getMaintenancePlanId() != null && detail.getMaintenancePlanId().equals(plan.getMaintenancePlanId())) {
+                return detail;
+            }
+            long distance = monthDistance(detail.getMaintenanceDate(), planDate);
+            if (distance < nearestDistance) {
+                nearest = detail;
+                nearestDistance = distance;
+            }
+        }
+        if (nearest != null) {
+            return nearest;
+        }
+        DeviceMaintenancePlanDetails detail = new DeviceMaintenancePlanDetails();
+        detail.setMaintenancePlanId(plan.getMaintenancePlanId());
+        detail.setDeviceId(device.getId());
+        detail.setMaintenanceIntervals(device.getMaintenanceCycle());
+        detail.setMaintenanceDate(planDate);
+        deviceMaintenancePlanDetailsMapper.insert(detail);
+        return detail;
+    }
+
+    /**
+     * 淇敼璁惧淇濆吇璁″垝璇︽儏锛堝凡瀹℃牳鐨勬槑缁嗕笉鍏佽淇敼锛�
+     */
+    @Override
+    public boolean updateMaintenancePlanDetail(DeviceMaintenancePlanDetails detail) {
+        if (detail == null || detail.getMaintenancePlanDetailId() == null) {
+            throw new ErrorException("缂哄皯璁惧淇濆吇璁″垝鏄庣粏id");
+        }
+        DeviceMaintenance record = deviceMaintenanceMapper.selectOne(Wrappers.<DeviceMaintenance>lambdaQuery()
+                .eq(DeviceMaintenance::getMaintenancePlanDetailId, detail.getMaintenancePlanDetailId())
+                .last("limit 1"));
+        if (record != null && record.getReviewStatus() != null) {
+            throw new ErrorException("璇ヨ鍒掑凡瀹℃牳锛屼笉鍏佽淇敼");
+        }
+        return deviceMaintenancePlanDetailsMapper.updateById(detail) > 0;
+    }
+
+    /**
+     * 鍒犻櫎璁惧淇濆吇璁″垝璇︽儏锛涘凡鍑哄鏍哥粨鏋滅殑璁板綍闅忓巻鍙茬暀妗o紝鏈鏍哥殑璁板綍闅忔槑缁嗕竴璧峰垹闄�
+     */
+    @Override
+    public boolean removeMaintenancePlanDetail(Integer maintenancePlanDetailId) {
+        if (maintenancePlanDetailId == null) {
+            throw new ErrorException("缂哄皯璁惧淇濆吇璁″垝鏄庣粏id");
+        }
+        DeviceMaintenancePlanDetails detail = deviceMaintenancePlanDetailsMapper.selectById(maintenancePlanDetailId);
+        if (detail == null) {
+            return false;
+        }
+        List<DeviceMaintenance> records = deviceMaintenanceMapper.selectList(Wrappers.<DeviceMaintenance>lambdaQuery()
+                .eq(DeviceMaintenance::getMaintenancePlanDetailId, maintenancePlanDetailId));
+        // 瀹℃牳涓嶉�氳繃鐨勮褰曞厑璁歌繛鍚屾槑缁嗕竴璧峰垹闄わ紱瀹℃牳閫氳繃鐨勮褰曚负鍘嗗彶鐣欐。锛屼笉鍏佽鍒犻櫎
+        boolean hasPassedRecord = records.stream().anyMatch(item -> item.getReviewStatus() != null
+                && item.getReviewStatus() == 1);
+        if (hasPassedRecord) {
+            throw new ErrorException("璇ヨ鍒掑凡瀛樺湪瀹℃牳閫氳繃鐨勪繚鍏昏褰曪紝涓轰繚鐣欏巻鍙茶褰曚笉鍏佽鍒犻櫎");
+        }
+        if (!records.isEmpty()) {
+            List<Integer> recordIds = records.stream().map(DeviceMaintenance::getId).collect(Collectors.toList());
+            deviceMaintenanceMapper.deleteBatchIds(recordIds);
+        }
+        return deviceMaintenancePlanDetailsMapper.deleteById(maintenancePlanDetailId) > 0;
+    }
+
+    /**
+     * 涓や釜淇濆吇鏃ユ湡涔嬮棿鐩稿樊鐨勬湀鏁�
+     */
+    private long monthDistance(LocalDate from, LocalDate target) {
+        if (from == null || target == null) {
+            return Long.MAX_VALUE;
+        }
+        return Math.abs(ChronoUnit.MONTHS.between(YearMonth.from(from), YearMonth.from(target)));
+    }
 
 }

--
Gitblit v1.9.3