From 8ad863082097ffdcab4a8e7f426d344a1fe6f892 Mon Sep 17 00:00:00 2001
From: 吴羡 <16976746+Xian-TT3@user.noreply.gitee.com>
Date: 星期六, 07 十一月 2026 00:27:02 +0800
Subject: [PATCH] 修复人员管理接口校验与导出,完善设备保养计划与文件受控功能

---
 cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceMaintenancePlanServiceImpl.java |  150 ++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 133 insertions(+), 17 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 1b680fb..64e266f 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
@@ -17,12 +17,14 @@
 import com.ruoyi.common.utils.WxCpUtils;
 import com.ruoyi.device.dto.DeviceMaintenancePlanDetailsDto;
 import com.ruoyi.device.dto.DeviceMaintenancePlanDto;
+import com.ruoyi.device.mapper.DeviceMaintenanceAttachmentMapper;
 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.DeviceMaintenanceAttachment;
 import com.ruoyi.device.pojo.DeviceMaintenancePlan;
 import com.ruoyi.device.pojo.DeviceMaintenancePlanDetails;
 import com.ruoyi.device.service.DeviceMaintenancePlanDetailsService;
@@ -34,12 +36,16 @@
 import com.ruoyi.system.service.InformationNotificationService;
 import org.apache.commons.collections4.CollectionUtils;
 import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
 
 import jakarta.annotation.Resource;
 import jakarta.servlet.http.HttpServletResponse;
+import java.io.File;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.URLEncoder;
@@ -48,7 +54,9 @@
 import java.time.YearMonth;
 import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
@@ -79,6 +87,14 @@
     private DeviceMapper deviceMapper;
     @Resource
     private DeviceMaintenanceMapper deviceMaintenanceMapper;
+    @Resource
+    private DeviceMaintenanceAttachmentMapper deviceMaintenanceAttachmentMapper;
+
+    @Value("${file.path}")
+    private String filePath;
+
+    @Value("${wordUrl}")
+    private String wordUrl;
 
     /**
      * 鍒嗛〉鏌ヨ璁惧淇濆吇璁″垝
@@ -141,13 +157,22 @@
         }
         this.saveOrUpdate(deviceMaintenancePlanDto);
 
-        // 鍒犻櫎鍘熸湰鐨勮鎯�
+        // 鍒犻櫎鍘熸湰鐨勮鎯咃紱鏄庣粏閲嶅缓鍚庝粛淇濈暀鍘熸槑缁唅d鐨勯檮浠朵笉娓呯悊锛宨d涓㈠け鐨勬墠娓呯悊
+        List<DeviceMaintenancePlanDetails> oldDetails = deviceMaintenancePlanDetailsService.list(
+                Wrappers.<DeviceMaintenancePlanDetails>lambdaQuery()
+                        .eq(DeviceMaintenancePlanDetails::getMaintenancePlanId, deviceMaintenancePlanDto.getMaintenancePlanId()));
         deviceMaintenancePlanDetailsService.remove(Wrappers.<DeviceMaintenancePlanDetails>lambdaQuery().eq(DeviceMaintenancePlanDetails::getMaintenancePlanId, deviceMaintenancePlanDto.getMaintenancePlanId()));
+        List<Integer> lostDetailIds = oldDetails.stream().map(DeviceMaintenancePlanDetails::getMaintenancePlanDetailId)
+                .collect(Collectors.toList());
         // 璇︽儏璧嬪�煎苟淇濆瓨
         List<DeviceMaintenancePlanDetailsDto> deviceMaintenancePlanDetails = deviceMaintenancePlanDto.getDeviceMaintenancePlanDetails();
         if (CollectionUtils.isNotEmpty(deviceMaintenancePlanDetails)) { // 璇︽儏涓嶄负绌�
+            List<Integer> keptDetailIds = deviceMaintenancePlanDetails.stream()
+                    .map(DeviceMaintenancePlanDetails::getMaintenancePlanDetailId)
+                    .filter(id -> id != null)
+                    .collect(Collectors.toList());
+            lostDetailIds.removeAll(keptDetailIds);
             List<DeviceMaintenancePlanDetails> collect = deviceMaintenancePlanDetails.stream().map(deviceMaintenancePlanDetail -> { // 閬嶅巻璇︽儏
-                deviceMaintenancePlanDetail.setDeviceId(deviceMaintenancePlanDto.getDeviceId()); // 璁惧ID
                 deviceMaintenancePlanDetail.setMaintenancePlanId(deviceMaintenancePlanDto.getMaintenancePlanId()); // 淇濆吇璁″垝ID
                 DeviceMaintenancePlanDetails planDetails = new DeviceMaintenancePlanDetails();
                 BeanUtils.copyProperties(deviceMaintenancePlanDetail, planDetails);
@@ -155,6 +180,7 @@
             }).collect(Collectors.toList());
             deviceMaintenancePlanDetailsService.saveBatch(collect);
         }
+        removeAttachmentsByDetailIds(lostDetailIds);
         return Result.success();
     }
 
@@ -165,8 +191,12 @@
      */
     @Override
     public Result deleteMaintenancePlan(DeviceMaintenancePlanDto deviceMaintenancePlanDto) {
+        List<DeviceMaintenancePlanDetails> details = deviceMaintenancePlanDetailsService.list(
+                Wrappers.<DeviceMaintenancePlanDetails>lambdaQuery()
+                        .eq(DeviceMaintenancePlanDetails::getMaintenancePlanId, deviceMaintenancePlanDto.getMaintenancePlanId()));
         this.removeById(deviceMaintenancePlanDto);
         deviceMaintenancePlanDetailsService.remove(Wrappers.<DeviceMaintenancePlanDetails>lambdaQuery().eq(DeviceMaintenancePlanDetails::getMaintenancePlanId, deviceMaintenancePlanDto.getMaintenancePlanId()));
+        removeAttachmentsByDetailIds(details.stream().map(DeviceMaintenancePlanDetails::getMaintenancePlanDetailId).collect(Collectors.toList()));
         return Result.success();
     }
 
@@ -181,27 +211,27 @@
         // 鏌ヨ璁惧淇濆吇璁″垝
         DeviceMaintenancePlanDto deviceMaintenancePlan = baseMapper.selectMaintenancePlanById(maintenancePlanId);
 
-        // 鏌ヨ璁惧淇濆吇璁″垝璇︽儏
-        List<DeviceMaintenancePlanDetailsDto> deviceMaintenancePlanDetailsDtoList = deviceMaintenancePlanDetailsMapper.deviceInspectionRecordDetailsList(maintenancePlanId);
+        // 鏌ヨ璁惧淇濆吇璁″垝璇︽儏锛屾寜鈥滆澶� + 淇濆吇鏈堜唤鈥濆厹搴曞幓閲�
+        List<DeviceMaintenancePlanDetailsDto> deviceMaintenancePlanDetailsDtoList = dedupeMaintenanceDetails(
+                deviceMaintenancePlanDetailsMapper.deviceInspectionRecordDetailsList(maintenancePlanId));
         // 璁剧疆搴忓彿
-        deviceMaintenancePlanDetailsDtoList.forEach(deviceInspectionRecordDetails -> {
-            deviceInspectionRecordDetails.setIndex(deviceMaintenancePlanDetailsDtoList.indexOf(deviceInspectionRecordDetails) + 1);
-        });
+        for (int i = 0; i < deviceMaintenancePlanDetailsDtoList.size(); i++) {
+            deviceMaintenancePlanDetailsDtoList.get(i).setIndex(i + 1);
+        }
 
         // 鑾峰彇璺緞
         InputStream inputStream = this.getClass().getResourceAsStream("/static/word/maintenance-plan.docx");
         Configure configure = Configure.builder()
                 .bind("deviceMaintenancePlanDetailsDtoList", new HackLoopTableRenderPolicy())
                 .build();
-        XWPFTemplate template = XWPFTemplate.compile(inputStream, configure).render(
-                new HashMap<String, Object>() {{
-                    put("deviceMaintenancePlan", deviceMaintenancePlan);
-                    put("deviceMaintenancePlanDetailsDtoList", deviceMaintenancePlanDetailsDtoList);
-                    // 缂栧埗浜虹鍚嶅湴鍧�
-                    put("compilerUrl", UserUtils.getFinalUserSignatureOrTransparent(deviceMaintenancePlan.getCompilerId()));
-                    // 瀹℃牳浜虹鍚嶅湴鍧�
-                    put("auditUrl", UserUtils.getFinalUserSignatureOrTransparent(deviceMaintenancePlan.getAuditId()));
-                }});
+        Map<String, Object> renderData = new HashMap<>();
+        renderData.put("deviceMaintenancePlan", deviceMaintenancePlan);
+        renderData.put("deviceMaintenancePlanDetailsDtoList", deviceMaintenancePlanDetailsDtoList);
+        // 缂栧埗浜虹鍚嶅湴鍧�
+        renderData.put("compilerUrl", UserUtils.getFinalUserSignatureOrTransparent(deviceMaintenancePlan.getCompilerId()));
+        // 瀹℃牳浜虹鍚嶅湴鍧�
+        renderData.put("auditUrl", UserUtils.getFinalUserSignatureOrTransparent(deviceMaintenancePlan.getAuditId()));
+        XWPFTemplate template = XWPFTemplate.compile(inputStream, configure).render(renderData);
 
         try {
             response.setContentType("application/msword");
@@ -233,7 +263,8 @@
         // 鏌ヨ璇︽儏
         DeviceMaintenancePlanDto deviceMaintenancePlanDto = new DeviceMaintenancePlanDto();
         BeanUtils.copyProperties(deviceMaintenancePlan, deviceMaintenancePlanDto);
-        deviceMaintenancePlanDto.setDeviceMaintenancePlanDetails(deviceMaintenancePlanDetailsMapper.deviceInspectionRecordDetailsList(maintenancePlanId));
+        deviceMaintenancePlanDto.setDeviceMaintenancePlanDetails(dedupeMaintenanceDetails(
+                deviceMaintenancePlanDetailsMapper.deviceInspectionRecordDetailsList(maintenancePlanId)));
         return Result.success(deviceMaintenancePlanDto);
     }
 
@@ -659,9 +690,94 @@
             List<Integer> recordIds = records.stream().map(DeviceMaintenance::getId).collect(Collectors.toList());
             deviceMaintenanceMapper.deleteBatchIds(recordIds);
         }
+        removeAttachmentsByDetailIds(List.of(maintenancePlanDetailId));
         return deviceMaintenancePlanDetailsMapper.deleteById(maintenancePlanDetailId) > 0;
     }
 
+    @Override
+    public List<DeviceMaintenanceAttachment> selectAttachments(Integer maintenancePlanDetailId) {
+        if (maintenancePlanDetailId == null) {
+            return new ArrayList<>();
+        }
+        return deviceMaintenanceAttachmentMapper.selectList(Wrappers.<DeviceMaintenanceAttachment>lambdaQuery()
+                .eq(DeviceMaintenanceAttachment::getMaintenancePlanDetailId, maintenancePlanDetailId)
+                .orderByAsc(DeviceMaintenanceAttachment::getSort, DeviceMaintenanceAttachment::getId));
+    }
+
+    @Override
+    public DeviceMaintenanceAttachment uploadAttachment(Integer maintenancePlanDetailId, MultipartFile file) {
+        if (maintenancePlanDetailId == null) {
+            throw new ErrorException("缂哄皯淇濆吇璁″垝鏄庣粏id");
+        }
+        if (deviceMaintenancePlanDetailsMapper.selectById(maintenancePlanDetailId) == null) {
+            throw new ErrorException("淇濆吇璁″垝鏄庣粏涓嶅瓨鍦�");
+        }
+        if (file == null || file.isEmpty()) {
+            throw new ErrorException("闄勪欢涓嶈兘涓虹┖");
+        }
+        // 绯荤粺鍏ㄥ眬涓婁紶涓婇檺涓�200MB锛岃繖閲屾彁鍓嶇粰鍑哄弸濂芥彁绀�
+        if (file.getSize() > 200L * 1024 * 1024) {
+            throw new ErrorException("闄勪欢涓嶈兘瓒呰繃200MB");
+        }
+        String originalName = file.getOriginalFilename();
+        if (originalName == null || originalName.trim().isEmpty()) {
+            originalName = "鏈懡鍚嶆枃浠�";
+        }
+        // 涓埆娴忚鍣ㄥ彲鑳藉甫涓婅矾寰勫墠缂�锛屽彧淇濈暀鏂囦欢鍚嶆湰韬�
+        originalName = originalName.replaceAll(".*[/\\\\]", "");
+        try {
+            // 鍥剧墖鏀惧浘鐗囩洰褰曪紙/img 闈欐�佽矾寰勶級锛屽叾浣欎换鎰忔牸寮忔斁 word 鐩綍锛�/word 闈欐�佽矾寰勶級
+            boolean image = originalName.matches("(?i).+\\.(jpg|jpeg|png|gif)$");
+            File directory = new File(image ? filePath : wordUrl);
+            if (!directory.exists()) {
+                directory.mkdirs();
+            }
+            String fileUrl = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyMMddHHmmssSSS")) + "-" + originalName;
+            file.transferTo(new File(directory, fileUrl));
+            DeviceMaintenanceAttachment attachment = new DeviceMaintenanceAttachment();
+            attachment.setMaintenancePlanDetailId(maintenancePlanDetailId);
+            attachment.setFileName(originalName);
+            attachment.setFileUrl(fileUrl);
+            attachment.setSort(selectAttachments(maintenancePlanDetailId).size());
+            deviceMaintenanceAttachmentMapper.insert(attachment);
+            return attachment;
+        } catch (IOException e) {
+            throw new ErrorException("闄勪欢涓婁紶澶辫触");
+        }
+    }
+
+    @Override
+    public boolean deleteAttachment(Long id) {
+        return deviceMaintenanceAttachmentMapper.deleteById(id) > 0;
+    }
+
+    /**
+     * 鍒犻櫎璁″垝鏄庣粏瀵瑰簲鐨勯檮浠惰褰�
+     */
+    private void removeAttachmentsByDetailIds(List<Integer> maintenancePlanDetailIds) {
+        if (CollectionUtils.isEmpty(maintenancePlanDetailIds)) {
+            return;
+        }
+        deviceMaintenanceAttachmentMapper.delete(Wrappers.<DeviceMaintenanceAttachment>lambdaQuery()
+                .in(DeviceMaintenanceAttachment::getMaintenancePlanDetailId, maintenancePlanDetailIds));
+    }
+
+    /**
+     * 鍚屼竴璁″垝銆佸悓涓�璁惧鍙繚鐣欎竴鏉★紱SQL 宸叉寜鈥滃凡鏈変繚鍏昏褰曚紭鍏堛�佸鏍搁�氳繃浼樺厛銆佹渶鏂颁紭鍏堚�濇帓搴忥紝杩欓噷鍙栭鏉�
+     */
+    private List<DeviceMaintenancePlanDetailsDto> dedupeMaintenanceDetails(List<DeviceMaintenancePlanDetailsDto> details) {
+        if (CollectionUtils.isEmpty(details)) {
+            return details;
+        }
+        Map<String, DeviceMaintenancePlanDetailsDto> unique = new LinkedHashMap<>();
+        for (DeviceMaintenancePlanDetailsDto detail : details) {
+            String key = detail.getDeviceId() != null ? String.valueOf(detail.getDeviceId())
+                    : "name:" + detail.getDeviceName();
+            unique.putIfAbsent(key, detail);
+        }
+        return new ArrayList<>(unique.values());
+    }
+
     /**
      * 涓や釜淇濆吇鏃ユ湡涔嬮棿鐩稿樊鐨勬湀鏁�
      */

--
Gitblit v1.9.3