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-manage/src/main/java/com/ruoyi/manage/service/ManageDocumentControlledService.java                     |    4 
 cnas-manage/src/main/java/com/ruoyi/manage/excel/ManageDocumentControlledImportDto.java                     |   36 +
 cnas-manage/src/main/java/com/ruoyi/manage/service/impl/ManageDocumentCancelServiceImpl.java                |   38 +
 sql/fix_device_record_backfill.sql                                                                          |  104 +++
 cnas-personnel/src/main/java/com/ruoyi/personnel/pojo/PersonJobResponsibilities.java                        |    5 
 sql/fix_inspection_order_detail_backfill.sql                                                                |   78 ++
 cnas-personnel/src/main/resources/mapper/PersonTrainingRecordMapper.xml                                     |   12 
 cnas-device/src/main/java/com/ruoyi/device/mapper/DeviceRecordMapper.java                                   |   18 
 cnas-device/src/main/java/com/ruoyi/device/service/DeviceMaintenancePlanService.java                        |   17 
 cnas-device/src/main/java/com/ruoyi/device/service/DeviceRecordService.java                                 |   11 
 cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonPersonnelCapacityServiceImpl.java       |   63 +
 cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonTrainingRecordServiceImpl.java          |   97 +-
 cnas-personnel/src/main/resources/mapper/PersonPersonnelCapacityMapper.xml                                  |    1 
 inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderPlanServiceImpl.java                    |   62 +
 cnas-device/src/main/java/com/ruoyi/device/controller/DeviceMaintenancePlanController.java                  |   33 
 cnas-device/src/main/java/com/ruoyi/device/mapper/DeviceMaintenanceAttachmentMapper.java                    |    7 
 cnas-device/src/main/java/com/ruoyi/device/pojo/DeviceMaintenanceAttachment.java                            |   28 
 inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsProductServiceImpl.java                      |   39 +
 cnas-personnel/src/main/java/com/ruoyi/personnel/pojo/PersonTraining.java                                   |    6 
 cnas-personnel/src/main/java/com/ruoyi/personnel/controller/PersonPostAuthorizationRecordController.java    |   16 
 cnas-personnel/src/main/java/com/ruoyi/personnel/pojo/PersonBasicInfo.java                                  |    3 
 cnas-manage/src/main/java/com/ruoyi/manage/mapper/ManageDocumentControlledMapper.java                       |   10 
 cnas-device/src/main/java/com/ruoyi/device/controller/DeviceRecordController.java                           |   12 
 cnas-manage/src/main/java/com/ruoyi/manage/pojo/ManageDocumentControlled.java                               |   10 
 cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonTrainingServiceImpl.java                |  207 ++++-
 cnas-manage/src/main/java/com/ruoyi/manage/controller/ManageDocumentControlledController.java               |   21 
 cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonJobResponsibilitiesServiceImpl.java     |   21 
 cnas-device/src/main/java/com/ruoyi/device/dto/DeviceMaintenancePlanDetailsDto.java                         |    3 
 cnas-manage/src/main/java/com/ruoyi/manage/service/impl/ManageDocumentControlledServiceImpl.java            |   66 +
 cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceMaintenancePlanServiceImpl.java               |  150 ++++
 cnas-personnel/src/main/java/com/ruoyi/personnel/pojo/PersonPostAuthorizationRecord.java                    |    3 
 cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceRecordServiceImpl.java                        |  151 ++--
 sql/lims_delta.sql                                                                                          |   19 
 cnas-manage/src/main/java/com/ruoyi/manage/service/impl/ManageDocumentIssueRecycleServiceImpl.java          |   37 
 cnas-device/src/main/resources/mapper/DeviceMaintenancePlanDetailsMapper.xml                                |   13 
 sql/fix_inspection_order_orphans.sql                                                                        |   53 +
 cnas-personnel/src/main/java/com/ruoyi/personnel/dto/UserPageDto.java                                       |    2 
 cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonBasicInfoServiceImpl.java               |   62 +
 cnas-device/src/main/resources/static/word/maintenance-plan.docx                                            |    0 
 cnas-device/src/main/resources/mapper/InsOrderDeviceRecordMapper.xml                                        |   19 
 cnas-personnel/src/main/java/com/ruoyi/personnel/controller/PersonBasicInfoController.java                  |   17 
 cnas-personnel/src/main/resources/static/credentials-deal.docx                                              |    0 
 cnas-personnel/src/main/java/com/ruoyi/personnel/pojo/PersonPersonnelCapacity.java                          |    4 
 cnas-personnel/src/main/java/com/ruoyi/personnel/controller/PersonPersonnelCapacityController.java          |   43 +
 cnas-manage/src/main/resources/mapper/ManageDocumentControlledMapper.xml                                    |   12 
 cnas-manage/src/main/java/com/ruoyi/manage/excel/ManageDocumentControlledImportListener.java                |   96 ++
 cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonPostAuthorizationRecordServiceImpl.java |   90 +-
 sql/fix_device_maintenance_plan_duplicates.sql                                                              |   72 ++
 cnas-personnel/src/main/resources/mapper/PersonBasicInfoMapper.xml                                          |   52 +
 cnas-manage/src/main/java/com/ruoyi/manage/service/impl/ManageDocumentAlterServiceImpl.java                 |   51 +
 50 files changed, 1,642 insertions(+), 332 deletions(-)

diff --git a/cnas-device/src/main/java/com/ruoyi/device/controller/DeviceMaintenancePlanController.java b/cnas-device/src/main/java/com/ruoyi/device/controller/DeviceMaintenancePlanController.java
index bc27f33..bed07d2 100644
--- a/cnas-device/src/main/java/com/ruoyi/device/controller/DeviceMaintenancePlanController.java
+++ b/cnas-device/src/main/java/com/ruoyi/device/controller/DeviceMaintenancePlanController.java
@@ -5,6 +5,7 @@
 import com.ruoyi.common.core.domain.Result;
 import com.ruoyi.device.dto.DeviceMaintenancePlanDetailsDto;
 import com.ruoyi.device.dto.DeviceMaintenancePlanDto;
+import com.ruoyi.device.pojo.DeviceMaintenanceAttachment;
 import com.ruoyi.device.pojo.DeviceMaintenancePlan;
 import com.ruoyi.device.pojo.DeviceMaintenancePlanDetails;
 import com.ruoyi.device.service.DeviceMaintenancePlanService;
@@ -12,6 +13,7 @@
 import io.swagger.v3.oas.annotations.Operation;
 import lombok.SneakyThrows;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
 
 import jakarta.annotation.Resource;
 import jakarta.servlet.http.HttpServletResponse;
@@ -132,7 +134,8 @@
      */
     @Operation(summary = "瀵煎嚭璁惧淇濆吇璁″垝")
     @GetMapping("/exportDeviceMaintenancePlan")
-    public Result exportDeviceMaintenancePlan(@RequestParam("maintenancePlanId") Integer maintenancePlanId, HttpServletResponse response) {
+    public Result exportDeviceMaintenancePlan(@RequestParam("maintenancePlanId") Integer maintenancePlanId,
+                                              HttpServletResponse response) {
         return deviceMaintenancePlanService.exportDeviceMaintenancePlanDto(maintenancePlanId, response);
     }
 
@@ -153,4 +156,32 @@
     public Result reviewMaintenancePlanStatus(@RequestBody DeviceMaintenancePlanDto deviceMaintenancePlanDto) {
         return deviceMaintenancePlanService.reviewMaintenancePlanStatus(deviceMaintenancePlanDto);
     }
+
+    /**
+     * 鏌ヨ淇濆吇璁″垝鏄庣粏闄勪欢
+     */
+    @Operation(summary = "鏌ヨ淇濆吇璁″垝鏄庣粏闄勪欢")
+    @GetMapping("/selectAttachments")
+    public Result<List<DeviceMaintenanceAttachment>> selectAttachments(Integer maintenancePlanDetailId) {
+        return Result.success(deviceMaintenancePlanService.selectAttachments(maintenancePlanDetailId));
+    }
+
+    /**
+     * 涓婁紶淇濆吇璁″垝鏄庣粏闄勪欢
+     */
+    @Operation(summary = "涓婁紶淇濆吇璁″垝鏄庣粏闄勪欢")
+    @PostMapping("/uploadAttachment")
+    public Result uploadAttachment(@RequestParam("maintenancePlanDetailId") Integer maintenancePlanDetailId,
+                                   @RequestParam("file") MultipartFile file) {
+        return Result.success(deviceMaintenancePlanService.uploadAttachment(maintenancePlanDetailId, file));
+    }
+
+    /**
+     * 鍒犻櫎淇濆吇璁″垝鏄庣粏闄勪欢
+     */
+    @Operation(summary = "鍒犻櫎淇濆吇璁″垝鏄庣粏闄勪欢")
+    @DeleteMapping("/deleteAttachment")
+    public Result deleteAttachment(Long id) {
+        return Result.success(deviceMaintenancePlanService.deleteAttachment(id));
+    }
 }
diff --git a/cnas-device/src/main/java/com/ruoyi/device/controller/DeviceRecordController.java b/cnas-device/src/main/java/com/ruoyi/device/controller/DeviceRecordController.java
index 4a844be..2fe8b6b 100644
--- a/cnas-device/src/main/java/com/ruoyi/device/controller/DeviceRecordController.java
+++ b/cnas-device/src/main/java/com/ruoyi/device/controller/DeviceRecordController.java
@@ -29,8 +29,10 @@
 
     @Operation(summary = "澶囦娇鐢ㄨ褰曟煡璇�")
     @GetMapping("/deviceRecordPage")
-    public Result deviceRecordPage(Integer deviceId, Page page, String sampleCode, String managementNumber) {
-        return Result.success(deviceRecordService.deviceRecordPage(deviceId, page, sampleCode, managementNumber));
+    public Result deviceRecordPage(Integer deviceId, Page page, String deviceName,
+                                   String specificationModel, String sampleCode, String managementNumber) {
+        return Result.success(deviceRecordService.deviceRecordPage(
+                deviceId, page, deviceName, specificationModel, sampleCode, managementNumber));
     }
 
     @Operation(summary = "鏂板")
@@ -57,7 +59,9 @@
 
     @Operation(summary = "璁惧浣跨敤璁板綍瀵煎嚭")
     @GetMapping("/exportUseRecord")
-    public void exportUseRecord(Integer deviceId, String sampleCode, String managementNumber, String exportDate, HttpServletResponse response){
-        deviceRecordService.exportUseRecord(deviceId, sampleCode, managementNumber, exportDate, response);
+    public void exportUseRecord(Integer deviceId, String deviceName, String specificationModel,
+                                String managementNumber, String sampleCode, HttpServletResponse response) {
+        deviceRecordService.exportUseRecord(
+                deviceId, deviceName, specificationModel, managementNumber, sampleCode, response);
     }
 }
diff --git a/cnas-device/src/main/java/com/ruoyi/device/dto/DeviceMaintenancePlanDetailsDto.java b/cnas-device/src/main/java/com/ruoyi/device/dto/DeviceMaintenancePlanDetailsDto.java
index bab14d8..72c6181 100644
--- a/cnas-device/src/main/java/com/ruoyi/device/dto/DeviceMaintenancePlanDetailsDto.java
+++ b/cnas-device/src/main/java/com/ruoyi/device/dto/DeviceMaintenancePlanDetailsDto.java
@@ -98,4 +98,7 @@
 
     @Schema(description = "瀹℃牳鏃堕棿")
     private LocalDateTime reviewTime;
+
+    @Schema(description = "闄勪欢鏁伴噺")
+    private Integer attachmentCount;
 }
diff --git a/cnas-device/src/main/java/com/ruoyi/device/mapper/DeviceMaintenanceAttachmentMapper.java b/cnas-device/src/main/java/com/ruoyi/device/mapper/DeviceMaintenanceAttachmentMapper.java
new file mode 100644
index 0000000..bd59a94
--- /dev/null
+++ b/cnas-device/src/main/java/com/ruoyi/device/mapper/DeviceMaintenanceAttachmentMapper.java
@@ -0,0 +1,7 @@
+package com.ruoyi.device.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.device.pojo.DeviceMaintenanceAttachment;
+
+public interface DeviceMaintenanceAttachmentMapper extends BaseMapper<DeviceMaintenanceAttachment> {
+}
diff --git a/cnas-device/src/main/java/com/ruoyi/device/mapper/DeviceRecordMapper.java b/cnas-device/src/main/java/com/ruoyi/device/mapper/DeviceRecordMapper.java
index 214de38..f56de36 100644
--- a/cnas-device/src/main/java/com/ruoyi/device/mapper/DeviceRecordMapper.java
+++ b/cnas-device/src/main/java/com/ruoyi/device/mapper/DeviceRecordMapper.java
@@ -19,7 +19,12 @@
  */
 public interface DeviceRecordMapper extends BaseMapper<DeviceRecord> {
 
-    IPage<DeviceRecordDto> deviceRecordPage(@Param("deviceId") Integer deviceId, @Param("page") Page page, @Param("sampleCode") String sampleCode, @Param("managementNumber") String managementNumber, @Param("userId") Integer userId);
+    IPage<DeviceRecordDto> deviceRecordPage(@Param("deviceId") Integer deviceId, @Param("page") Page page,
+                                            @Param("deviceName") String deviceName,
+                                            @Param("specificationModel") String specificationModel,
+                                            @Param("sampleCode") String sampleCode,
+                                            @Param("managementNumber") String managementNumber,
+                                            @Param("userId") Integer userId);
 
 
     /**
@@ -31,10 +36,15 @@
     /**
      * 鏌ヨ瀵煎嚭璁惧浣跨敤璁板綍
      * @param deviceId
+     * @param deviceName 璁惧鍚嶇О绛涢��
+     * @param specificationModel 璁惧鍨嬪彿绛涢��
+     * @param managementNumber 璁惧缂栧彿绛涢��
      * @param sampleCode 鏍峰搧缂栧彿绛涢��
-     * @param managementNumber 绠$悊缂栧彿绛涢��
-     * @param exportDate
      * @return
      */
-    List<DeviceRecordDto> selectExportList(@Param("deviceId") Integer deviceId, @Param("sampleCode") String sampleCode, @Param("managementNumber") String managementNumber, @Param("exportDate") String exportDate);
+    List<DeviceRecordDto> selectExportList(@Param("deviceId") Integer deviceId,
+                                           @Param("deviceName") String deviceName,
+                                           @Param("specificationModel") String specificationModel,
+                                           @Param("managementNumber") String managementNumber,
+                                           @Param("sampleCode") String sampleCode);
 }
diff --git a/cnas-device/src/main/java/com/ruoyi/device/pojo/DeviceMaintenanceAttachment.java b/cnas-device/src/main/java/com/ruoyi/device/pojo/DeviceMaintenanceAttachment.java
new file mode 100644
index 0000000..fe609e7
--- /dev/null
+++ b/cnas-device/src/main/java/com/ruoyi/device/pojo/DeviceMaintenanceAttachment.java
@@ -0,0 +1,28 @@
+package com.ruoyi.device.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+@TableName("device_maintenance_attachment")
+public class DeviceMaintenanceAttachment {
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    private Integer maintenancePlanDetailId;
+    private String fileName;
+    private String fileUrl;
+    private Integer sort;
+    @TableField(fill = FieldFill.INSERT)
+    private Integer createUser;
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Integer updateUser;
+    @TableField(fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+}
diff --git a/cnas-device/src/main/java/com/ruoyi/device/service/DeviceMaintenancePlanService.java b/cnas-device/src/main/java/com/ruoyi/device/service/DeviceMaintenancePlanService.java
index f227086..f714788 100644
--- a/cnas-device/src/main/java/com/ruoyi/device/service/DeviceMaintenancePlanService.java
+++ b/cnas-device/src/main/java/com/ruoyi/device/service/DeviceMaintenancePlanService.java
@@ -7,8 +7,10 @@
 import com.ruoyi.device.dto.DeviceMaintenancePlanDetailsDto;
 import com.ruoyi.device.dto.DeviceMaintenancePlanDto;
 import com.ruoyi.device.pojo.Device;
+import com.ruoyi.device.pojo.DeviceMaintenanceAttachment;
 import com.ruoyi.device.pojo.DeviceMaintenancePlan;
 import com.ruoyi.device.pojo.DeviceMaintenancePlanDetails;
+import org.springframework.web.multipart.MultipartFile;
 
 import jakarta.servlet.http.HttpServletResponse;
 import java.time.LocalDate;
@@ -136,4 +138,19 @@
      * 鍒犻櫎璁惧淇濆吇璁″垝璇︽儏锛涘凡鍑哄鏍哥粨鏋滅殑璁板綍闅忓巻鍙茬暀妗o紝鏈鏍哥殑璁板綍闅忔槑缁嗕竴璧峰垹闄�
      */
     boolean removeMaintenancePlanDetail(Integer maintenancePlanDetailId);
+
+    /**
+     * 鏌ヨ璁″垝鏄庣粏闄勪欢鍒楄〃
+     */
+    List<DeviceMaintenanceAttachment> selectAttachments(Integer maintenancePlanDetailId);
+
+    /**
+     * 涓婁紶璁″垝鏄庣粏闄勪欢
+     */
+    DeviceMaintenanceAttachment uploadAttachment(Integer maintenancePlanDetailId, MultipartFile file);
+
+    /**
+     * 鍒犻櫎璁″垝鏄庣粏闄勪欢
+     */
+    boolean deleteAttachment(Long id);
 }
diff --git a/cnas-device/src/main/java/com/ruoyi/device/service/DeviceRecordService.java b/cnas-device/src/main/java/com/ruoyi/device/service/DeviceRecordService.java
index c0560c6..9a18eba 100644
--- a/cnas-device/src/main/java/com/ruoyi/device/service/DeviceRecordService.java
+++ b/cnas-device/src/main/java/com/ruoyi/device/service/DeviceRecordService.java
@@ -18,17 +18,20 @@
  */
 public interface DeviceRecordService extends IService<DeviceRecord> {
 
-    IPage<DeviceRecordDto> deviceRecordPage(Integer deviceId, Page page, String sampleCode, String managementNumber);
+    IPage<DeviceRecordDto> deviceRecordPage(Integer deviceId, Page page, String deviceName,
+                                            String specificationModel, String sampleCode, String managementNumber);
 
 
     /**
      * 瀵煎嚭璁惧浣跨敤璁板綍
      *
      * @param deviceId
+     * @param deviceName 璁惧鍚嶇О绛涢��
+     * @param specificationModel 璁惧鍨嬪彿绛涢��
+     * @param managementNumber 璁惧缂栧彿绛涢��
      * @param sampleCode 鏍峰搧缂栧彿绛涢��
-     * @param managementNumber 绠$悊缂栧彿绛涢��
-     * @param exportDate 瀵煎嚭鏈堜唤
      * @param response
      */
-    void exportUseRecord(Integer deviceId, String sampleCode, String managementNumber, String exportDate, HttpServletResponse response);
+    void exportUseRecord(Integer deviceId, String deviceName, String specificationModel,
+                         String managementNumber, String sampleCode, HttpServletResponse response);
 }
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());
+    }
+
     /**
      * 涓や釜淇濆吇鏃ユ湡涔嬮棿鐩稿樊鐨勬湀鏁�
      */
diff --git a/cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceRecordServiceImpl.java b/cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceRecordServiceImpl.java
index d4fc412..a554c43 100644
--- a/cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceRecordServiceImpl.java
+++ b/cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceRecordServiceImpl.java
@@ -11,6 +11,7 @@
 import com.ruoyi.device.service.DeviceRecordService;
 import com.ruoyi.system.domain.vo.SysRoleVo;
 import com.ruoyi.system.mapper.SysRoleMenuMapper;
+import org.apache.poi.xwpf.usermodel.BreakType;
 import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
 import org.apache.poi.xwpf.usermodel.TableRowAlign;
 import org.apache.poi.xwpf.usermodel.UnderlinePatterns;
@@ -71,11 +72,9 @@
     private static final String RECORD_NUMBER = "LMSY-CX07-JL03";
     private static final String FONT_NAME = "瀹嬩綋";
     private static final int[] COLUMN_WIDTHS = {1622, 4168, 1288, 1107, 1137, 1076, 940, 1182, 1288, 1350};
-    private static final String[] HEADER_FIRST_ROW = {"浣跨敤鏃ユ湡", "鏍峰搧缂栧彿", "鏃堕棿 24 灏忔椂鍒�", "", "璁惧鐘跺喌", "", "鐜鏉′欢", "", "浣跨敤浜�", "澶囨敞"};
+    private static final String[] HEADER_FIRST_ROW = {"浣跨敤鏃ユ湡", "鏍峰搧鍚嶇О", "鏃堕棿 24 灏忔椂鍒�", "", "璁惧鐘跺喌", "", "鐜鏉′欢", "", "浣跨敤浜�", "澶囨敞"};
     private static final String[] HEADER_SECOND_ROW = {"", "", "寮�濮�", "缁撴潫", "浣跨敤鍓�", "浣跨敤鍚�", "鈩�", "%RH", "", ""};
-    private static final int[] COMBINED_COLUMN_WIDTHS = {1300, 2000, 1500, 1600, 2000, 900, 900, 800, 800, 650, 750, 950, 1008};
-    private static final String[] COMBINED_HEADER_FIRST_ROW = {"浣跨敤鏃ユ湡", "璁惧鍚嶇О", "璁惧鍨嬪彿", "璁惧缂栧彿", "鏍峰搧缂栧彿", "鏃堕棿 24 灏忔椂鍒�", "", "璁惧鐘跺喌", "", "鐜鏉′欢", "", "浣跨敤浜�", "澶囨敞"};
-    private static final String[] COMBINED_HEADER_SECOND_ROW = {"", "", "", "", "", "寮�濮�", "缁撴潫", "浣跨敤鍓�", "浣跨敤鍚�", "鈩�", "%RH", "", ""};
+    private static final String FOOTNOTE = "娉細璁惧浣跨敤鍓嶅悗涓�鏍忥紝姝e父鍒掆�溾垰鈥濓紱涓嶆甯稿垝鈥溍椻�濆苟鍐欐槑鎯呭喌銆�";
     private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
     private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm");
 
@@ -83,7 +82,9 @@
     private SysRoleMenuMapper sysRoleMenuMapper;
 
     @Override
-    public IPage<DeviceRecordDto> deviceRecordPage(Integer deviceId, Page page, String sampleCode, String managementNumber) {
+    public IPage<DeviceRecordDto> deviceRecordPage(Integer deviceId, Page page, String deviceName,
+                                                   String specificationModel, String sampleCode,
+                                                   String managementNumber) {
         // todo:浠呯湅鑷繁
         Long userId = SecurityUtils.getUserId();
         // 鑾峰彇鏉冮檺鍒ゆ柇鏄惁鏄粎鐪嬫垜
@@ -95,19 +96,20 @@
             testUserId = SecurityUtils.getUserId().intValue();
         }
 
-        return baseMapper.deviceRecordPage(deviceId, page, sampleCode, managementNumber, testUserId);
+        return baseMapper.deviceRecordPage(deviceId, page, deviceName, specificationModel,
+                sampleCode, managementNumber, testUserId);
     }
 
     @Override
-    public void exportUseRecord(Integer deviceId, String sampleCode, String managementNumber, String exportDate, HttpServletResponse response) {
-        List<DeviceRecordDto> records = baseMapper.selectExportList(deviceId, sampleCode, managementNumber, exportDate);
+    public void exportUseRecord(Integer deviceId, String deviceName, String specificationModel,
+                                String managementNumber, String sampleCode, HttpServletResponse response) {
+        List<DeviceRecordDto> records = baseMapper.selectExportList(deviceId, deviceName,
+                specificationModel, managementNumber, sampleCode);
         if (records == null || records.isEmpty()) {
-            throw new ServiceException(exportDate == null || exportDate.isEmpty()
-                    ? "娌℃湁鍙鍑虹殑浣跨敤璁板綍锛屾棤娉曞鍑�"
-                    : exportDate + " 娌℃湁鍙鍑虹殑浣跨敤璁板綍锛屾棤娉曞鍑�");
+            throw new ServiceException("娌℃湁鍙鍑虹殑浣跨敤璁板綍锛屾棤娉曞鍑�");
         }
 
-        // 鍗曞彴璁惧娌跨敤鎴浘琛ㄥご; 澶氬彴璁惧鍚堝苟鍒颁竴寮犺〃, 姣忚甯﹁澶囦俊鎭�
+        // 涓�鍙拌澶囦竴鏈繍琛岃褰曪紝澶氬彴璁惧鍦ㄥ悓涓� Word 鍐呮寜璁惧鍒嗛〉
         Map<Integer, List<DeviceRecordDto>> deviceRecords = new LinkedHashMap<>();
         for (DeviceRecordDto record : records) {
             deviceRecords.computeIfAbsent(record.getDeviceId(), key -> new ArrayList<>()).add(record);
@@ -115,11 +117,13 @@
 
         try (XWPFDocument document = new XWPFDocument()) {
             setupPage(document);
-            if (deviceRecords.size() == 1) {
-                List<DeviceRecordDto> group = deviceRecords.values().iterator().next();
-                renderForm(document, group.get(0), group);
-            } else {
-                renderCombinedForm(document, records);
+            boolean first = true;
+            for (List<DeviceRecordDto> group : deviceRecords.values()) {
+                if (!first) {
+                    addPageBreak(document);
+                }
+                renderForm(document, group.get(0), mergeSameConditionRecords(group));
+                first = false;
             }
 
             response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
@@ -132,6 +136,49 @@
         } catch (IOException e) {
             throw new RuntimeException("瀵煎嚭澶辫触", e);
         }
+    }
+
+    /**
+     * 鍚屼竴璁惧銆佸悓涓�澶┿�佽捣姝㈡椂闂�/娓╂箍搴�/浣跨敤浜�/浣跨敤鍓嶅悗鐘跺喌鐩稿悓鐨勮褰曞悎骞朵竴琛岋紝鏍峰搧缂栧彿鐢ㄢ�溿�佲�濇嫾鎺ャ��
+     */
+    private List<DeviceRecordDto> mergeSameConditionRecords(List<DeviceRecordDto> records) {
+        Map<String, DeviceRecordDto> merged = new LinkedHashMap<>();
+        for (DeviceRecordDto record : records) {
+            String key = String.join("|",
+                    formatDate(record.getUseStartDate()),
+                    formatTime(record.getUseStartDate()),
+                    formatTime(record.getUseEndDate()),
+                    valueOrEmpty(record.getTemperature()),
+                    valueOrEmpty(record.getHumidity()),
+                    valueOrEmpty(record.getUsePerson()),
+                    String.valueOf(record.getUseBefore()),
+                    String.valueOf(record.getUseAfter()));
+            DeviceRecordDto existing = merged.get(key);
+            if (existing == null) {
+                merged.put(key, record);
+            } else {
+                existing.setSampleCode(appendDistinct(existing.getSampleCode(), record.getSampleCode()));
+                existing.setRemark(appendDistinct(existing.getRemark(), record.getRemark()));
+            }
+        }
+        return new ArrayList<>(merged.values());
+    }
+
+    private String appendDistinct(String existing, String addition) {
+        if (addition == null || addition.trim().isEmpty()) {
+            return existing;
+        }
+        if (existing == null || existing.trim().isEmpty()) {
+            return addition;
+        }
+        List<String> parts = java.util.Arrays.asList(existing.split("銆�"));
+        return parts.contains(addition) ? existing : existing + "銆�" + addition;
+    }
+
+    private void addPageBreak(XWPFDocument document) {
+        XWPFParagraph paragraph = document.createParagraph();
+        paragraph.setSpacingAfter(0);
+        paragraph.createRun().addBreak(BreakType.PAGE);
     }
 
     private void setupPage(XWPFDocument document) {
@@ -198,54 +245,10 @@
                 setCellText(row.getCell(j), values[j]);
             }
         }
-    }
 
-    private void renderCombinedForm(XWPFDocument document, List<DeviceRecordDto> records) {
-        XWPFParagraph recordNumber = addParagraph(document, "璁板綍缂栧彿锛�" + RECORD_NUMBER, 10.5, false, ParagraphAlignment.LEFT);
-        recordNumber.setSpacingAfter(240);
-
-        XWPFParagraph title = addParagraph(document, FORM_TITLE, 22, true, ParagraphAlignment.CENTER);
-        title.setSpacingAfter(240);
-
-        XWPFTable table = document.createTable(2 + records.size(), COMBINED_COLUMN_WIDTHS.length);
-        configureTable(table, COMBINED_COLUMN_WIDTHS);
-
-        for (int i = 0; i < table.getNumberOfRows(); i++) {
-            XWPFTableRow row = table.getRow(i);
-            row.setHeight(i < 2 ? 560 : 520);
-            for (int j = 0; j < COMBINED_COLUMN_WIDTHS.length; j++) {
-                setCellWidth(row.getCell(j), COMBINED_COLUMN_WIDTHS[j]);
-            }
-            setRowProperties(row, i < 2);
-        }
-
-        XWPFTableRow firstHeader = table.getRow(0);
-        for (int i = 0; i < COMBINED_HEADER_FIRST_ROW.length; i++) {
-            setCellText(firstHeader.getCell(i), COMBINED_HEADER_FIRST_ROW[i]);
-        }
-        XWPFTableRow secondHeader = table.getRow(1);
-        for (int i = 0; i < COMBINED_HEADER_SECOND_ROW.length; i++) {
-            setCellText(secondHeader.getCell(i), COMBINED_HEADER_SECOND_ROW[i]);
-        }
-
-        mergeHorizontal(firstHeader, COMBINED_COLUMN_WIDTHS, 9, 2);
-        mergeHorizontal(firstHeader, COMBINED_COLUMN_WIDTHS, 7, 2);
-        mergeHorizontal(firstHeader, COMBINED_COLUMN_WIDTHS, 5, 2);
-        mergeVertical(firstHeader.getCell(0), secondHeader.getCell(0));
-        mergeVertical(firstHeader.getCell(1), secondHeader.getCell(1));
-        mergeVertical(firstHeader.getCell(2), secondHeader.getCell(2));
-        mergeVertical(firstHeader.getCell(3), secondHeader.getCell(3));
-        mergeVertical(firstHeader.getCell(4), secondHeader.getCell(4));
-        mergeVertical(firstHeader.getCell(8), secondHeader.getCell(11));
-        mergeVertical(firstHeader.getCell(9), secondHeader.getCell(12));
-
-        for (int i = 0; i < records.size(); i++) {
-            XWPFTableRow row = table.getRow(2 + i);
-            String[] values = buildCombinedRowValues(records.get(i));
-            for (int j = 0; j < values.length; j++) {
-                setCellText(row.getCell(j), values[j]);
-            }
-        }
+        XWPFParagraph footnote = addParagraph(document, FOOTNOTE, 10.5, false, ParagraphAlignment.LEFT);
+        footnote.setSpacingBefore(120);
+        footnote.setSpacingAfter(0);
     }
 
     private XWPFParagraph addParagraph(XWPFDocument document, String text, double fontSize, boolean bold, ParagraphAlignment alignment) {
@@ -461,24 +464,6 @@
         };
     }
 
-    private String[] buildCombinedRowValues(DeviceRecordDto record) {
-        return new String[]{
-                formatDate(record.getUseStartDate()),
-                valueOrEmpty(record.getDeviceName()),
-                valueOrEmpty(record.getSpecificationModel()),
-                valueOrEmpty(record.getManagementNumber()),
-                valueOrEmpty(record.getSampleCode()),
-                formatTime(record.getUseStartDate()),
-                formatTime(record.getUseEndDate()),
-                statusText(record.getUseBefore()),
-                statusText(record.getUseAfter()),
-                valueOrEmpty(record.getTemperature()),
-                valueOrEmpty(record.getHumidity()),
-                valueOrEmpty(record.getUsePerson()),
-                valueOrEmpty(record.getRemark())
-        };
-    }
-
     private String formatDate(LocalDateTime value) {
         return value == null ? "" : value.format(DATE_FORMATTER);
     }
@@ -491,7 +476,7 @@
         if (value == null) {
             return "";
         }
-        return value == 0 ? "寮傚父" : "鑹ソ";
+        return value == 0 ? "寮傚父" : "姝e父";
     }
 
     private String valueOrEmpty(String value) {
diff --git a/cnas-device/src/main/resources/mapper/DeviceMaintenancePlanDetailsMapper.xml b/cnas-device/src/main/resources/mapper/DeviceMaintenancePlanDetailsMapper.xml
index f772a03..6ec5d4a 100644
--- a/cnas-device/src/main/resources/mapper/DeviceMaintenancePlanDetailsMapper.xml
+++ b/cnas-device/src/main/resources/mapper/DeviceMaintenancePlanDetailsMapper.xml
@@ -11,10 +11,17 @@
             d.management_number device_number,
             d.specification_model,
             d.management_number,
-            d.storage_point
+            d.storage_point,
+            (select count(*) from device_maintenance dm
+             where dm.maintenance_plan_detail_id = dmpd.maintenance_plan_detail_id) has_record
         from device_maintenance_plan_details dmpd
         left join device d on dmpd.device_id = d.id
         where dmpd.maintenance_plan_id = #{maintenancePlanId}
+        order by has_record desc,
+                 (select count(*) from device_maintenance dm2
+                  where dm2.maintenance_plan_detail_id = dmpd.maintenance_plan_detail_id
+                    and dm2.review_status = 1) desc,
+                 dmpd.maintenance_plan_detail_id desc
     </select>
 
     <!-- 璁惧淇濆吇璁″垝璇︽儏鍒楄〃锛堝惈鍒扮偣鏍囩孩銆佸叧鑱旂殑淇濆吇璁板綍涓庡鏍哥姸鎬侊級 -->
@@ -53,7 +60,9 @@
             case when d.next_maintenance_date is not null
                 and d.next_maintenance_date &lt;= date_add(curdate(), interval 1 month)
                 and (d.last_maintenance_date is null or d.last_maintenance_date &lt; dmpd.maintenance_date)
-                then 1 else 0 end as red_flag
+                then 1 else 0 end as red_flag,
+            (select count(*) from device_maintenance_attachment a
+             where a.maintenance_plan_detail_id = dmpd.maintenance_plan_detail_id) attachment_count
         from device_maintenance_plan_details dmpd
         left join device d on dmpd.device_id = d.id
         left join device_maintenance dm on dm.maintenance_plan_detail_id = dmpd.maintenance_plan_detail_id
diff --git a/cnas-device/src/main/resources/mapper/InsOrderDeviceRecordMapper.xml b/cnas-device/src/main/resources/mapper/InsOrderDeviceRecordMapper.xml
index 571542b..06c044f 100644
--- a/cnas-device/src/main/resources/mapper/InsOrderDeviceRecordMapper.xml
+++ b/cnas-device/src/main/resources/mapper/InsOrderDeviceRecordMapper.xml
@@ -15,6 +15,12 @@
         <if test="deviceId != null">
             and dr.device_id = #{deviceId}
         </if>
+        <if test="deviceName != '' and deviceName != null">
+            and d.device_name like concat('%', #{deviceName}, '%')
+        </if>
+        <if test="specificationModel != '' and specificationModel != null">
+            and d.specification_model like concat('%', #{specificationModel}, '%')
+        </if>
         <if test="sampleCode != '' and sampleCode != null">
             and dr.sample_code like concat('%', #{sampleCode}, '%')
         </if>
@@ -50,15 +56,18 @@
         <if test="deviceId != null">
             and dr.device_id = #{deviceId}
         </if>
-        <if test="sampleCode != '' and sampleCode != null">
-            and dr.sample_code like concat('%', #{sampleCode}, '%')
+        <if test="deviceName != '' and deviceName != null">
+            and d.device_name like concat('%', #{deviceName}, '%')
+        </if>
+        <if test="specificationModel != '' and specificationModel != null">
+            and d.specification_model like concat('%', #{specificationModel}, '%')
         </if>
         <if test="managementNumber != '' and managementNumber != null">
             and d.management_number like concat('%', #{managementNumber}, '%')
         </if>
-        <if test="exportDate != '' and exportDate != null">
-            and DATE_FORMAT(dr.use_start_date, '%Y-%m') = #{exportDate}
+        <if test="sampleCode != '' and sampleCode != null">
+            and dr.sample_code like concat('%', #{sampleCode}, '%')
         </if>
-        ORDER BY dr.use_start_date asc
+        ORDER BY d.device_name asc, d.id asc, dr.use_start_date asc
     </select>
 </mapper>
diff --git a/cnas-device/src/main/resources/static/word/maintenance-plan.docx b/cnas-device/src/main/resources/static/word/maintenance-plan.docx
index d4e69a0..d56c55f 100644
--- a/cnas-device/src/main/resources/static/word/maintenance-plan.docx
+++ b/cnas-device/src/main/resources/static/word/maintenance-plan.docx
Binary files differ
diff --git a/cnas-manage/src/main/java/com/ruoyi/manage/controller/ManageDocumentControlledController.java b/cnas-manage/src/main/java/com/ruoyi/manage/controller/ManageDocumentControlledController.java
index a9c1c9d..cbbbae1 100644
--- a/cnas-manage/src/main/java/com/ruoyi/manage/controller/ManageDocumentControlledController.java
+++ b/cnas-manage/src/main/java/com/ruoyi/manage/controller/ManageDocumentControlledController.java
@@ -1,20 +1,26 @@
 package com.ruoyi.manage.controller;
 
+import com.alibaba.excel.EasyExcel;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 
 import com.deepoove.poi.data.style.*;
 
 import com.ruoyi.common.core.domain.Result;
 import com.ruoyi.common.utils.JackSonUtil;
+import com.ruoyi.framework.exception.ErrorException;
+import com.ruoyi.manage.excel.ManageDocumentControlledImportDto;
+import com.ruoyi.manage.excel.ManageDocumentControlledImportListener;
 import com.ruoyi.manage.pojo.ManageDocumentControlled;
 import com.ruoyi.manage.service.ManageDocumentControlledService;
 import com.deepoove.poi.data.style.*;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import io.swagger.v3.oas.annotations.Operation;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
 
 import jakarta.annotation.Resource;
 import jakarta.servlet.http.HttpServletResponse;
+import java.io.IOException;
 import java.util.Map;
 
 /**
@@ -45,6 +51,21 @@
         return Result.success(manageDocumentControlledService.addManageDocumentControlled(manageDocumentControlled));
     }
 
+    @Operation(summary = "鎵归噺瀵煎叆鏂囦欢鍙楁帶鐢宠")
+    @PostMapping("/importManageDocumentControlled")
+    public Result importManageDocumentControlled(MultipartFile file) {
+        if (file == null || file.isEmpty()) {
+            throw new ErrorException("璇烽�夋嫨瀵煎叆鏂囦欢");
+        }
+        try {
+            EasyExcel.read(file.getInputStream(), ManageDocumentControlledImportDto.class,
+                    new ManageDocumentControlledImportListener(manageDocumentControlledService)).sheet().doRead();
+        } catch (IOException e) {
+            throw new ErrorException("璇诲彇瀵煎叆鏂囦欢澶辫触");
+        }
+        return Result.success();
+    }
+
 
     @Operation(summary = "鏌ョ湅鏂囦欢鍙楁帶")
     @GetMapping("/getManageDocumentControlled")
diff --git a/cnas-manage/src/main/java/com/ruoyi/manage/excel/ManageDocumentControlledImportDto.java b/cnas-manage/src/main/java/com/ruoyi/manage/excel/ManageDocumentControlledImportDto.java
new file mode 100644
index 0000000..8fb3398
--- /dev/null
+++ b/cnas-manage/src/main/java/com/ruoyi/manage/excel/ManageDocumentControlledImportDto.java
@@ -0,0 +1,36 @@
+package com.ruoyi.manage.excel;
+
+import com.alibaba.excel.annotation.ExcelIgnore;
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.time.LocalDate;
+
+/**
+ * 鏂囦欢鍙楁帶鐢宠瀵煎叆琛�
+ */
+@Getter
+@Setter
+@ExcelIgnoreUnannotated
+public class ManageDocumentControlledImportDto {
+
+    @ExcelProperty("鏂囦欢鍚嶇О")
+    private String name;
+
+    @ExcelProperty("鏂囦欢鐗堟湰")
+    private String version;
+
+    @ExcelProperty("鍙戝竷鏃ユ湡")
+    private String publishDate;
+
+    @ExcelProperty("瀹炴柦鏃ユ湡")
+    private String implementDate;
+
+    @ExcelIgnore
+    private LocalDate publishDateValue;
+
+    @ExcelIgnore
+    private LocalDate implementDateValue;
+}
diff --git a/cnas-manage/src/main/java/com/ruoyi/manage/excel/ManageDocumentControlledImportListener.java b/cnas-manage/src/main/java/com/ruoyi/manage/excel/ManageDocumentControlledImportListener.java
new file mode 100644
index 0000000..65d58f9
--- /dev/null
+++ b/cnas-manage/src/main/java/com/ruoyi/manage/excel/ManageDocumentControlledImportListener.java
@@ -0,0 +1,96 @@
+package com.ruoyi.manage.excel;
+
+import com.alibaba.excel.context.AnalysisContext;
+import com.alibaba.excel.event.AnalysisEventListener;
+import com.ruoyi.common.exception.base.BaseException;
+import com.ruoyi.manage.service.ManageDocumentControlledService;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 鏂囦欢鍙楁帶鐢宠鎵归噺瀵煎叆
+ */
+public class ManageDocumentControlledImportListener extends AnalysisEventListener<ManageDocumentControlledImportDto> {
+
+    private static final List<String> HEADERS = Arrays.asList("鏂囦欢鍚嶇О", "鏂囦欢鐗堟湰", "鍙戝竷鏃ユ湡", "瀹炴柦鏃ユ湡");
+    private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-M-d");
+
+    private final ManageDocumentControlledService manageDocumentControlledService;
+    private final List<ManageDocumentControlledImportDto> rows = new ArrayList<>();
+
+    public ManageDocumentControlledImportListener(ManageDocumentControlledService manageDocumentControlledService) {
+        this.manageDocumentControlledService = manageDocumentControlledService;
+    }
+
+    @Override
+    public void invoke(ManageDocumentControlledImportDto data, AnalysisContext context) {
+        if (isBlankRow(data)) {
+            return;
+        }
+        int rowNum = context.readRowHolder().getRowIndex() + 1;
+        if (!StringUtils.hasText(data.getName())) {
+            throw new BaseException("绗�" + rowNum + "琛岋細鏂囦欢鍚嶇О涓嶈兘涓虹┖");
+        }
+        data.setPublishDateValue(parseDate(data.getPublishDate(), rowNum, "鍙戝竷鏃ユ湡"));
+        data.setImplementDateValue(parseDate(data.getImplementDate(), rowNum, "瀹炴柦鏃ユ湡"));
+        rows.add(data);
+    }
+
+    private LocalDate parseDate(String value, int rowNum, String column) {
+        if (!StringUtils.hasText(value)) {
+            return null;
+        }
+        String text = value.trim();
+        int space = text.indexOf(' ');
+        if (space > 0) {
+            text = text.substring(0, space);
+        }
+        text = text.replace("骞�", "-").replace("鏈�", "-").replace("鏃�", "")
+                .replace("/", "-").replace(".", "-");
+        try {
+            // Excel 閲屾棩鏈熷崟鍏冩牸鍙兘琚鎴愭暟瀛楀簭鍒楋紙1899-12-30 璧风畻锛�
+            if (text.matches("\\d{5}(\\.\\d+)?")) {
+                return LocalDate.of(1899, 12, 30).plusDays((long) Double.parseDouble(text));
+            }
+            return LocalDate.parse(text, DATE_FORMAT);
+        } catch (Exception e) {
+            throw new BaseException("绗�" + rowNum + "琛岋細" + column + "鏍煎紡涓嶆纭紝璇锋寜 yyyy-MM-dd 濉啓");
+        }
+    }
+
+    @Override
+    public void doAfterAllAnalysed(AnalysisContext context) {
+        if (rows.isEmpty()) {
+            throw new BaseException("瀵煎叆鏁版嵁涓嶅緱涓虹┖");
+        }
+        manageDocumentControlledService.importManageDocumentControlled(rows);
+    }
+
+    @Override
+    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
+        if (CollectionUtils.isEmpty(headMap) || headMap.size() != HEADERS.size()) {
+            throw new BaseException("瀵煎叆妯℃澘鏈夎锛岃浣跨敤鏈�鏂版ā鏉�");
+        }
+        for (int i = 0; i < HEADERS.size(); i++) {
+            String head = headMap.get(i);
+            if (head == null || !HEADERS.get(i).equals(head.trim())) {
+                throw new BaseException("瀵煎叆妯℃澘鏈夎锛岃〃澶村繀椤讳负锛氭枃浠跺悕绉般�佹枃浠剁増鏈�佸彂甯冩棩鏈熴�佸疄鏂芥棩鏈�");
+            }
+        }
+    }
+
+    private boolean isBlankRow(ManageDocumentControlledImportDto data) {
+        return data == null
+                || (!StringUtils.hasText(data.getName())
+                && !StringUtils.hasText(data.getVersion())
+                && !StringUtils.hasText(data.getPublishDate())
+                && !StringUtils.hasText(data.getImplementDate()));
+    }
+}
diff --git a/cnas-manage/src/main/java/com/ruoyi/manage/mapper/ManageDocumentControlledMapper.java b/cnas-manage/src/main/java/com/ruoyi/manage/mapper/ManageDocumentControlledMapper.java
index 7ae6501..0f127a8 100644
--- a/cnas-manage/src/main/java/com/ruoyi/manage/mapper/ManageDocumentControlledMapper.java
+++ b/cnas-manage/src/main/java/com/ruoyi/manage/mapper/ManageDocumentControlledMapper.java
@@ -7,12 +7,14 @@
 import com.ruoyi.manage.pojo.ManageDocumentControlled;
 import org.apache.ibatis.annotations.Param;
 
+import java.util.Map;
+
 /**
  * <p>
  * 鏂囦欢鍙楁帶 Mapper 鎺ュ彛
  * </p>
  *
- * @author 
+ * @author
  * @since 2024-11-08 02:54:44
  */
 public interface ManageDocumentControlledMapper extends BaseMapper<ManageDocumentControlled> {
@@ -20,4 +22,10 @@
     IPage<ManageDocumentControlled> pageManageDocumentControlled(Page page, @Param("ew") QueryWrapper<ManageDocumentControlled> queryWrappers);
 
     ManageDocumentControlled getManageDocumentControlled(Long id);
+
+    /**
+     * 瀵煎叆鑽夌锛氱敤 Map 浼犲弬锛岀粫杩� MyBatis-Plus 鐨� createUser 鑷姩濉厖锛�
+     * 璁╁鍏ユ暟鎹殑鐢宠浜轰负绌恒��
+     */
+    int insertImportedDraft(Map<String, Object> row);
 }
diff --git a/cnas-manage/src/main/java/com/ruoyi/manage/pojo/ManageDocumentControlled.java b/cnas-manage/src/main/java/com/ruoyi/manage/pojo/ManageDocumentControlled.java
index 5255437..8e148cd 100644
--- a/cnas-manage/src/main/java/com/ruoyi/manage/pojo/ManageDocumentControlled.java
+++ b/cnas-manage/src/main/java/com/ruoyi/manage/pojo/ManageDocumentControlled.java
@@ -52,6 +52,16 @@
     @DateTimeFormat(pattern = "yyyy-MM-dd")
     private LocalDate submitDate;
 
+    @Schema(description = "鍙戝竷鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate publishDate;
+
+    @Schema(description = "瀹炴柦鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate implementDate;
+
     @Schema(description = "璇存槑")
     private String instructions;
 
diff --git a/cnas-manage/src/main/java/com/ruoyi/manage/service/ManageDocumentControlledService.java b/cnas-manage/src/main/java/com/ruoyi/manage/service/ManageDocumentControlledService.java
index 086bbe4..27a8832 100644
--- a/cnas-manage/src/main/java/com/ruoyi/manage/service/ManageDocumentControlledService.java
+++ b/cnas-manage/src/main/java/com/ruoyi/manage/service/ManageDocumentControlledService.java
@@ -3,9 +3,11 @@
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.manage.excel.ManageDocumentControlledImportDto;
 import com.ruoyi.manage.pojo.ManageDocumentControlled;
 
 import jakarta.servlet.http.HttpServletResponse;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -30,5 +32,7 @@
 
     int checkManageDocumentControlled(ManageDocumentControlled manageDocumentControlled);
 
+    int importManageDocumentControlled(List<ManageDocumentControlledImportDto> rows);
+
     void checkManageDocumentControlledPdf(Long id, HttpServletResponse response) throws Exception ;
 }
diff --git a/cnas-manage/src/main/java/com/ruoyi/manage/service/impl/ManageDocumentAlterServiceImpl.java b/cnas-manage/src/main/java/com/ruoyi/manage/service/impl/ManageDocumentAlterServiceImpl.java
index 6984e14..f0b243e 100644
--- a/cnas-manage/src/main/java/com/ruoyi/manage/service/impl/ManageDocumentAlterServiceImpl.java
+++ b/cnas-manage/src/main/java/com/ruoyi/manage/service/impl/ManageDocumentAlterServiceImpl.java
@@ -99,6 +99,7 @@
         manageRecordAudit.setReason(manageDocumentAlter.getAlterNote());
         manageRecordAudit.setAlterUser(SecurityUtils.getUserId().intValue());
         manageRecordAudit.setMethod("淇");
+        manageRecordAudit.setCreateTime(LocalDate.now());
         if (ObjectUtils.isNotEmpty(manageDocumentAlter.getFile())) {
             String urlString;
             String pathName;
@@ -156,7 +157,44 @@
                 return 0;
             }
         }
-        return manageDocumentAlterMapper.updateById(manageDocumentAlter);
+        int result = manageDocumentAlterMapper.updateById(manageDocumentAlter);
+        // 鍙樻洿鍐呭淇敼鍚庡悓姝ュ埌鑷姩鐢熸垚鐨勪慨璁㈣褰曪紝閬垮厤涓や釜椤甸潰鍐呭瀵逛笉涓�
+        ManageRecordAudit linkedAudit = findLinkedAudit(manageDocumentAlter1);
+        if (linkedAudit != null) {
+            if (ObjectUtils.isNotEmpty(manageDocumentAlter.getAlterBeforeCode())) {
+                linkedAudit.setDocumentCode(manageDocumentAlter.getAlterBeforeCode());
+            }
+            if (ObjectUtils.isNotEmpty(manageDocumentAlter.getAlterBeforeName())) {
+                linkedAudit.setDocumentName(manageDocumentAlter.getAlterBeforeName());
+            }
+            if (ObjectUtils.isNotEmpty(manageDocumentAlter.getAlterBeforeVersion())) {
+                linkedAudit.setBeforeVersion(manageDocumentAlter.getAlterBeforeVersion());
+            }
+            if (ObjectUtils.isNotEmpty(manageDocumentAlter.getAlterAfterVersion())) {
+                linkedAudit.setAfterVersion(manageDocumentAlter.getAlterAfterVersion());
+            }
+            if (ObjectUtils.isNotEmpty(manageDocumentAlter.getAlterNote())) {
+                linkedAudit.setReason(manageDocumentAlter.getAlterNote());
+            }
+            if (ObjectUtils.isNotEmpty(manageDocumentAlter.getAlterAfterUrl())) {
+                linkedAudit.setAfterUrl(manageDocumentAlter.getAlterAfterUrl());
+            }
+            manageRecordAuditMapper.updateById(linkedAudit);
+        }
+        return result;
+    }
+
+    // 鎸夊彉鏇村墠淇℃伅瀹氫綅鑷姩鐢熸垚鐨勯偅鏉′慨璁㈣褰�
+    private ManageRecordAudit findLinkedAudit(ManageDocumentAlter manageDocumentAlter) {
+        return manageRecordAuditMapper.selectOne(Wrappers.<ManageRecordAudit>lambdaQuery()
+                .eq(ObjectUtils.isNotEmpty(manageDocumentAlter.getAlterBeforeCode()), ManageRecordAudit::getDocumentCode, manageDocumentAlter.getAlterBeforeCode())
+                .eq(ObjectUtils.isNotEmpty(manageDocumentAlter.getAlterBeforeName()), ManageRecordAudit::getDocumentName, manageDocumentAlter.getAlterBeforeName())
+                .eq(ObjectUtils.isNotEmpty(manageDocumentAlter.getAlterBeforeVersion()), ManageRecordAudit::getBeforeVersion, manageDocumentAlter.getAlterBeforeVersion())
+                .eq(ObjectUtils.isNotEmpty(manageDocumentAlter.getAlterAfterVersion()), ManageRecordAudit::getAfterVersion, manageDocumentAlter.getAlterAfterVersion())
+                .eq(ObjectUtils.isNotEmpty(manageDocumentAlter.getCreateUser()), ManageRecordAudit::getAlterUser, manageDocumentAlter.getCreateUser())
+                .eq(ManageRecordAudit::getMethod, "淇")
+                .orderByDesc(ManageRecordAudit::getId)
+                .last("limit 1"));
     }
 
     @Override
@@ -217,6 +255,8 @@
                 manageRecordAudit.setDocumentName(manageDocumentCancel.getName());
                 manageRecordAudit.setAlterThing("浣滃簾");
                 manageRecordAudit.setMethod("浣滃簾");
+                manageRecordAudit.setAlterUser(oldManageDocumentAlter.getCreateUser());
+                manageRecordAudit.setCreateTime(LocalDate.now());
                 manageRecordAuditMapper.insert(manageRecordAudit);
             }else {
                 //濡傛灉鏄瓨妗d笉鍙敤,閭e氨灏嗘枃浠舵竻鍗曠殑鐘舵�佹敼涓烘棤鏁�
@@ -235,6 +275,15 @@
         manageRecordCheck.setCheckState(manageDocumentAlter.getState());
         manageRecordCheck.setRemark(manageDocumentAlter.getAlterNote());
         manageRecordCheckMapper.insert(manageRecordCheck);
+        // 瀹℃牳閫氳繃鍚庡洖鍐欐壒鍑嗕汉锛屾枃浠朵慨璁㈢敵璇峰鎵硅褰曢噷鑳界洿鎺ョ湅鍒板鎵圭粨鏋�
+        ManageRecordAudit linkedAudit = findLinkedAudit(oldManageDocumentAlter);
+        if (linkedAudit != null
+                && "閫氳繃".equals(manageDocumentAlter.getState())
+                && linkedAudit.getRatifyUser() == null
+                && oldManageDocumentAlter.getCheckUser() != null) {
+            linkedAudit.setRatifyUser(oldManageDocumentAlter.getCheckUser());
+            manageRecordAuditMapper.updateById(linkedAudit);
+        }
         return manageDocumentAlterMapper.updateById(manageDocumentAlter);
     }
 
diff --git a/cnas-manage/src/main/java/com/ruoyi/manage/service/impl/ManageDocumentCancelServiceImpl.java b/cnas-manage/src/main/java/com/ruoyi/manage/service/impl/ManageDocumentCancelServiceImpl.java
index a97290a..3847b0b 100644
--- a/cnas-manage/src/main/java/com/ruoyi/manage/service/impl/ManageDocumentCancelServiceImpl.java
+++ b/cnas-manage/src/main/java/com/ruoyi/manage/service/impl/ManageDocumentCancelServiceImpl.java
@@ -5,20 +5,25 @@
 import com.alibaba.excel.write.metadata.WriteSheet;
 import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.ruoyi.common.utils.QueryWrappers;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.framework.exception.ErrorException;
 import com.ruoyi.system.mapper.UserMapper;
 
 
 import com.ruoyi.manage.mapper.ManageDocumentCancelMapper;
 import com.ruoyi.manage.mapper.ManageDocumentListMapper;
 import com.ruoyi.manage.mapper.ManageRecordAuditMapper;
+import com.ruoyi.manage.mapper.ManageRecordCancelMapper;
 import com.ruoyi.manage.mapper.ManageRecordCheckMapper;
 import com.ruoyi.manage.pojo.ManageDocumentCancel;
 import com.ruoyi.manage.pojo.ManageDocumentList;
 import com.ruoyi.manage.pojo.ManageRecordAudit;
+import com.ruoyi.manage.pojo.ManageRecordCancel;
 import com.ruoyi.manage.pojo.ManageRecordCheck;
 import com.ruoyi.manage.service.ManageDocumentCancelService;
 
@@ -29,6 +34,7 @@
 import jakarta.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.net.URLEncoder;
+import java.time.LocalDate;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -54,7 +60,8 @@
     @Resource
     private ManageRecordAuditMapper manageRecordAuditMapper;
 
-
+    @Resource
+    private ManageRecordCancelMapper manageRecordCancelMapper;
 
     @Resource
     private ManageDocumentListMapper manageDocumentListMapper;
@@ -76,6 +83,8 @@
         manageRecordAudit.setDocumentName(manageDocumentCancel.getName());
         manageRecordAudit.setAlterThing("浣滃簾");
         manageRecordAudit.setMethod("浣滃簾");
+        manageRecordAudit.setAlterUser(SecurityUtils.getUserId().intValue());
+        manageRecordAudit.setCreateTime(LocalDate.now());
         manageRecordAuditMapper.insert(manageRecordAudit);
         return manageDocumentCancelMapper.insert(manageDocumentCancel);
     }
@@ -83,9 +92,14 @@
     @Override
     public int checkManageDocumentCancel(Integer id, String state) {
         ManageDocumentCancel manageDocumentCancel = manageDocumentCancelMapper.selectById(id);
-        if (state.equals("閫氳繃")){
+        if (manageDocumentCancel == null) {
+            throw new ErrorException("鐢宠璁板綍涓嶅瓨鍦�");
+        }
+        if ("閫氳繃".equals(state)){
             //鍒犻櫎鏂囦欢娓呭崟瀵瑰簲鏁版嵁
             manageDocumentListMapper.delete(Wrappers.<ManageDocumentList>lambdaQuery().eq(ManageDocumentList::getDocumentCode,manageDocumentCancel.getDocumentCode()));
+            // 浣滃簾閫氳繃鍚庤嚜鍔ㄧ敓鎴愰攢姣佸彴璐︼紝淇濊瘉鈥滄枃浠朵綔搴熺敵璇封�濆拰鈥滀綔搴熸枃浠堕攢姣佽褰曗�濈殑鏁版嵁鑱斿姩
+            createRecordCancelIfAbsent(manageDocumentCancel);
         }
         manageDocumentCancel.setState(state);
         /*鏂板8.4鐨勬枃浠跺鎵硅褰�*/
@@ -143,4 +157,24 @@
     public int doManageDocumentCancel(ManageDocumentCancel manageDocumentCancel) {
         return manageDocumentCancelMapper.updateById(manageDocumentCancel);
     }
+
+    // 浣滃簾鐢宠閫氳繃鍚庯紝鍦ㄢ�滀綔搴熸枃浠堕攢姣佽褰曗�濈敓鎴愪竴鏉″緟琛ュ叏鐨勫彴璐﹁褰�
+    private void createRecordCancelIfAbsent(ManageDocumentCancel manageDocumentCancel) {
+        ManageRecordCancel existing = manageRecordCancelMapper.selectOne(Wrappers.<ManageRecordCancel>lambdaQuery()
+                .eq(ObjectUtils.isNotEmpty(manageDocumentCancel.getDocumentCode()), ManageRecordCancel::getDocumentCode, manageDocumentCancel.getDocumentCode())
+                .eq(ObjectUtils.isNotEmpty(manageDocumentCancel.getName()), ManageRecordCancel::getDocumentName, manageDocumentCancel.getName())
+                .orderByDesc(ManageRecordCancel::getId)
+                .last("limit 1"));
+        if (existing != null) {
+            return;
+        }
+        ManageRecordCancel manageRecordCancel = new ManageRecordCancel();
+        manageRecordCancel.setDocumentCode(manageDocumentCancel.getDocumentCode());
+        manageRecordCancel.setDocumentName(manageDocumentCancel.getName());
+        manageRecordCancel.setReason(ObjectUtils.isNotEmpty(manageDocumentCancel.getCancelNote())
+                ? manageDocumentCancel.getCancelNote() : "鏂囦欢浣滃簾");
+        manageRecordCancel.setRemark("鐢辨枃浠朵綔搴熺敵璇疯嚜鍔ㄧ敓鎴愶紝璇疯ˉ鍏呴攢姣佹暟閲忓悗鎵瑰噯");
+        manageRecordCancel.setCreateUser(manageDocumentCancel.getCreateUser());
+        manageRecordCancelMapper.insert(manageRecordCancel);
+    }
 }
diff --git a/cnas-manage/src/main/java/com/ruoyi/manage/service/impl/ManageDocumentControlledServiceImpl.java b/cnas-manage/src/main/java/com/ruoyi/manage/service/impl/ManageDocumentControlledServiceImpl.java
index 29ed115..182f382 100644
--- a/cnas-manage/src/main/java/com/ruoyi/manage/service/impl/ManageDocumentControlledServiceImpl.java
+++ b/cnas-manage/src/main/java/com/ruoyi/manage/service/impl/ManageDocumentControlledServiceImpl.java
@@ -7,8 +7,10 @@
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.ruoyi.common.core.domain.entity.User;
+import com.ruoyi.common.numgen.NumberGenerator;
 import com.ruoyi.common.utils.QueryWrappers;
 import com.ruoyi.framework.exception.ErrorException;
+import com.ruoyi.manage.excel.ManageDocumentControlledImportDto;
 import com.ruoyi.manage.mapper.ManageDocumentControlledMapper;
 import com.ruoyi.manage.mapper.ManageDocumentListMapper;
 import com.ruoyi.manage.mapper.ManageRecordCheckMapper;
@@ -28,7 +30,10 @@
 import java.io.FileInputStream;
 import java.io.OutputStream;
 import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -60,6 +65,11 @@
     @Resource
     private UserMapper userMapper;
 
+    @Resource
+    private NumberGenerator<ManageDocumentControlled> numberGenerator;
+
+    private static final DateTimeFormatter CODE_DATE_FORMAT = DateTimeFormatter.ofPattern("yyMMdd");
+
     @Override
     public IPage<ManageDocumentControlled> pageManageDocumentControlled(Page page, ManageDocumentControlled manageDocumentControlled) {
         return manageDocumentControlledMapper.pageManageDocumentControlled(page, QueryWrappers.queryWrappers(manageDocumentControlled));
@@ -68,6 +78,10 @@
     @Override
     public int addManageDocumentControlled(ManageDocumentControlled manageDocumentControlled) {
         manageDocumentControlled.setState("寰呭鏍�");
+        // 鎻愪氦鏃ユ湡涓嶅啀鍦ㄧ晫闈㈠~鍐欙紝鍏ュ簱鏃惰嚜鍔ㄥ啓褰撳ぉ锛屼繚鐣欏師鏈夊瓧娈靛惈涔�
+        if (manageDocumentControlled.getSubmitDate() == null) {
+            manageDocumentControlled.setSubmitDate(LocalDate.now());
+        }
         String urlString;
         String pathName;
         String path;
@@ -149,6 +163,13 @@
     @Override
     public int checkManageDocumentControlled(ManageDocumentControlled manageDocumentControlled) {
         ManageDocumentControlled documentControlled = manageDocumentControlledMapper.selectById(manageDocumentControlled.getId());
+        if (documentControlled == null) {
+            throw new ErrorException("鐢宠璁板綍涓嶅瓨鍦�");
+        }
+        // 宸查�氳繃鐨勭敵璇蜂笉鑳藉啀瀹℃牳锛屽惁鍒欎細閲嶅鐢熸垚鏂囦欢娓呭崟鍜屽鎵硅褰�
+        if ("閫氳繃".equals(documentControlled.getState())) {
+            throw new ErrorException("璇ョ敵璇峰凡瀹℃牳閫氳繃锛屼笉鑳介噸澶嶅鏍�");
+        }
         if (manageDocumentControlled.getState().equals("閫氳繃")) {
             // 鍒犻櫎鏃ф枃浠�
             File oldFile = new File(wordUrl + "/" + documentControlled.getUrl());
@@ -180,7 +201,9 @@
             manageDocumentList.setName(documentControlled.getName());
             manageDocumentList.setVersion(documentControlled.getVersion());
             manageDocumentList.setWriter(documentControlled.getWriter());
-            manageDocumentList.setEffectiveDate(LocalDate.now());
+            // 鏂囦欢娓呭崟鐨勭敓鏁堟棩鏈熷彇鐢宠鐨勨�滃疄鏂芥棩鏈熲�濓紝娌″~鎵嶇敤褰撳ぉ
+            manageDocumentList.setEffectiveDate(documentControlled.getImplementDate() != null
+                    ? documentControlled.getImplementDate() : LocalDate.now());
             manageDocumentList.setState("鏈夋晥");
             manageDocumentList.setUrl(manageDocumentControlled.getUrl());
             manageDocumentListMapper.insert(manageDocumentList);
@@ -190,7 +213,14 @@
         manageRecordCheck.setDocumentCode(documentControlled.getDocumentCode());
         manageRecordCheck.setDocumentName(documentControlled.getName());
         manageRecordCheck.setDocumentVersion(documentControlled.getVersion());
-        manageRecordCheck.setWriteUser(userMapper.selectOne(Wrappers.<User>lambdaQuery().eq(User::getName,documentControlled.getWriter())).getId());
+        // 浣滆�呭彲鑳戒笉鏄郴缁熺敤鎴凤紙鍘嗗彶鏁版嵁/鎵嬪~锛夛紝鏌ヤ笉鍒版椂涓嶅啀绌烘寚閽�
+        User writerUser = null;
+        if (documentControlled.getWriter() != null && !documentControlled.getWriter().trim().isEmpty()) {
+            writerUser = userMapper.selectOne(Wrappers.<User>lambdaQuery()
+                    .eq(User::getName, documentControlled.getWriter())
+                    .last("limit 1"));
+        }
+        manageRecordCheck.setWriteUser(writerUser == null ? null : writerUser.getId());
         manageRecordCheck.setCheckUser(documentControlled.getDutyUser());
         manageRecordCheck.setCheckState(manageDocumentControlled.getState());
         manageRecordCheck.setRemark(documentControlled.getInstructions());
@@ -199,6 +229,38 @@
     }
 
     @Override
+    public int importManageDocumentControlled(List<ManageDocumentControlledImportDto> rows) {
+        LocalDate today = LocalDate.now();
+        LocalDateTime now = LocalDateTime.now();
+        String codePrefix = "WT" + CODE_DATE_FORMAT.format(today);
+        int count = 0;
+        for (ManageDocumentControlledImportDto dto : rows) {
+            Map<String, Object> row = new HashMap<>();
+            row.put("documentCode", numberGenerator.generateNumberWithPrefix(
+                    3, codePrefix, ManageDocumentControlled::getDocumentCode));
+            row.put("type", null);
+            row.put("name", dto.getName().trim());
+            row.put("version", trimToNull(dto.getVersion()));
+            row.put("writer", null);
+            row.put("submitDate", today);
+            row.put("publishDate", dto.getPublishDateValue());
+            row.put("implementDate", dto.getImplementDateValue());
+            row.put("instructions", null);
+            row.put("state", "鑽夌");
+            row.put("url", null);
+            row.put("dutyUser", null);
+            row.put("createTime", now);
+            row.put("updateTime", now);
+            count += manageDocumentControlledMapper.insertImportedDraft(row);
+        }
+        return count;
+    }
+
+    private String trimToNull(String value) {
+        return value == null || value.trim().isEmpty() ? null : value.trim();
+    }
+
+    @Override
     public void checkManageDocumentControlledPdf(Long id, HttpServletResponse response) throws Exception {
         ManageDocumentControlled manageDocumentControlled = manageDocumentControlledMapper.selectById(id);
         if (manageDocumentControlled == null || ObjectUtils.isEmpty(manageDocumentControlled.getUrl())) {
diff --git a/cnas-manage/src/main/java/com/ruoyi/manage/service/impl/ManageDocumentIssueRecycleServiceImpl.java b/cnas-manage/src/main/java/com/ruoyi/manage/service/impl/ManageDocumentIssueRecycleServiceImpl.java
index 74a57c4..5f3c1f7 100644
--- a/cnas-manage/src/main/java/com/ruoyi/manage/service/impl/ManageDocumentIssueRecycleServiceImpl.java
+++ b/cnas-manage/src/main/java/com/ruoyi/manage/service/impl/ManageDocumentIssueRecycleServiceImpl.java
@@ -149,14 +149,35 @@
         if (ObjectUtils.isNotEmpty(manageDocumentIssueRecycle.getRecycleUser())){
             /*鏂板8.3鐨勫彂鏀句笌鍥炴敹璁板綍*/
             ManageRecordIssueRecycle manageRecordIssueRecycle = manageRecordIssueRecycleMapper.selectOne(Wrappers.<ManageRecordIssueRecycle>lambdaQuery()
-                    .eq(ManageRecordIssueRecycle::getDocumentCode, documentIssueRecycle.getDocumentCode())
-                    .eq(ManageRecordIssueRecycle::getDocumentName, documentIssueRecycle.getName())
-                    .eq(ManageRecordIssueRecycle::getDocumentVersion, documentIssueRecycle.getVersion())
-                    .eq(ManageRecordIssueRecycle::getReceiveUser, documentIssueRecycle.getIssueUser())
-                    .eq(ManageRecordIssueRecycle::getReceiveDate, documentIssueRecycle.getIssueDate()));
-            manageRecordIssueRecycle.setSignedUser(manageDocumentIssueRecycle.getRecycleUser());
-            manageRecordIssueRecycle.setSignedDate(manageDocumentIssueRecycle.getRecycleDate());
-            manageRecordIssueRecycleMapper.updateById(manageRecordIssueRecycle);
+                    .eq(ObjectUtils.isNotEmpty(documentIssueRecycle.getDocumentCode()), ManageRecordIssueRecycle::getDocumentCode, documentIssueRecycle.getDocumentCode())
+                    .eq(ObjectUtils.isNotEmpty(documentIssueRecycle.getName()), ManageRecordIssueRecycle::getDocumentName, documentIssueRecycle.getName())
+                    .eq(ObjectUtils.isNotEmpty(documentIssueRecycle.getVersion()), ManageRecordIssueRecycle::getDocumentVersion, documentIssueRecycle.getVersion())
+                    .eq(ObjectUtils.isNotEmpty(documentIssueRecycle.getIssueUser()), ManageRecordIssueRecycle::getReceiveUser, documentIssueRecycle.getIssueUser())
+                    .eq(ObjectUtils.isNotEmpty(documentIssueRecycle.getIssueDate()), ManageRecordIssueRecycle::getReceiveDate, documentIssueRecycle.getIssueDate())
+                    .orderByDesc(ManageRecordIssueRecycle::getId)
+                    .last("limit 1"));
+            if (manageRecordIssueRecycle == null) {
+                // 鍘嗗彶鍙戞斁璁板綍鍦ㄥ彴璐﹂噷鍙兘娌℃湁瀵瑰簲琛岋紝鍥炴敹鏃惰ˉ寤猴紝淇濊瘉涓や釜椤甸潰濮嬬粓鑳藉涓�
+                manageRecordIssueRecycle = new ManageRecordIssueRecycle();
+                manageRecordIssueRecycle.setDocumentCode(documentIssueRecycle.getDocumentCode());
+                manageRecordIssueRecycle.setDocumentName(documentIssueRecycle.getName());
+                manageRecordIssueRecycle.setDocumentVersion(documentIssueRecycle.getVersion());
+                manageRecordIssueRecycle.setReceiveUser(documentIssueRecycle.getIssueUser());
+                manageRecordIssueRecycle.setReceiveDate(documentIssueRecycle.getIssueDate());
+                manageRecordIssueRecycle.setSignedUser(manageDocumentIssueRecycle.getRecycleUser());
+                manageRecordIssueRecycle.setSignedDate(manageDocumentIssueRecycle.getRecycleDate());
+                manageRecordIssueRecycleMapper.insert(manageRecordIssueRecycle);
+            } else {
+                // 鍙戞斁淇℃伅璺熺潃鍙樻洿锛屽洖鏀朵俊鎭洖鍐欑鏀朵汉/绛炬敹鏃ユ湡
+                manageRecordIssueRecycle.setDocumentCode(manageDocumentIssueRecycle.getDocumentCode());
+                manageRecordIssueRecycle.setDocumentName(manageDocumentIssueRecycle.getName());
+                manageRecordIssueRecycle.setDocumentVersion(manageDocumentIssueRecycle.getVersion());
+                manageRecordIssueRecycle.setReceiveUser(manageDocumentIssueRecycle.getIssueUser());
+                manageRecordIssueRecycle.setReceiveDate(manageDocumentIssueRecycle.getIssueDate());
+                manageRecordIssueRecycle.setSignedUser(manageDocumentIssueRecycle.getRecycleUser());
+                manageRecordIssueRecycle.setSignedDate(manageDocumentIssueRecycle.getRecycleDate());
+                manageRecordIssueRecycleMapper.updateById(manageRecordIssueRecycle);
+            }
         }
         if (ObjectUtils.isNotEmpty(manageDocumentIssueRecycle.getFile())) {
             if (ObjectUtils.isNotEmpty(documentIssueRecycle.getUrl())) {
diff --git a/cnas-manage/src/main/resources/mapper/ManageDocumentControlledMapper.xml b/cnas-manage/src/main/resources/mapper/ManageDocumentControlledMapper.xml
index 554119e..9b844b3 100644
--- a/cnas-manage/src/main/resources/mapper/ManageDocumentControlledMapper.xml
+++ b/cnas-manage/src/main/resources/mapper/ManageDocumentControlledMapper.xml
@@ -10,6 +10,8 @@
         <result column="version" property="version"/>
         <result column="writer" property="writer"/>
         <result column="submit_date" property="submitDate"/>
+        <result column="publish_date" property="publishDate"/>
+        <result column="implement_date" property="implementDate"/>
         <result column="instructions" property="instructions"/>
         <result column="state" property="state"/>
         <result column="url" property="url"/>
@@ -39,4 +41,14 @@
                  left join user u1 on cmdc.create_user = u1.id
                  left join user u2 on cmdc.duty_user = u2.id
     </select>
+
+    <!-- 瀵煎叆鑽夌锛歝reate_user 鏄惧紡鍐� NULL锛岀敵璇蜂汉鐣欑┖ -->
+    <insert id="insertImportedDraft" parameterType="java.util.Map">
+        insert into cnas_manage_document_controlled
+        (document_code, type, name, version, writer, submit_date, publish_date, implement_date,
+         instructions, state, url, duty_user, create_user, update_user, create_time, update_time)
+        values
+        (#{documentCode}, #{type}, #{name}, #{version}, #{writer}, #{submitDate}, #{publishDate}, #{implementDate},
+         #{instructions}, #{state}, #{url}, #{dutyUser}, null, null, #{createTime}, #{updateTime})
+    </insert>
 </mapper>
diff --git a/cnas-personnel/src/main/java/com/ruoyi/personnel/controller/PersonBasicInfoController.java b/cnas-personnel/src/main/java/com/ruoyi/personnel/controller/PersonBasicInfoController.java
index f4bb37f..e6e02ae 100644
--- a/cnas-personnel/src/main/java/com/ruoyi/personnel/controller/PersonBasicInfoController.java
+++ b/cnas-personnel/src/main/java/com/ruoyi/personnel/controller/PersonBasicInfoController.java
@@ -96,9 +96,20 @@
     // 鍒犻櫎鏂囦欢
     @DeleteMapping("/deleteCNASFile")
     public Result saveFile(String fileName) {
+        if (fileName == null || fileName.trim().isEmpty()) {
+            throw new ErrorException("缂哄皯鏂囦欢鍚�");
+        }
         String[] split = fileName.split(",");
         for (String s : split) {
-            FileSaveUtil.DeleteFile(s);
+            String name = s.trim();
+            if (name.isEmpty()) {
+                continue;
+            }
+            // 鍙厑璁稿垹闄ゆ枃浠跺悕锛岀姝㈤�氳繃璺緞绌胯秺鍒犻櫎鍏朵粬鐩綍
+            if (name.contains("/") || name.contains("\\") || name.contains("..")) {
+                throw new ErrorException("鏂囦欢鍚嶄笉鍚堟硶");
+            }
+            FileSaveUtil.DeleteFile(name);
         }
         return Result.success();
 
@@ -144,8 +155,8 @@
 
     @Operation(summary = "涓嬭浇浜哄憳妗f鍗�")
     @GetMapping("/exportPersonBasicInfoById")
-    public Result exportPersonBasicInfoById(Integer id, HttpServletResponse response) {
-        return Result.success(personBasicInfoService.exportPersonBasicInfoById(id,response));
+    public void exportPersonBasicInfoById(Integer id, HttpServletResponse response) {
+        personBasicInfoService.exportPersonBasicInfoById(id, response);
     }
 
     /**
diff --git a/cnas-personnel/src/main/java/com/ruoyi/personnel/controller/PersonPersonnelCapacityController.java b/cnas-personnel/src/main/java/com/ruoyi/personnel/controller/PersonPersonnelCapacityController.java
index 814664f..ce9d3c9 100644
--- a/cnas-personnel/src/main/java/com/ruoyi/personnel/controller/PersonPersonnelCapacityController.java
+++ b/cnas-personnel/src/main/java/com/ruoyi/personnel/controller/PersonPersonnelCapacityController.java
@@ -5,6 +5,7 @@
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ruoyi.common.core.domain.Result;
 import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.framework.exception.ErrorException;
 import com.ruoyi.personnel.dto.PersonPersonnelCapacityDto;
 import com.ruoyi.personnel.pojo.PersonPersonnelCapacity;
 import com.ruoyi.personnel.service.PersonPersonnelCapacityService;
@@ -36,6 +37,18 @@
     @Operation(summary = "鏂板/鏇存柊 浜哄憳鑳藉姏")
     @PostMapping("addOrUpdatePersonPersonnelCapacity")
     public Result<?> addOrUpdatePersonPersonnelCapacity(@RequestBody PersonPersonnelCapacity personPersonnelCapacity) {
+        if (personPersonnelCapacity == null || personPersonnelCapacity.getUserId() == null) {
+            throw new ErrorException("缂哄皯浜哄憳");
+        }
+        if (personPersonnelCapacity.getId() != null) {
+            PersonPersonnelCapacity existing = personPersonnelCapacityService.getById(personPersonnelCapacity.getId());
+            if (existing == null) {
+                throw new ErrorException("浜哄憳鑳藉姏璁板綍涓嶅瓨鍦ㄦ垨宸茶鍒犻櫎");
+            }
+            if (existing.getConfirmDate() != null) {
+                throw new ErrorException("璇ヨ褰曞凡纭锛屼笉鑳戒慨鏀�");
+            }
+        }
         personPersonnelCapacityService.saveOrUpdate(personPersonnelCapacity);
         return Result.success();
     }
@@ -43,7 +56,16 @@
     @Operation(summary = "鍒犻櫎 浜哄憳鑳藉姏")
     @DeleteMapping("deletePersonPersonnelCapacity")
     public Result<?> deletePersonPersonnelCapacity(@RequestParam("id") Integer id) {
-        // 鍒犻櫎鏁版嵁
+        if (id == null) {
+            throw new ErrorException("缂哄皯浜哄憳鑳藉姏璁板綍id");
+        }
+        PersonPersonnelCapacity existing = personPersonnelCapacityService.getById(id);
+        if (existing == null) {
+            throw new ErrorException("浜哄憳鑳藉姏璁板綍涓嶅瓨鍦ㄦ垨宸茶鍒犻櫎");
+        }
+        if (existing.getConfirmDate() != null) {
+            throw new ErrorException("璇ヨ褰曞凡纭锛屼笉鑳藉垹闄�");
+        }
         personPersonnelCapacityService.removeById(id);
         return Result.success();
     }
@@ -60,11 +82,24 @@
     @Operation(summary = "纭 浜哄憳鑳藉姏")
     @PostMapping("confirmPersonnelCapability")
     public Result<?> confirmPersonnelCapability(@RequestBody PersonPersonnelCapacity personPersonnelCapacity) {
+        if (personPersonnelCapacity == null || personPersonnelCapacity.getId() == null) {
+            throw new ErrorException("缂哄皯浜哄憳鑳藉姏璁板綍id");
+        }
+        PersonPersonnelCapacity record = personPersonnelCapacityService.getById(personPersonnelCapacity.getId());
+        if (record == null) {
+            throw new ErrorException("浜哄憳鑳藉姏璁板綍涓嶅瓨鍦ㄦ垨宸茶鍒犻櫎");
+        }
+        if (record.getConfirmDate() != null) {
+            throw new ErrorException("璇ヨ褰曞凡纭锛岃鍕块噸澶嶇‘璁�");
+        }
         Integer userId = SecurityUtils.getUserId().intValue();
+        if (record.getConfirmOperatingPersonnelId() == null
+                || !record.getConfirmOperatingPersonnelId().equals(userId)) {
+            throw new ErrorException("鎮ㄤ笉鏄璁板綍鎸囧畾鐨勭‘璁や汉锛屾棤娉曠‘璁�");
+        }
         personPersonnelCapacityService.update(Wrappers.<PersonPersonnelCapacity>lambdaUpdate()
-                .eq(PersonPersonnelCapacity::getId, personPersonnelCapacity.getId())
-                .set(PersonPersonnelCapacity::getConfirmOperatingPersonnelId, userId)
-                .set(PersonPersonnelCapacity::getConfirmDate, personPersonnelCapacity.getConfirmDate()));
+                .eq(PersonPersonnelCapacity::getId, record.getId())
+                .set(PersonPersonnelCapacity::getConfirmDate, LocalDateTime.now()));
         return Result.success();
     }
 
diff --git a/cnas-personnel/src/main/java/com/ruoyi/personnel/controller/PersonPostAuthorizationRecordController.java b/cnas-personnel/src/main/java/com/ruoyi/personnel/controller/PersonPostAuthorizationRecordController.java
index d5b5893..ea8e9fd 100644
--- a/cnas-personnel/src/main/java/com/ruoyi/personnel/controller/PersonPostAuthorizationRecordController.java
+++ b/cnas-personnel/src/main/java/com/ruoyi/personnel/controller/PersonPostAuthorizationRecordController.java
@@ -3,6 +3,7 @@
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ruoyi.common.core.domain.Result;
+import com.ruoyi.framework.exception.ErrorException;
 import com.ruoyi.common.utils.FileSaveUtil;
 import com.ruoyi.personnel.dto.PersonPostAuthorizationRecordDto;
 import com.ruoyi.personnel.pojo.PersonPostAuthorizationRecord;
@@ -33,6 +34,16 @@
     @Operation(summary = "鏂板/鏇存柊 浠昏亴鎺堟潈璁板綍")
     @PostMapping("/addOrUpdatePersonPostAuthorizationRecord")
     public Result<?> addOrUpdatePersonPostAuthorizationRecord(@RequestBody PersonPostAuthorizationRecord personRewardPunishmentRecord) {
+        if (personRewardPunishmentRecord == null || personRewardPunishmentRecord.getUserId() == null) {
+            throw new ErrorException("璇烽�夋嫨琚换鑱屼汉鍛�");
+        }
+        if (personRewardPunishmentRecord.getPost() == null || personRewardPunishmentRecord.getPost().trim().isEmpty()) {
+            throw new ErrorException("璇疯緭鍏ヤ换鑱屽矖浣�");
+        }
+        if (personRewardPunishmentRecord.getId() != null
+                && personPostAuthorizationRecordService.getById(personRewardPunishmentRecord.getId()) == null) {
+            throw new ErrorException("浠昏亴鎺堟潈璁板綍涓嶅瓨鍦ㄦ垨宸茶鍒犻櫎");
+        }
         personPostAuthorizationRecordService.saveOrUpdate(personRewardPunishmentRecord);
         return Result.success();
     }
@@ -40,8 +51,11 @@
     @Operation(summary = "鍒犻櫎 浠昏亴鎺堟潈璁板綍")
     @DeleteMapping("/deletePersonPostAuthorizationRecord")
     public Result<?> deletePersonPostAuthorizationRecord(@RequestParam("id") Integer id) {
-        // 鍒犻櫎鏂囦欢
         PersonPostAuthorizationRecord postAuthorizationRecord = personPostAuthorizationRecordService.getById(id);
+        if (postAuthorizationRecord == null) {
+            throw new ErrorException("浠昏亴鎺堟潈璁板綍涓嶅瓨鍦ㄦ垨宸茶鍒犻櫎");
+        }
+        // 鍒犻櫎鏂囦欢
         FileSaveUtil.DeleteFile(postAuthorizationRecord.getSystemFileName());
         // 鍒犻櫎鏁版嵁
         personPostAuthorizationRecordService.removeById(id);
diff --git a/cnas-personnel/src/main/java/com/ruoyi/personnel/dto/UserPageDto.java b/cnas-personnel/src/main/java/com/ruoyi/personnel/dto/UserPageDto.java
index 77c257d..a80a46a 100644
--- a/cnas-personnel/src/main/java/com/ruoyi/personnel/dto/UserPageDto.java
+++ b/cnas-personnel/src/main/java/com/ruoyi/personnel/dto/UserPageDto.java
@@ -20,5 +20,7 @@
     @Schema(description = "瑙掕壊")
     private String roleName;
 
+    @Schema(description = "缁勭粐鏋舵瀯id")
+    private Integer departmentId;
 
 }
diff --git a/cnas-personnel/src/main/java/com/ruoyi/personnel/pojo/PersonBasicInfo.java b/cnas-personnel/src/main/java/com/ruoyi/personnel/pojo/PersonBasicInfo.java
index 40154d6..db9b35b 100644
--- a/cnas-personnel/src/main/java/com/ruoyi/personnel/pojo/PersonBasicInfo.java
+++ b/cnas-personnel/src/main/java/com/ruoyi/personnel/pojo/PersonBasicInfo.java
@@ -44,6 +44,7 @@
     private String personnelClassification;
 
     @Schema(description = "鍑虹敓鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
     @TableField(updateStrategy = FieldStrategy.ALWAYS) // 涓虹┖鍙互鏇存柊
     private LocalDateTime dateBirth;
 
@@ -62,6 +63,7 @@
     private String officialAcademicRedentials;
 
     @Schema(description = "姣曚笟鏃堕棿1")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
     @ExcelProperty("姣曚笟鏃堕棿")
     private LocalDateTime graduationTime1;
 
@@ -74,6 +76,7 @@
     private String major1;
 
     @Schema(description = "姣曚笟鏃堕棿2")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
     @TableField(updateStrategy = FieldStrategy.ALWAYS) // 涓虹┖鍙互鏇存柊
     private LocalDateTime graduationTime2;
 
diff --git a/cnas-personnel/src/main/java/com/ruoyi/personnel/pojo/PersonJobResponsibilities.java b/cnas-personnel/src/main/java/com/ruoyi/personnel/pojo/PersonJobResponsibilities.java
index 4cdd931..7663f2f 100644
--- a/cnas-personnel/src/main/java/com/ruoyi/personnel/pojo/PersonJobResponsibilities.java
+++ b/cnas-personnel/src/main/java/com/ruoyi/personnel/pojo/PersonJobResponsibilities.java
@@ -1,6 +1,7 @@
 package com.ruoyi.personnel.pojo;
 
 import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Getter;
 import lombok.Setter;
@@ -44,6 +45,7 @@
     private String submittingOperator;
 
     @Schema(description = "1鎻愪氦鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private LocalDateTime submittingDate;
 
     @Schema(description = "2 浠昏亴浜� 涓荤id")
@@ -53,12 +55,14 @@
     private String incumbentOperator;
 
     @Schema(description = "2 浠昏亴浜� 鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private LocalDateTime incumbentDate;
 
     @Schema(description = "3 涓荤 鎿嶄綔浜�")
     private String supervisorOperator;
 
     @Schema(description = "3 涓荤 鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private LocalDateTime supervisorDate;
 
     @Schema(description = "鍒涘缓浜篿d", hidden = true)
@@ -70,6 +74,7 @@
 
     @Schema(description = "鍒涘缓鏃ユ湡 / 鎻愪氦鏃ユ湡", hidden = true)
     @TableField(fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private LocalDateTime createTime;
 
     @Schema(description = "褰撳墠璐熻矗浜�")
diff --git a/cnas-personnel/src/main/java/com/ruoyi/personnel/pojo/PersonPersonnelCapacity.java b/cnas-personnel/src/main/java/com/ruoyi/personnel/pojo/PersonPersonnelCapacity.java
index 242607d..7bab38c 100644
--- a/cnas-personnel/src/main/java/com/ruoyi/personnel/pojo/PersonPersonnelCapacity.java
+++ b/cnas-personnel/src/main/java/com/ruoyi/personnel/pojo/PersonPersonnelCapacity.java
@@ -1,6 +1,7 @@
 package com.ruoyi.personnel.pojo;
 
 import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Getter;
 import lombok.Setter;
@@ -89,14 +90,17 @@
     private Integer confirmOperatingPersonnelId;
 
     @Schema(description = "2 纭浜� 鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private LocalDateTime confirmDate;
 
     @Schema(description = "鍒涘缓鏃ユ湡", hidden = true)
     @TableField(fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private LocalDateTime createTime;
 
     @Schema(description = "鏇存柊鏃ユ湡", hidden = true)
     @TableField(fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private LocalDateTime updateTime;
 
     @Schema(description = "鍒涘缓浜�", hidden = true)
diff --git a/cnas-personnel/src/main/java/com/ruoyi/personnel/pojo/PersonPostAuthorizationRecord.java b/cnas-personnel/src/main/java/com/ruoyi/personnel/pojo/PersonPostAuthorizationRecord.java
index 8fd0b49..04d4fc4 100644
--- a/cnas-personnel/src/main/java/com/ruoyi/personnel/pojo/PersonPostAuthorizationRecord.java
+++ b/cnas-personnel/src/main/java/com/ruoyi/personnel/pojo/PersonPostAuthorizationRecord.java
@@ -1,6 +1,7 @@
 package com.ruoyi.personnel.pojo;
 
 import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Getter;
 import lombok.Setter;
@@ -51,10 +52,12 @@
 
     @Schema(description = "鍒涘缓鏃堕棿", hidden = true)
     @TableField(fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private LocalDateTime createTime;
 
     @Schema(description = "鏇存柊鏃堕棿", hidden = true)
     @TableField(fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private LocalDateTime updateTime;
 
     @Schema(description = "鏇存柊浜篿d", hidden = true)
diff --git a/cnas-personnel/src/main/java/com/ruoyi/personnel/pojo/PersonTraining.java b/cnas-personnel/src/main/java/com/ruoyi/personnel/pojo/PersonTraining.java
index 6ea9853..a488462 100644
--- a/cnas-personnel/src/main/java/com/ruoyi/personnel/pojo/PersonTraining.java
+++ b/cnas-personnel/src/main/java/com/ruoyi/personnel/pojo/PersonTraining.java
@@ -1,6 +1,7 @@
 package com.ruoyi.personnel.pojo;
 
 import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Getter;
 import lombok.Setter;
@@ -38,12 +39,14 @@
     private Integer compilerId;
 
     @Schema(description = "缂栧埗鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private LocalDateTime compilationDate;
 
     @Schema(description = "瀹℃牳浜篿d")
     private Integer reviewerId;
 
     @Schema(description = "瀹℃牳鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private LocalDateTime auditDate;
 
     @Schema(description = "瀹℃牳鐘舵��")
@@ -62,10 +65,12 @@
     private Integer approvalStatus;
 
     @Schema(description = "鎵瑰噯鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private LocalDateTime approvalDate;
 
     @Schema(description = "鍒涘缓鏃ユ湡")
     @TableField(fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private LocalDateTime createTime;
 
     @Schema(description = "鍒涘缓浜篿d")
@@ -74,6 +79,7 @@
 
     @Schema(description = "鏇存柊鏃堕棿")
     @TableField(fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private LocalDateTime updateTime;
 
     @Schema(description = "鏇存柊浜篿d")
diff --git a/cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonBasicInfoServiceImpl.java b/cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonBasicInfoServiceImpl.java
index 3e4049a..dae7724 100644
--- a/cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonBasicInfoServiceImpl.java
+++ b/cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonBasicInfoServiceImpl.java
@@ -163,7 +163,18 @@
     @Override
     public void exportPersonBasicInfo(UserPageDto userPageDto, HttpServletResponse response) throws Exception {
         ArrayList<PersonBasicInfoDto> data = new ArrayList<>();
-        List<User> list = userMapper.selectList(null);
+        LambdaQueryWrapper<User> userWrapper = Wrappers.<User>lambdaQuery()
+                .and(w -> w.eq(User::getDelFlag, "0").or().isNull(User::getDelFlag))
+                .and(w -> w.eq(User::getIsCustom, 0).or().isNull(User::getIsCustom));
+        if (userPageDto != null) {
+            if (ObjectUtils.isNotEmpty(userPageDto.getName())) {
+                userWrapper.like(User::getName, userPageDto.getName().trim());
+            }
+            if (userPageDto.getDepartmentId() != null) {
+                userWrapper.apply("FIND_IN_SET({0}, depart_lims_id)", userPageDto.getDepartmentId());
+            }
+        }
+        List<User> list = userMapper.selectList(userWrapper);
         for (User user : list) {
             PersonBasicInfoDto personBasicInfoDto = new PersonBasicInfoDto();
             PersonBasicInfo personBasicInfo = baseMapper.selectOne(Wrappers.<PersonBasicInfo>lambdaQuery().eq(PersonBasicInfo::getUserId, user.getId()));
@@ -193,12 +204,18 @@
 
     @Override
     public String exportPersonBasicInfoById(Integer id, HttpServletResponse response) {
-        Map<String, Object> userMap = baseMapper.selectexportPersonBasic(id);
+        if (id == null) {
+            throw new ErrorException("缂哄皯浜哄憳id");
+        }
         User user = userMapper.selectById(id);
-        PersonBasicInfo personBasicInfo = baseMapper.selectOne(Wrappers.<PersonBasicInfo>lambdaQuery().eq(PersonBasicInfo::getUserId, user.getId()));
+        if (user == null) {
+            throw new ErrorException("浜哄憳涓嶅瓨鍦ㄦ垨宸茶鍒犻櫎");
+        }
+        PersonBasicInfo personBasicInfo = baseMapper.selectOne(Wrappers.<PersonBasicInfo>lambdaQuery().eq(PersonBasicInfo::getUserId, id));
         if (ObjectUtils.isEmpty(personBasicInfo)){
             throw new ErrorException("璇ョ敤鎴风殑鍩烘湰淇℃伅娌℃湁褰曞叆,鏆傛棤娉曞鍑�");
         }
+        Map<String, Object> userMap = baseMapper.selectexportPersonBasic(id);
         // 璇佷欢
         List<Annex> annexList = annexMapper.selectList(Wrappers.<Annex>lambdaQuery()
                 .eq(Annex::getUserId, id));
@@ -320,27 +337,26 @@
      */
     @Override
     public boolean delDepartment(Integer id) {
-        //鍒ゆ柇鏄惁鏈夊瓙绫�,鐩村埌娌℃湁涓烘
-        List<DepartmentLims> department = getDepartment(id);
-        List<Integer> ids = department.stream().map(DepartmentLims::getId).collect(Collectors.toList());
-        departmentMapper.delete(Wrappers.<DepartmentLims>lambdaQuery()
-                .in(DepartmentLims::getId, ids));
-        return true;
-    }
-
-    //鍒ゆ柇鏄惁鏈夊瓙绫�,鐩村埌娌℃湁涓烘
-    public List<DepartmentLims> getDepartment(Integer id) {
-        List<DepartmentLims> list = new ArrayList<>();
-        DepartmentLims depart = departmentMapper.selectById(id);
-        list.add(depart);
-        List<DepartmentLims> departments = departmentMapper.selectList(Wrappers.<DepartmentLims>lambdaQuery().eq(DepartmentLims::getFatherId, id));
-        if (ObjectUtils.isNotEmpty(departments)) {
-            list.addAll(departments);
-            for (DepartmentLims department : departments) {
-                list.addAll(getDepartment(department.getId()));
-            }
+        if (id == null) {
+            throw new ErrorException("缂哄皯鏋舵瀯id");
         }
-        return list;
+        DepartmentLims depart = departmentMapper.selectById(id);
+        if (depart == null) {
+            throw new ErrorException("璇ユ灦鏋勪笉瀛樺湪鎴栧凡琚垹闄�");
+        }
+        Long childCount = departmentMapper.selectCount(Wrappers.<DepartmentLims>lambdaQuery()
+                .eq(DepartmentLims::getFatherId, id));
+        if (childCount != null && childCount > 0) {
+            throw new ErrorException("璇ユ灦鏋勪笅瀛樺湪涓嬬骇鏋舵瀯锛岃鍏堝垹闄や笅绾ф灦鏋�");
+        }
+        Long userCount = userMapper.selectCount(Wrappers.<User>lambdaQuery()
+                .and(w -> w.eq(User::getDelFlag, "0").or().isNull(User::getDelFlag))
+                .apply("FIND_IN_SET({0}, depart_lims_id)", id));
+        if (userCount != null && userCount > 0) {
+            throw new ErrorException("璇ユ灦鏋勪笅杩樻湁浜哄憳锛岃鍏堝皢浜哄憳绉诲嚭璇ユ灦鏋�");
+        }
+        departmentMapper.deleteById(id);
+        return true;
     }
 
     /**
diff --git a/cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonJobResponsibilitiesServiceImpl.java b/cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonJobResponsibilitiesServiceImpl.java
index e34c522..c5f0c12 100644
--- a/cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonJobResponsibilitiesServiceImpl.java
+++ b/cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonJobResponsibilitiesServiceImpl.java
@@ -8,6 +8,7 @@
 import com.deepoove.poi.config.Configure;
 import com.deepoove.poi.config.ConfigureBuilder;
 import com.ruoyi.common.core.domain.entity.User;
+import com.ruoyi.framework.exception.ErrorException;
 import com.ruoyi.personnel.dto.PersonJobResponsibilitiesDto;
 import com.ruoyi.personnel.mapper.PersonJobResponsibilitiesMapper;
 import com.ruoyi.personnel.pojo.PersonJobResponsibilities;
@@ -47,18 +48,31 @@
 
     @Override
     public void exportPersonJobResponsibilities(Integer id, HttpServletResponse response) {
+        if (id == null) {
+            throw new ErrorException("缂哄皯宀椾綅鑱岃矗id");
+        }
         PersonJobResponsibilities personJobResponsibilities = baseMapper.selectById(id);
+        if (personJobResponsibilities == null) {
+            throw new ErrorException("宀椾綅鑱岃矗涓嶅瓨鍦ㄦ垨宸茶鍒犻櫎");
+        }
+        if (ObjectUtils.isEmpty(personJobResponsibilities.getIncumbentId())) {
+            throw new ErrorException("璇ュ矖浣嶈亴璐h繕娌℃湁閫夋嫨浠昏亴浜猴紝鏆傛棤娉曞鍑�");
+        }
         DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy骞碝M鏈坉d鏃�");
         //宀椾綅鍚嶇О
         String name = personJobResponsibilities.getPostName();
+        Integer incumbentUserId = Integer.parseInt(personJobResponsibilities.getIncumbentId());
         //鎵�灞為儴闂�
-        String departLims = userMapper.selectUserDepartmentLimsName(Integer.parseInt(personJobResponsibilities.getIncumbentId()));
+        String departLims = userMapper.selectUserDepartmentLimsName(incumbentUserId);
         //宸ヤ綔鐩爣
         String objective = personJobResponsibilities.getJobObjective();
         //宀椾綅鑱岃矗
         String responsibilities = personJobResponsibilities.getJobResponsibilities();
         //浠昏亴浜�
-        User user = userMapper.selectById(Integer.parseInt(personJobResponsibilities.getIncumbentId()));
+        User user = userMapper.selectById(incumbentUserId);
+        if (user == null) {
+            throw new ErrorException("浠昏亴浜轰笉瀛樺湪鎴栧凡琚垹闄わ紝璇烽噸鏂伴�夋嫨浠昏亴浜�");
+        }
         String incumbent = user.getName();
         //浠昏亴鏃堕棿
         String incumbentDate = "";
@@ -68,7 +82,8 @@
         //涓荤
         String supervisor = "";
         if (ObjectUtils.isNotEmpty(personJobResponsibilities.getSupervisorId())) {
-            supervisor = userMapper.selectById(personJobResponsibilities.getSupervisorId()).getName();
+            User supervisorUser = userMapper.selectById(personJobResponsibilities.getSupervisorId());
+            supervisor = supervisorUser != null ? supervisorUser.getName() : "";
         }
         //涓荤鏃堕棿
         String supervisorDate = "";
diff --git a/cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonPersonnelCapacityServiceImpl.java b/cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonPersonnelCapacityServiceImpl.java
index 2974840..d10a4bd 100644
--- a/cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonPersonnelCapacityServiceImpl.java
+++ b/cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonPersonnelCapacityServiceImpl.java
@@ -29,6 +29,7 @@
 
 import jakarta.annotation.Resource;
 import jakarta.servlet.http.HttpServletResponse;
+import java.io.File;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.URLEncoder;
@@ -69,19 +70,35 @@
      */
     @Override
     public void exportPersonnelCapacity(Integer id, HttpServletResponse response) {
+        if (id == null) {
+            throw new ErrorException("缂哄皯浜哄憳鑳藉姏璁板綍id");
+        }
         PersonPersonnelCapacityExportDto capacityExportDto = baseMapper.selectExportPersonnelCapacity(id);
+        if (capacityExportDto == null) {
+            throw new ErrorException("浜哄憳鑳藉姏璁板綍涓嶅瓨鍦ㄦ垨宸茶鍒犻櫎");
+        }
 
         // 纭浜�
         String confirmUrl = null;
         if (capacityExportDto.getConfirmOperatingPersonnelId() != null) {
-            confirmUrl = userMapper.selectById(capacityExportDto.getConfirmOperatingPersonnelId()).getSignatureUrl();
+            User confirmUser = userMapper.selectById(capacityExportDto.getConfirmOperatingPersonnelId());
+            if (confirmUser == null) {
+                throw new ErrorException("纭浜轰笉瀛樺湪鎴栧凡琚垹闄わ紝鏃犳硶瀵煎嚭");
+            }
+            confirmUrl = confirmUser.getSignatureUrl();
             if (StringUtils.isBlank(confirmUrl)) {
-                throw new ErrorException("缂哄皯纭浜虹鍚�");
+                throw new ErrorException("纭浜鸿繕娌℃湁涓婁紶绛惧悕锛屾棤娉曞鍑�");
+            }
+            if (!new File(imgUrl + "/" + confirmUrl).exists()) {
+                throw new ErrorException("纭浜虹鍚嶆枃浠朵笉瀛樺湪锛岃璁╃‘璁や汉閲嶆柊涓婁紶");
             }
         }
 
         // 鑾峰彇璺緞
         InputStream inputStream = this.getClass().getResourceAsStream("/static/personnel-capacity.docx");
+        if (inputStream == null) {
+            throw new ErrorException("浜哄憳鑳藉姏瀵煎嚭妯℃澘涓嶅瓨鍦�");
+        }
         ConfigureBuilder builder = Configure.builder();
         builder.useSpringEL(true);
         String finalConfirmUrl = confirmUrl;
@@ -95,8 +112,8 @@
 
         try {
             response.setContentType("application/msword");
-            String fileName = URLEncoder.encode(
-                    "浜哄憳鑳藉姏", "UTF-8");
+            String userName = StringUtils.isBlank(capacityExportDto.getUserName()) ? "浜哄憳鑳藉姏" : capacityExportDto.getUserName() + "浜哄憳鑳藉姏璁ゅ畾";
+            String fileName = URLEncoder.encode(userName, "UTF-8");
             response.setHeader("Content-disposition",
                     "attachment;filename=" + fileName + ".docx");
             OutputStream os = response.getOutputStream();
@@ -117,17 +134,40 @@
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void submitConfirmPersonnelCapability(PersonPersonnelCapacity personPersonnelCapacity) {
+        if (personPersonnelCapacity.getUserId() == null) {
+            throw new ErrorException("缂哄皯浜哄憳");
+        }
         if (personPersonnelCapacity.getConfirmOperatingPersonnelId() == null) {
             throw new ErrorException("缂哄皯纭浜�");
         }
         User formUser = userMapper.selectById(personPersonnelCapacity.getUserId());
+        if (formUser == null) {
+            throw new ErrorException("浜哄憳涓嶅瓨鍦ㄦ垨宸茶鍒犻櫎锛屾棤娉曟彁浜�");
+        }
+        User confirmUser = userMapper.selectById(personPersonnelCapacity.getConfirmOperatingPersonnelId());
+        if (confirmUser == null) {
+            throw new ErrorException("纭浜轰笉瀛樺湪鎴栧凡琚垹闄わ紝鏃犳硶鎻愪氦");
+        }
+        if (personPersonnelCapacity.getId() != null) {
+            PersonPersonnelCapacity existing = this.getById(personPersonnelCapacity.getId());
+            if (existing == null) {
+                throw new ErrorException("浜哄憳鑳藉姏璁板綍涓嶅瓨鍦ㄦ垨宸茶鍒犻櫎");
+            }
+            if (existing.getConfirmDate() != null) {
+                throw new ErrorException("璇ヨ褰曞凡纭锛屼笉鑳介噸澶嶆彁浜�");
+            }
+        }
 
         Integer userId = SecurityUtils.getUserId().intValue();
         User user = userMapper.selectById(userId);
+        // 鎻愪氦鍜岀‘璁ゆ棩鏈熷垎寮�锛氱‘璁ゆ棩鏈熷彧鍦ㄧ‘璁ゆ搷浣滄椂鐢辩郴缁熷啓鍏�
+        personPersonnelCapacity.setConfirmDate(null);
+        this.saveOrUpdate(personPersonnelCapacity);
+
         // 娑堟伅鍙戦��
         InformationNotification info = new InformationNotification();
         // 鍙戦�佷汉
-        info.setCreateUser(user.getName());
+        info.setCreateUser(user != null ? user.getName() : "");
         info.setMessageType("6");
         info.setTheme("CNAS浜哄憳鑳藉姏纭閫氱煡");
         info.setContent(formUser.getName() + "鐨勪汉鍛樿兘鍔涘緟纭");
@@ -136,22 +176,21 @@
         info.setConsigneeId(personPersonnelCapacity.getConfirmOperatingPersonnelId());
         info.setJumpPath(MenuJumpPathConstants.PERSONNEL);
         informationNotificationService.addInformationNotification(info);
-        this.saveOrUpdate(personPersonnelCapacity);
 
         // 鍙戦�佷紒涓氬井淇¢�氱煡
         threadPoolTaskExecutor.execute(() -> {
-            // 鏌ヨ鎺ユ敹浜�
-            User personnel = userMapper.selectById(personPersonnelCapacity.getConfirmOperatingPersonnelId());
-
+            if (confirmUser.getAccount() == null || confirmUser.getAccount().trim().isEmpty()) {
+                return;
+            }
             String message = "";
             message += "CNAS浜哄憳鑳藉姏纭閫氱煡";
-            message += "\n璇峰幓璧勬簮绠$悊-浜哄憳-浜哄憳鑳藉姏濉啓";
+            message += "\n璇峰幓璧勬簮瑕佹眰-浜哄憳-浜哄憳绠$悊-浜哄憳鑳藉姏濉啓";
             message += "\n" + formUser.getName() + "鐨勪汉鍛樿兘鍔涘緟纭";
             //鍙戦�佷紒涓氬井淇℃秷鎭�氱煡
             try {
-                WxCpUtils.inform(personnel.getAccount(), message, null);
+                WxCpUtils.inform(confirmUser.getAccount(), message, null);
             } catch (Exception e) {
-                throw new RuntimeException(e);
+                e.printStackTrace();
             }
         });
 
diff --git a/cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonPostAuthorizationRecordServiceImpl.java b/cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonPostAuthorizationRecordServiceImpl.java
index 4f45be8..bd81697 100644
--- a/cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonPostAuthorizationRecordServiceImpl.java
+++ b/cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonPostAuthorizationRecordServiceImpl.java
@@ -2,6 +2,8 @@
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+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;
 import com.deepoove.poi.XWPFTemplate;
@@ -11,7 +13,9 @@
 import com.ruoyi.common.core.domain.entity.User;
 import com.ruoyi.framework.exception.ErrorException;
 import com.ruoyi.personnel.dto.PersonPostAuthorizationRecordDto;
+import com.ruoyi.personnel.mapper.PersonBasicInfoMapper;
 import com.ruoyi.personnel.mapper.PersonPostAuthorizationRecordMapper;
+import com.ruoyi.personnel.pojo.PersonBasicInfo;
 import com.ruoyi.personnel.pojo.PersonPostAuthorizationRecord;
 import com.ruoyi.personnel.service.PersonPostAuthorizationRecordService;
 import com.ruoyi.system.mapper.UserMapper;
@@ -20,6 +24,7 @@
 
 import jakarta.annotation.Resource;
 import jakarta.servlet.http.HttpServletResponse;
+import java.io.File;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.URLEncoder;
@@ -39,6 +44,8 @@
 
     @Resource
     private UserMapper userMapper;
+    @Resource
+    private PersonBasicInfoMapper personBasicInfoMapper;
 
     @Value("${file.path}")
     private String imgUrl;
@@ -50,73 +57,80 @@
 
     @Override
     public void exportPersonPostAuthorizationRecord(Integer id, HttpServletResponse response) {
+        if (id == null) {
+            throw new ErrorException("缂哄皯浠昏亴鎺堟潈璁板綍id");
+        }
         PersonPostAuthorizationRecord personPostAuthorizationRecord = baseMapper.selectById(id);
+        if (personPostAuthorizationRecord == null) {
+            throw new ErrorException("浠昏亴鎺堟潈璁板綍涓嶅瓨鍦ㄦ垨宸茶鍒犻櫎");
+        }
+        Integer incumbentUserId;
+        try {
+            incumbentUserId = Integer.valueOf(personPostAuthorizationRecord.getUserId());
+        } catch (Exception e) {
+            throw new ErrorException("璇ヨ褰曟病鏈夐�夋嫨琚换鑱屼汉鍛橈紝鏃犳硶瀵煎嚭涓婂矖璇�");
+        }
         //濮撳悕
-        User user = userMapper.selectById(personPostAuthorizationRecord.getUserId());
+        User user = userMapper.selectById(incumbentUserId);
+        if (user == null) {
+            throw new ErrorException("琚换鑱屼汉鍛樹笉瀛樺湪鎴栧凡琚垹闄わ紝鏃犳硶瀵煎嚭涓婂矖璇�");
+        }
         String name = user.getName();
-        //todo: 浜哄憳浠昏亴鎺堟潈璁板綍濮撳悕鑻辨枃
-//        HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
-//        format.setCaseType(HanyuPinyinCaseType.LOWERCASE);
-//        format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
-//        StringBuilder pinyinBuilder = new StringBuilder();
-//        for (int i = 0; i < name.length(); i++) {
-//            char c = name.charAt(i);
-//            // 鍒ゆ柇鏄惁鏄眽瀛�
-//            if (Character.toString(c).matches("[\\u4E00-\\u9FFF]")) {
-//                // 鑾峰彇姹夊瓧鐨勬嫾闊虫暟缁�
-//                String[] pinyinArray = new String[0];
-//                try {
-//                    pinyinArray = PinyinHelper.toHanyuPinyinStringArray(c, format);
-//                } catch (BadHanyuPinyinOutputFormatCombination badHanyuPinyinOutputFormatCombination) {
-//
-//                }
-//                if (pinyinArray!= null && pinyinArray.length > 0) {
-//                    pinyinBuilder.append(pinyinArray[0]);
-//                }
-//            } else {
-//                pinyinBuilder.append(c);
-//            }
-//        }
-//        String nameEn = pinyinBuilder.toString();
-        String nameEn = user.getNameEn();
-        //鐞嗚鐭ヨ瘑鑰冭瘯鎴愮哗
-        String num1=personPostAuthorizationRecord.getNum1();
-        //鎿嶄綔鎶�鑳借�冭瘯鎴愮哗
-        String num2=personPostAuthorizationRecord.getNum2();
+        //韬唤璇佸彿
+        PersonBasicInfo basicInfo = personBasicInfoMapper.selectOne(Wrappers.<PersonBasicInfo>lambdaQuery()
+                .eq(PersonBasicInfo::getUserId, incumbentUserId)
+                .last("limit 1"));
+        if (basicInfo == null || StringUtils.isBlank(basicInfo.getIdentityCard())) {
+            throw new ErrorException(name + "杩樻病鏈夊湪浜哄憳鍩烘湰淇℃伅涓~鍐欒韩浠借瘉鍙凤紝鏃犳硶瀵煎嚭涓婂矖璇�");
+        }
+        String identityCard = basicInfo.getIdentityCard();
         //璇佷功缂栧彿
         String code = personPostAuthorizationRecord.getCertificateNumber();
+        if (StringUtils.isBlank(code)) {
+            throw new ErrorException("璇ヨ褰曡繕娌℃湁濉啓璇佷功缂栧彿锛屾棤娉曞鍑轰笂宀楄瘉");
+        }
+        //浠昏亴宀椾綅
+        String post = personPostAuthorizationRecord.getPost();
+        String finalPost = StringUtils.isBlank(post) ? "妫�娴�" : post;
         //鍙戣瘉鏃堕棿
         LocalDateTime createTime = personPostAuthorizationRecord.getCreateTime();
+        if (createTime == null) {
+            createTime = LocalDateTime.now();
+        }
         String year = createTime.getYear() + "";
-        String mon = createTime.getMonth().getValue() + "";
+        String mon = createTime.getMonthValue() + "";
         String day = createTime.getDayOfMonth() + "";
         //涓汉鐓х墖
         if (ObjectUtils.isEmpty(user.getPictureUrl())) {
-            throw new ErrorException(name+"鐨勪釜浜虹収鐗囨病鏈変笂浼�");
+            throw new ErrorException(name + "鐨勪釜浜虹収鐗囨病鏈変笂浼狅紝鏃犳硶瀵煎嚭涓婂矖璇�");
         }
         String pictureUrl = user.getPictureUrl();
-
+        if (!new File(imgUrl + "/" + pictureUrl).exists()) {
+            throw new ErrorException(name + "鐨勪釜浜虹収鐗囨枃浠朵笉瀛樺湪锛岃閲嶆柊涓婁紶");
+        }
 
         InputStream inputStream = this.getClass().getResourceAsStream("/static/credentials-deal.docx");
+        if (inputStream == null) {
+            throw new ErrorException("涓婂矖璇佸鍑烘ā鏉夸笉瀛樺湪");
+        }
         ConfigureBuilder builder = Configure.builder();
         builder.useSpringEL(true);
         XWPFTemplate template = XWPFTemplate.compile(inputStream, builder.build()).render(
                 new HashMap<String, Object>() {{
                     put("name", name);
-                    put("nameEn", nameEn);
-                    put("num1", num1);
-                    put("num2", num2);
+                    put("identityCard", identityCard);
                     put("code", code);
+                    put("post", finalPost);
                     put("year", year);
                     put("mon", mon);
                     put("day", day);
-                    put("writeUrl", new FilePictureRenderData(100,50,imgUrl + "/" + pictureUrl));
+                    put("writeUrl", new FilePictureRenderData(100, 140, imgUrl + "/" + pictureUrl));
                 }});
 
         try {
             response.setContentType("application/msword");
             String fileName = URLEncoder.encode(
-                    name+"鐨勫矖浣嶈亴涓氳祫鏍兼寮�", "UTF-8");
+                    name + "鐨勬娴嬩汉鍛樹笂宀楄瘉", "UTF-8");
             response.setHeader("Content-disposition",
                     "attachment;filename=" + fileName + ".docx");
             OutputStream os = response.getOutputStream();
diff --git a/cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonTrainingRecordServiceImpl.java b/cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonTrainingRecordServiceImpl.java
index badf239..d67d453 100644
--- a/cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonTrainingRecordServiceImpl.java
+++ b/cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonTrainingRecordServiceImpl.java
@@ -11,6 +11,7 @@
 import com.ruoyi.common.core.domain.entity.User;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.WxCpUtils;
+import com.ruoyi.framework.exception.ErrorException;
 import com.ruoyi.inspect.util.HackLoopTableRenderPolicy;
 import com.ruoyi.personnel.dto.PersonTrainingRecordDto;
 import com.ruoyi.personnel.dto.PersonTrainingRecordListDto;
@@ -90,8 +91,14 @@
 
     @Override
     public void exportTrainingRecordAddTrainingDate(Integer userId, Integer trainingDate, HttpServletResponse response) {
+        if (userId == null) {
+            throw new ErrorException("缂哄皯浜哄憳id");
+        }
         // 鏌ヨ浜哄憳浜轰俊鎭�
         PersonTrainingRecordListDto trainingRecordListDto = baseMapper.selectUserTraining(userId);
+        if (trainingRecordListDto == null) {
+            throw new ErrorException("浜哄憳涓嶅瓨鍦ㄦ垨宸茶鍒犻櫎锛屾棤娉曞鍑�");
+        }
 
         // 鏌ヨ鍩硅璁板綍
         List<TrainingRecordPersonDetailedDto> personDetailedDtos = baseMapper.selectPersonDetailedDtosByTrainingDate(userId, trainingDate);
@@ -99,6 +106,9 @@
 
         // 鑾峰彇璺緞
         InputStream inputStream = this.getClass().getResourceAsStream("/static/training-record.docx");
+        if (inputStream == null) {
+            throw new ErrorException("鍩硅璁板綍瀵煎嚭妯℃澘涓嶅瓨鍦�");
+        }
         Configure configure = Configure.builder()
                 .bind("personnelDetailsLisat", new HackLoopTableRenderPolicy())
                 .build();
@@ -130,6 +140,21 @@
      */
     @Override
     public void trainingAndAssessmentRecordsAdded(PersonTrainingRecordSubmitDto personTrainingRecordSubmitDto) {
+        if (personTrainingRecordSubmitDto == null || personTrainingRecordSubmitDto.getTrainingDetailedId() == null) {
+            throw new ErrorException("缂哄皯鍩硅鏄庣粏id");
+        }
+        if (personTrainingRecordSubmitDto.getState() == null) {
+            throw new ErrorException("缂哄皯鍩硅鐘舵��");
+        }
+        PersonTrainingDetailed personPersonnelCapacity = personTrainingDetailedService.getById(personTrainingRecordSubmitDto.getTrainingDetailedId());
+        if (personPersonnelCapacity == null) {
+            throw new ErrorException("鍩硅璁板綍涓嶅瓨鍦ㄦ垨宸茶鍒犻櫎");
+        }
+        boolean waitEvaluate = Integer.valueOf(2).equals(personTrainingRecordSubmitDto.getState());
+        if (waitEvaluate && personTrainingRecordSubmitDto.getAssessmentUserId() == null) {
+            throw new ErrorException("璇烽�夋嫨璇勪环浜�");
+        }
+
         personTrainingDetailedService.update(Wrappers.<PersonTrainingDetailed>lambdaUpdate()
                 .eq(PersonTrainingDetailed::getId, personTrainingRecordSubmitDto.getTrainingDetailedId())
                 .set(PersonTrainingDetailed::getAssessmentMethod, personTrainingRecordSubmitDto.getAssessmentMethod())
@@ -138,42 +163,44 @@
                 .set(PersonTrainingDetailed::getAssessmentUserId, personTrainingRecordSubmitDto.getAssessmentUserId())
                 .set(PersonTrainingDetailed::getState, personTrainingRecordSubmitDto.getState()));
 
-        // 鍙戦�佹秷鎭�氱煡
-        if (personTrainingRecordSubmitDto.getState().equals(2)) {
-            PersonTrainingDetailed personPersonnelCapacity = personTrainingDetailedService.getById(personTrainingRecordSubmitDto.getTrainingDetailedId());
-
-            Integer userId = SecurityUtils.getUserId().intValue();
-            User user = userMapper.selectById(userId);
-            // 娑堟伅鍙戦��
-            InformationNotification info = new InformationNotification();
-            // 鍙戦�佷汉
-            info.setCreateUser(user.getName());
-            info.setMessageType("6");
-            info.setTheme("CNAS浜哄憳鍩硅璁″垝寰呰瘎浠�");
-            info.setContent("鍩硅鍐呭:" + personPersonnelCapacity.getTrainingContent() + "鐨勪汉鍛樺煿璁緟璇勪环");
-            info.setSenderId(userId);
-            // 鎺ユ敹浜�
-            info.setConsigneeId(personTrainingRecordSubmitDto.getAssessmentUserId());
-            info.setJumpPath(MenuJumpPathConstants.PERSONNEL);
-            informationNotificationService.addInformationNotification(info);
-
-            // 鍙戦�佷紒涓氬井淇¢�氱煡
-            threadPoolTaskExecutor.execute(() -> {
-                // 鏌ヨ鎺ユ敹浜�
-                User personnel = userMapper.selectById(personTrainingRecordSubmitDto.getAssessmentUserId());
-
-                String message = "";
-                message += "CNAS浜哄憳鍩硅璁″垝寰呰瘎浠�";
-                message += "\n璇峰幓璧勬簮绠$悊-浜哄憳-浜哄憳鍩硅璁″垝";
-                message += "\n" + "鍩硅鍐呭:" + personPersonnelCapacity.getTrainingContent() + "鐨勪汉鍛樺煿璁緟璇勪环";
-                //鍙戦�佷紒涓氬井淇℃秷鎭�氱煡
-                try {
-                    WxCpUtils.inform(personnel.getAccount(), message, null);
-                } catch (Exception e) {
-                    throw new RuntimeException(e);
-                }
-            });
+        // 鍙湁鎻愪氦寰呰瘎浠锋椂鎵嶉�氱煡璇勪环浜�
+        if (!waitEvaluate) {
+            return;
         }
+
+        Integer userId = SecurityUtils.getUserId().intValue();
+        User user = userMapper.selectById(userId);
+        // 娑堟伅鍙戦��
+        InformationNotification info = new InformationNotification();
+        // 鍙戦�佷汉
+        info.setCreateUser(user != null ? user.getName() : "");
+        info.setMessageType("6");
+        info.setTheme("CNAS浜哄憳鍩硅璁″垝寰呰瘎浠�");
+        info.setContent("鍩硅鍐呭:" + personPersonnelCapacity.getTrainingContent() + "鐨勪汉鍛樺煿璁緟璇勪环");
+        info.setSenderId(userId);
+        // 鎺ユ敹浜�
+        info.setConsigneeId(personTrainingRecordSubmitDto.getAssessmentUserId());
+        info.setJumpPath(MenuJumpPathConstants.PERSONNEL);
+        informationNotificationService.addInformationNotification(info);
+
+        // 鍙戦�佷紒涓氬井淇¢�氱煡
+        threadPoolTaskExecutor.execute(() -> {
+            // 鏌ヨ鎺ユ敹浜�
+            User personnel = userMapper.selectById(personTrainingRecordSubmitDto.getAssessmentUserId());
+            if (personnel == null || personnel.getAccount() == null || personnel.getAccount().trim().isEmpty()) {
+                return;
+            }
+            String message = "";
+            message += "CNAS浜哄憳鍩硅璁″垝寰呰瘎浠�";
+            message += "\n璇峰幓璧勬簮绠$悊-浜哄憳-浜哄憳鍩硅璁″垝";
+            message += "\n" + "鍩硅鍐呭:" + personPersonnelCapacity.getTrainingContent() + "鐨勪汉鍛樺煿璁緟璇勪环";
+            //鍙戦�佷紒涓氬井淇℃秷鎭�氱煡
+            try {
+                WxCpUtils.inform(personnel.getAccount(), message, null);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        });
     }
 
 }
diff --git a/cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonTrainingServiceImpl.java b/cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonTrainingServiceImpl.java
index 8a6bd95..09d26e6 100644
--- a/cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonTrainingServiceImpl.java
+++ b/cnas-personnel/src/main/java/com/ruoyi/personnel/service/impl/PersonTrainingServiceImpl.java
@@ -33,6 +33,7 @@
 import com.ruoyi.personnel.pojo.PersonTraining;
 import com.ruoyi.personnel.pojo.PersonTrainingDetailed;
 import com.ruoyi.personnel.pojo.PersonTrainingDetailedFile;
+import com.ruoyi.personnel.pojo.PersonTrainingRecord;
 import com.ruoyi.personnel.service.PersonTrainingDetailedService;
 import com.ruoyi.personnel.service.PersonTrainingService;
 import com.ruoyi.system.mapper.UserMapper;
@@ -57,6 +58,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Optional;
+import java.util.stream.Collectors;
 
 /**
  * <p>
@@ -98,50 +100,32 @@
 
     @Override
     public void personTrainingImport(MultipartFile file, PersonTraining training) {
+        if (file == null || file.isEmpty()) {
+            throw new ErrorException("璇烽�夋嫨瀵煎叆鏂囦欢");
+        }
+        if (training == null || StringUtils.isBlank(training.getPlanYear())) {
+            throw new ErrorException("璇烽�夋嫨璁″垝骞翠唤");
+        }
+        if (training.getReviewerId() == null) {
+            throw new ErrorException("璇烽�夋嫨瀹℃牳浜�");
+        }
+        String originalFilename = file.getOriginalFilename();
+        if (StringUtils.isBlank(originalFilename)) {
+            throw new ErrorException("鏂囦欢鍚嶄笉鑳戒负绌�");
+        }
 
         Integer userId = SecurityUtils.getUserId().intValue();
         // 骞村害璁″垝鐖剁骇鏂板鏁版嵁
         PersonTraining personSupervisePlan = new PersonTraining();
-        String fileName = file.getOriginalFilename().substring(0, file.getOriginalFilename().lastIndexOf("."));
+        String fileName = originalFilename.contains(".")
+                ? originalFilename.substring(0, originalFilename.lastIndexOf("."))
+                : originalFilename;
         personSupervisePlan.setFileName(fileName);
         personSupervisePlan.setPlanYear(training.getPlanYear());
         personSupervisePlan.setCompilerId(userId);
         personSupervisePlan.setReviewerId(training.getReviewerId());
         personSupervisePlan.setCompilationDate(LocalDateTime.now());
         baseMapper.insert(personSupervisePlan);
-        User user = userMapper.selectById(userId);
-        // 娑堟伅鍙戦��
-        InformationNotification info = new InformationNotification();
-        // 鍙戦�佷汉
-        info.setCreateUser(user.getName());
-        info.setMessageType("6");
-        info.setTheme("CNAS鍩硅璁″垝瀹℃牳閫氱煡");
-        info.setContent("鎮ㄦ湁涓�鏉″煿璁鍒掑緟瀹℃牳");
-        info.setSenderId(userId);
-        // 鎺ユ敹浜�
-        info.setConsigneeId(training.getApproverId());
-        info.setJumpPath(MenuJumpPathConstants.PERSONNEL);
-        informationNotificationService.addInformationNotification(info);
-
-        // 鍙戦�佷紒涓氬井淇¢�氱煡
-        threadPoolTaskExecutor.execute(() -> {
-            // 鏌ヨ鎺ユ敹浜�
-            User personnel = userMapper.selectById(training.getApproverId());
-
-            String message = "";
-            message += "CNAS鍩硅璁″垝瀹℃牳閫氱煡";
-            message += "\n璇峰幓璧勬簮绠$悊-浜哄憳-鍩硅璁″垝濉啓";
-            message += "\n" + fileName + "鐨勫煿璁鍒掑緟瀹℃牳";
-            //鍙戦�佷紒涓氬井淇℃秷鎭�氱煡锛堟帴鏀朵汉涓嶅瓨鍦ㄦ垨鍙戦�佸け璐ヤ笉褰卞搷瀵煎叆娴佺▼锛�
-            if (personnel == null || StringUtils.isBlank(personnel.getAccount())) {
-                return;
-            }
-            try {
-                WxCpUtils.inform(personnel.getAccount(), message, null);
-            } catch (Exception e) {
-                e.printStackTrace();
-            }
-        });
 
         // 骞村害璁″垝璇︽儏 鏂板
         try {
@@ -149,65 +133,138 @@
             personSupervisePlanDetailsListener.setPlanId(personSupervisePlan.getId());
             EasyExcel.read(file.getInputStream(), PersonTrainingDetailedUpload.class, personSupervisePlanDetailsListener).sheet().doRead();
         } catch (IOException e) {
-            e.printStackTrace();
+            throw new ErrorException("璇诲彇瀵煎叆鏂囦欢澶辫触锛岃妫�鏌ユ枃浠舵牸寮�");
         }
+
+        User user = userMapper.selectById(userId);
+        // 娑堟伅鍙戦�侊紙鎺ユ敹浜轰负閫夊畾鐨勫鏍镐汉锛�
+        InformationNotification info = new InformationNotification();
+        info.setCreateUser(user != null ? user.getName() : "");
+        info.setMessageType("6");
+        info.setTheme("CNAS鍩硅璁″垝瀹℃牳閫氱煡");
+        info.setContent("鎮ㄦ湁涓�鏉″煿璁鍒掑緟瀹℃牳");
+        info.setSenderId(userId);
+        info.setConsigneeId(training.getReviewerId());
+        info.setJumpPath(MenuJumpPathConstants.PERSONNEL);
+        informationNotificationService.addInformationNotification(info);
+
+        // 鍙戦�佷紒涓氬井淇¢�氱煡锛堟帴鏀朵汉涓嶅瓨鍦ㄦ垨鍙戦�佸け璐ヤ笉褰卞搷瀵煎叆娴佺▼锛�
+        threadPoolTaskExecutor.execute(() -> {
+            User personnel = userMapper.selectById(training.getReviewerId());
+            if (personnel == null || StringUtils.isBlank(personnel.getAccount())) {
+                return;
+            }
+            String message = "";
+            message += "CNAS鍩硅璁″垝瀹℃牳閫氱煡";
+            message += "\n璇峰幓璧勬簮绠$悊-浜哄憳-鍩硅璁″垝濉啓";
+            message += "\n" + fileName + "鐨勫煿璁鍒掑緟瀹℃牳";
+            try {
+                WxCpUtils.inform(personnel.getAccount(), message, null);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        });
     }
 
     @Override
     public void personTrainingDelete(Integer id) {
-        personTrainingDetailedService.remove(Wrappers.<PersonTrainingDetailed>lambdaQuery()
+        if (id == null) {
+            throw new ErrorException("缂哄皯璁″垝id");
+        }
+        List<PersonTrainingDetailed> details = personTrainingDetailedService.list(Wrappers.<PersonTrainingDetailed>lambdaQuery()
                 .eq(PersonTrainingDetailed::getPlanId, id));
+        if (!details.isEmpty()) {
+            List<Integer> detailIds = details.stream().map(PersonTrainingDetailed::getId).collect(Collectors.toList());
+            // 杩炲悓鏄庣粏涓嬬殑鍩硅璁板綍銆侀檮浠朵竴璧峰垹闄わ紝閬垮厤鐣欎笅瀛ゅ効鏁版嵁
+            personTrainingRecordMapper.delete(Wrappers.<PersonTrainingRecord>lambdaQuery()
+                    .in(PersonTrainingRecord::getCourseId, detailIds));
+            personTrainingDetailedFileMapper.delete(Wrappers.<PersonTrainingDetailedFile>lambdaQuery()
+                    .in(PersonTrainingDetailedFile::getTrainingDetailedId, detailIds));
+            personTrainingDetailedService.remove(Wrappers.<PersonTrainingDetailed>lambdaQuery()
+                    .eq(PersonTrainingDetailed::getPlanId, id));
+        }
         baseMapper.deleteById(id);
     }
 
     @Override
     public void reviewAnnualPersonnelTraining(PersonTraining training) {
-        PersonTraining personTraining = new PersonTraining();
-        personTraining.setId(training.getId());
-        personTraining.setApproverId(training.getApproverId());// 娣诲姞鎵瑰噯浜�
-        personTraining.setAuditDate(LocalDateTime.now());
-        personTraining.setAuditRemarks(training.getAuditRemarks());
-        personTraining.setReviewerStatus(training.getReviewerStatus());
-
+        if (training == null || training.getId() == null) {
+            throw new ErrorException("缂哄皯璁″垝id");
+        }
+        if (!Integer.valueOf(1).equals(training.getReviewerStatus()) && !Integer.valueOf(2).equals(training.getReviewerStatus())) {
+            throw new ErrorException("瀹℃牳缁撴灉涓嶆纭�");
+        }
         PersonTraining old = baseMapper.selectById(training.getId());
+        if (old == null) {
+            throw new ErrorException("鍩硅璁″垝涓嶅瓨鍦ㄦ垨宸茶鍒犻櫎");
+        }
+        if (Integer.valueOf(1).equals(old.getReviewerStatus())) {
+            throw new ErrorException("璇ヨ鍒掑凡瀹℃牳閫氳繃锛岃鍕块噸澶嶅鏍�");
+        }
+        boolean passed = Integer.valueOf(1).equals(training.getReviewerStatus());
+        if (passed && training.getApproverId() == null) {
+            throw new ErrorException("璇烽�夋嫨鎵瑰噯浜�");
+        }
 
-        // 娑堟伅鍙戦��
+        baseMapper.update(null, Wrappers.<PersonTraining>lambdaUpdate()
+                .eq(PersonTraining::getId, training.getId())
+                .set(PersonTraining::getApproverId, passed ? training.getApproverId() : null)
+                .set(PersonTraining::getAuditDate, LocalDateTime.now())
+                .set(PersonTraining::getAuditRemarks, training.getAuditRemarks())
+                .set(PersonTraining::getReviewerStatus, training.getReviewerStatus()));
+
+        // 鍙湁瀹℃牳閫氳繃鎵嶉�氱煡鎵瑰噯浜�
+        if (!passed) {
+            return;
+        }
         Integer userId = SecurityUtils.getUserId().intValue();
         User user = userMapper.selectById(userId);
         InformationNotification info = new InformationNotification();
-        // 鍙戦�佷汉
-        info.setCreateUser(user.getName());
+        info.setCreateUser(user != null ? user.getName() : "");
         info.setMessageType("6");
         info.setTheme("CNAS鍩硅璁″垝瀹℃牳閫氱煡");
         info.setContent("鎮ㄦ湁涓�鏉″煿璁鍒掑緟鎵瑰噯");
         info.setSenderId(userId);
-        // 鎺ユ敹浜�
         info.setConsigneeId(training.getApproverId());
         info.setJumpPath(MenuJumpPathConstants.PERSONNEL);
         informationNotificationService.addInformationNotification(info);
 
         // 鍙戦�佷紒涓氬井淇¢�氱煡
         threadPoolTaskExecutor.execute(() -> {
-            // 鏌ヨ鎺ユ敹浜�
             User personnel = userMapper.selectById(training.getApproverId());
-
+            if (personnel == null || StringUtils.isBlank(personnel.getAccount())) {
+                return;
+            }
             String message = "";
             message += "CNAS鍩硅璁″垝鎵瑰噯閫氱煡";
             message += "\n璇峰幓璧勬簮绠$悊-浜哄憳-鍩硅璁″垝濉啓";
             message += "\n" + old.getFileName() + "鐨勫煿璁鍒掑緟鎵瑰噯";
-            //鍙戦�佷紒涓氬井淇℃秷鎭�氱煡
             try {
                 WxCpUtils.inform(personnel.getAccount(), message, null);
             } catch (Exception e) {
-                throw new RuntimeException(e);
+                e.printStackTrace();
             }
         });
-
-        baseMapper.updateById(personTraining);
     }
 
     @Override
     public void approveAnnualPersonnelTraining(PersonTraining training) {
+        if (training == null || training.getId() == null) {
+            throw new ErrorException("缂哄皯璁″垝id");
+        }
+        if (!Integer.valueOf(1).equals(training.getApprovalStatus()) && !Integer.valueOf(2).equals(training.getApprovalStatus())) {
+            throw new ErrorException("鎵瑰噯缁撴灉涓嶆纭�");
+        }
+        PersonTraining old = baseMapper.selectById(training.getId());
+        if (old == null) {
+            throw new ErrorException("鍩硅璁″垝涓嶅瓨鍦ㄦ垨宸茶鍒犻櫎");
+        }
+        if (!Integer.valueOf(1).equals(old.getReviewerStatus())) {
+            throw new ErrorException("璇ヨ鍒掑皻鏈鏍搁�氳繃锛屼笉鑳芥壒鍑�");
+        }
+        if (Integer.valueOf(1).equals(old.getApprovalStatus())) {
+            throw new ErrorException("璇ヨ鍒掑凡鎵瑰噯锛岃鍕块噸澶嶆搷浣�");
+        }
         LambdaUpdateWrapper<PersonTraining> wrapper = Wrappers.<PersonTraining>lambdaUpdate()
                 .eq(PersonTraining::getId, training.getId())
                 .set(PersonTraining::getApprovalDate, LocalDateTime.now())
@@ -224,30 +281,48 @@
     @Override
     public void exportPersonTraining(Integer id, HttpServletResponse response) {
 
+        if (id == null) {
+            throw new ErrorException("缂哄皯璁″垝id");
+        }
         // 鏌ヨ璇︽儏
         PersonTraining personTraining = baseMapper.selectById(id);
-
-        //鑾峰彇鎻愪氦浜虹殑绛惧悕鍦板潃
-        String writeUrl = userMapper.selectById(personTraining.getCompilerId()).getSignatureUrl();
-        if (ObjectUtils.isEmpty(writeUrl) || writeUrl.equals("")) {
-            throw new ErrorException("鎵句笉鍒版楠屼汉鐨勭鍚�");
+        if (personTraining == null) {
+            throw new ErrorException("鍩硅璁″垝涓嶅瓨鍦ㄦ垨宸茶鍒犻櫎");
         }
 
-        //鑾峰彇澶嶆牳浜虹殑绛惧悕鍦板潃
+        //鑾峰彇缂栧埗浜虹殑绛惧悕鍦板潃
+        User compiler = userMapper.selectById(personTraining.getCompilerId());
+        String writeUrl = compiler != null ? compiler.getSignatureUrl() : null;
+        if (StringUtils.isBlank(writeUrl)) {
+            throw new ErrorException("缂栧埗浜烘湭涓婁紶绛惧悕锛屾殏鏃犳硶瀵煎嚭");
+        }
+        if (!new File(imgUrl + "/" + writeUrl).exists()) {
+            throw new ErrorException("缂栧埗浜虹殑绛惧悕鍥剧墖涓嶅瓨鍦紝璇烽噸鏂颁笂浼犵鍚�");
+        }
+
+        //鑾峰彇瀹℃牳浜虹殑绛惧悕鍦板潃
         String examineUrl = null;
         if (personTraining.getReviewerId() != null) {
-            examineUrl = userMapper.selectById(personTraining.getReviewerId()).getSignatureUrl();
+            User reviewer = userMapper.selectById(personTraining.getReviewerId());
+            examineUrl = reviewer != null ? reviewer.getSignatureUrl() : null;
             if (StringUtils.isBlank(examineUrl)) {
-                throw new ErrorException("鎵句笉鍒板鏍镐汉鐨勭鍚�");
+                throw new ErrorException("瀹℃牳浜烘湭涓婁紶绛惧悕锛屾殏鏃犳硶瀵煎嚭");
+            }
+            if (!new File(imgUrl + "/" + examineUrl).exists()) {
+                throw new ErrorException("瀹℃牳浜虹殑绛惧悕鍥剧墖涓嶅瓨鍦紝璇烽噸鏂颁笂浼犵鍚�");
             }
         }
 
         //鑾峰彇鎵瑰噯浜虹殑绛惧悕鍦板潃
         String ratifyUrl = null;
         if (personTraining.getApproverId() != null) {
-            ratifyUrl = userMapper.selectById(personTraining.getApproverId()).getSignatureUrl();
+            User approver = userMapper.selectById(personTraining.getApproverId());
+            ratifyUrl = approver != null ? approver.getSignatureUrl() : null;
             if (StringUtils.isBlank(ratifyUrl)) {
-                throw new ErrorException("鎵句笉鍒板鏍镐汉鐨勭鍚�");
+                throw new ErrorException("鎵瑰噯浜烘湭涓婁紶绛惧悕锛屾殏鏃犳硶瀵煎嚭");
+            }
+            if (!new File(imgUrl + "/" + ratifyUrl).exists()) {
+                throw new ErrorException("鎵瑰噯浜虹殑绛惧悕鍥剧墖涓嶅瓨鍦紝璇烽噸鏂颁笂浼犵鍚�");
             }
         }
 
@@ -307,8 +382,14 @@
      */
     @Override
     public void exportPersonTrainingRecord(Integer id, HttpServletResponse response) {
+        if (id == null) {
+            throw new ErrorException("缂哄皯鍩硅鏄庣粏id");
+        }
         // 鏌ヨ浜哄憳鍩硅鏄庣粏
         PersonTrainingDetailedDto detailedDto = personTrainingDetailedMapper.selectTrainingDetail(id);
+        if (detailedDto == null) {
+            throw new ErrorException("鍩硅璁板綍涓嶅瓨鍦ㄦ垨宸茶鍒犻櫎");
+        }
 
         // 鐢熸垚鍩硅鑰佸笀绛惧悕锛堟湭閰嶇疆绛惧悕鏃剁敤閫忔槑鍥剧墖鍏滃簳锛屾ā鏉块噷鐨勫浘鐗囧崰浣嶄笉鑳戒负 null锛�
         detailedDto.setTrainingLecturerRender(UserUtils.orTransparentPicture(
diff --git a/cnas-personnel/src/main/resources/mapper/PersonBasicInfoMapper.xml b/cnas-personnel/src/main/resources/mapper/PersonBasicInfoMapper.xml
index da32169..9a3f953 100644
--- a/cnas-personnel/src/main/resources/mapper/PersonBasicInfoMapper.xml
+++ b/cnas-personnel/src/main/resources/mapper/PersonBasicInfoMapper.xml
@@ -5,13 +5,55 @@
     <select id="selectLimsUser" resultType="com.ruoyi.common.core.domain.entity.DepartmentDto">
         SELECT u.id userId, u.name, SUBSTRING_INDEX(SUBSTRING_INDEX(u.depart_lims_id, ',', -2), ',', 1) AS fatherId
         FROM user u
-        where u.is_custom = 0
+        where COALESCE(u.is_custom, 0) = 0
+          and COALESCE(u.del_flag, '0') = '0'
           and u.depart_lims_id is not null
           and u.depart_lims_id != ''
     </select>
 
     <select id="getCNASPersonnelInfo" resultType="com.ruoyi.personnel.dto.PersonBasicInfoDto">
-        SELECT *
+        SELECT cpbi.id                 AS id,
+               u.id                    AS userId,
+               u.account               AS account,
+               u.name                  AS name,
+               u.name_en               AS nameEn,
+               u.age                   AS age,
+               u.phone                 AS phone,
+               u.email                 AS email,
+               u.signature_url         AS signatureUrl,
+               u.picture_url           AS pictureUrl,
+               u.depart_lims_id        AS departLimsId,
+               cpbi.current_state      AS currentState,
+               cpbi.professional_title AS professionalTitle,
+               cpbi.sex                AS sex,
+               cpbi.personnel_classification AS personnelClassification,
+               cpbi.date_birth         AS dateBirth,
+               cpbi.identity_card      AS identityCard,
+               cpbi.nation             AS nation,
+               cpbi.political_status   AS politicalStatus,
+               cpbi.official_academic_redentials AS officialAcademicRedentials,
+               cpbi.graduation_time1   AS graduationTime1,
+               cpbi.graduated_institutions1 AS graduatedInstitutions1,
+               cpbi.major1             AS major1,
+               cpbi.graduation_time2   AS graduationTime2,
+               cpbi.graduated_institutions2 AS graduatedInstitutions2,
+               cpbi.major2             AS major2,
+               cpbi.telephone          AS telephone,
+               cpbi.remarks            AS remarks,
+               cpbi.corporate_name     AS corporateName,
+               cpbi.post_name          AS postName,
+               cpbi.group_time         AS groupTime,
+               cpbi.labor_relations    AS laborRelations,
+               cpbi.working_time       AS workingTime,
+               cpbi.contract_life_time AS contractLifeTime,
+               cpbi.native_place       AS nativePlace,
+               cpbi.validity_period    AS validityPeriod,
+               cpbi.marital_status     AS maritalStatus,
+               cpbi.id_address         AS idAddress,
+               cpbi.id_detail_address  AS idDetailAddress,
+               cpbi.dumpling_time      AS dumplingTime,
+               cpbi.highest_degree     AS highestDegree,
+               cpbi.last_update_time   AS lastUpdateTime
         FROM user u
                  left join cnas_person_basic_info cpbi on cpbi.user_id = u.id
         where u.id = #{userId}
@@ -36,10 +78,14 @@
         from user u
                  left join cnas_person_basic_info cpbi on cpbi.user_id = u.id
         <where>
-            FIND_IN_SET(#{departmentId},u.depart_lims_id)
+            <if test="departmentId != null">
+                FIND_IN_SET(#{departmentId},u.depart_lims_id)
+            </if>
             <if test="name != null and name != ''">
                 and u.name like concat('%',#{name},'%')
             </if>
+            and COALESCE(u.is_custom, 0) = 0
+            and COALESCE(u.del_flag, '0') = '0'
         </where>
     </select>
 
diff --git a/cnas-personnel/src/main/resources/mapper/PersonPersonnelCapacityMapper.xml b/cnas-personnel/src/main/resources/mapper/PersonPersonnelCapacityMapper.xml
index a4d9133..fe4dc5f 100644
--- a/cnas-personnel/src/main/resources/mapper/PersonPersonnelCapacityMapper.xml
+++ b/cnas-personnel/src/main/resources/mapper/PersonPersonnelCapacityMapper.xml
@@ -32,6 +32,7 @@
                 and u3.name like concat('%', #{userName}, '%')
             </if>
         </where>
+        order by cppc.id desc
     </select>
 
     <!-- 鏌ヨ浜哄憳鑳藉姏鎺ュ彛 -->
diff --git a/cnas-personnel/src/main/resources/mapper/PersonTrainingRecordMapper.xml b/cnas-personnel/src/main/resources/mapper/PersonTrainingRecordMapper.xml
index 88eabe4..4b5d06b 100644
--- a/cnas-personnel/src/main/resources/mapper/PersonTrainingRecordMapper.xml
+++ b/cnas-personnel/src/main/resources/mapper/PersonTrainingRecordMapper.xml
@@ -14,11 +14,13 @@
 
     <select id="personnelTrainingPersonnel" resultType="com.ruoyi.personnel.dto.PersonTrainingRecordListDto">
         select u.name, u.account, dl.name depart_lims_name, cpbi.professional_title,
-        cpbi.official_academic_redentials, u.id user_id
+        cpbi.official_academic_redentials, u.id user_id,
+        DATE_FORMAT(cpbi.group_time, '%Y-%m-%d') AS unitTimeSting
         from user u
         left join cnas_person_basic_info cpbi on cpbi.user_id = u.id
         left join department_lims dl on dl.id = SUBSTRING_INDEX(SUBSTRING_INDEX(u.depart_lims_id, ',', -2), ',', 1)
-        where u.is_custom = 0
+        where COALESCE(u.is_custom, 0) = 0
+          and COALESCE(u.del_flag, '0') = '0'
         <if test="userName != '' and userName != null and userName != 'null'">
             and u.name like concat('%', #{userName}, '%')
         </if>
@@ -66,7 +68,7 @@
         from user u
                  left join cnas_person_basic_info cpbi on cpbi.user_id = u.id
                  left join department_lims dl on dl.id = SUBSTRING_INDEX(SUBSTRING_INDEX(u.depart_lims_id, ',', -2), ',', 1)
-        where u.is_custom = 0
+        where COALESCE(u.is_custom, 0) = 0
         and u.id = #{userId}
     </select>
 
@@ -83,7 +85,7 @@
             and cptr.user_id = #{userId}
         <where>
             <if test="year!= null and year!= ''">
-                and cptd.training_date like concat('%', #{year}, '%')}
+                and cptd.training_date like concat('%', #{year}, '%')
             </if>
         </where>
     </select>
@@ -121,7 +123,7 @@
                 and cptd.training_date like concat('%', #{year}, '%')
             </if>
         </where>
-        order by STR_TO_DATE(CONCAT(cptd.training_date, '.01'), '%Y.%m.%d')
+        order by cptd.training_date
 
     </select>
 </mapper>
diff --git a/cnas-personnel/src/main/resources/static/credentials-deal.docx b/cnas-personnel/src/main/resources/static/credentials-deal.docx
index 2fda5fd..5d69caf 100644
--- a/cnas-personnel/src/main/resources/static/credentials-deal.docx
+++ b/cnas-personnel/src/main/resources/static/credentials-deal.docx
Binary files differ
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderPlanServiceImpl.java b/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderPlanServiceImpl.java
index 74f8661..a141ab0 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderPlanServiceImpl.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderPlanServiceImpl.java
@@ -1035,7 +1035,8 @@
 
     /**
      * 鍘熷璁板綍淇濆瓨鍚庯紝鎸夊綋鍓嶆ā鏉块�夌敤鐨勮澶囩敓鎴愬緟濉啓鐨勪娇鐢ㄨ褰曘��
-     * 鍚屼竴璁㈠崟銆佹牱鍝併�佽澶囧彧鐢熸垚涓�鏉★紱宸茬粡濉啓杩囩殑璁板綍涓嶄細琚鐩栥��
+     * 鍚屼竴璁㈠崟銆佹牱鍝併�佽澶囧彧鐢熸垚涓�鏉★紱娓╂箍搴﹀拰浣跨敤鏃ユ湡鍙栧師濮嬭褰曞揩鐓э紝
+     * 鏃堕棿榛樿 10:00-19:30锛涘凡缁忔墜宸ュ~鍐欒繃鐨勮褰曚笉浼氳瑕嗙洊銆�
      */
     private synchronized void syncDeviceRecordsFromTemplate(Integer orderId, Integer sampleId,
                                                             Integer templateId, Integer userId) {
@@ -1052,6 +1053,24 @@
         if (CollectionUtils.isEmpty(deviceIds)) {
             return;
         }
+        InsOrderStandardTemplate snapshot = insOrderStandardTemplateService.getOne(
+                Wrappers.<InsOrderStandardTemplate>lambdaQuery()
+                        .eq(InsOrderStandardTemplate::getInsOrderId, orderId)
+                        .eq(InsOrderStandardTemplate::getTemplateId, templateId)
+                        .last("limit 1"));
+        String temperature = snapshot == null || StringUtils.isBlank(snapshot.getTemperature())
+                ? order.getTemperature() : snapshot.getTemperature();
+        String humidity = snapshot == null || StringUtils.isBlank(snapshot.getHumidity())
+                ? order.getHumidity() : snapshot.getHumidity();
+        // 鍘熷璁板綍娌℃湁妫�娴嬫棩鏈熸椂鐣欑┖锛屼笉榛樿褰撳ぉ
+        LocalDateTime useStartDate = null;
+        LocalDateTime useEndDate = null;
+        if (snapshot != null && snapshot.getDetectionTime() != null) {
+            useStartDate = snapshot.getDetectionTime().atTime(10, 0);
+            LocalDate endDate = snapshot.getDetectionEndTime() != null
+                    ? snapshot.getDetectionEndTime() : snapshot.getDetectionTime();
+            useEndDate = endDate.atTime(19, 30);
+        }
         User user = userMapper.selectById(userId);
         String usePerson = user == null ? "" : user.getName();
         String sampleCode = StringUtils.isNotBlank(sample.getSampleCode())
@@ -1060,19 +1079,46 @@
             if (deviceId == null) {
                 continue;
             }
-            Long count = insOrderDeviceRecordMapper.selectCount(Wrappers.<InsOrderDeviceRecord>lambdaQuery()
-                    .eq(InsOrderDeviceRecord::getInsOrderId, orderId)
-                    .eq(InsOrderDeviceRecord::getDeviceId, deviceId)
-                    .eq(InsOrderDeviceRecord::getSampleCode, sampleCode));
-            if (count != null && count > 0) {
+            InsOrderDeviceRecord existing = insOrderDeviceRecordMapper.selectOne(
+                    Wrappers.<InsOrderDeviceRecord>lambdaQuery()
+                            .eq(InsOrderDeviceRecord::getInsOrderId, orderId)
+                            .eq(InsOrderDeviceRecord::getDeviceId, deviceId)
+                            .eq(InsOrderDeviceRecord::getSampleCode, sampleCode)
+                            .last("limit 1"));
+            if (existing != null) {
+                // 鍙ˉ鑷姩鐢熸垚浣嗗皻鏈~鍐欑殑璁板綍
+                if (existing.getUseStartDate() != null) {
+                    continue;
+                }
+                InsOrderDeviceRecord update = new InsOrderDeviceRecord();
+                update.setId(existing.getId());
+                if (StringUtils.isBlank(existing.getTemperature())) {
+                    update.setTemperature(temperature);
+                }
+                if (StringUtils.isBlank(existing.getHumidity())) {
+                    update.setHumidity(humidity);
+                }
+                if (useStartDate != null) {
+                    update.setUseStartDate(useStartDate);
+                    update.setUseEndDate(useEndDate);
+                }
+                if (existing.getUsePersonId() == null) {
+                    update.setUsePersonId(userId);
+                }
+                if (StringUtils.isBlank(existing.getUsePerson())) {
+                    update.setUsePerson(usePerson);
+                }
+                insOrderDeviceRecordMapper.updateById(update);
                 continue;
             }
             InsOrderDeviceRecord record = new InsOrderDeviceRecord();
             record.setInsOrderId(orderId);
             record.setDeviceId(deviceId);
             record.setSampleCode(sampleCode);
-            record.setTemperature(order.getTemperature());
-            record.setHumidity(order.getHumidity());
+            record.setTemperature(temperature);
+            record.setHumidity(humidity);
+            record.setUseStartDate(useStartDate);
+            record.setUseEndDate(useEndDate);
             record.setUseBefore(1);
             record.setUseAfter(1);
             record.setUsePersonId(userId);
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsProductServiceImpl.java b/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsProductServiceImpl.java
index b59afae..3089f47 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsProductServiceImpl.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsProductServiceImpl.java
@@ -11,12 +11,14 @@
 import com.ruoyi.framework.exception.ErrorException;
 import com.ruoyi.inspect.dto.InsProductBindingDto;
 import com.ruoyi.inspect.dto.WriteInspectionConditionDto;
+import com.ruoyi.inspect.mapper.InsOrderDeviceRecordMapper;
 import com.ruoyi.inspect.mapper.InsOrderMapper;
 import com.ruoyi.inspect.mapper.InsOrderStandardTemplateMapper;
 import com.ruoyi.inspect.mapper.InsOrderTemplateDeviceMapper;
 import com.ruoyi.inspect.mapper.InsProductMapper;
 import com.ruoyi.inspect.mapper.InsSampleMapper;
 import com.ruoyi.inspect.pojo.InsOrder;
+import com.ruoyi.inspect.pojo.InsOrderDeviceRecord;
 import com.ruoyi.inspect.pojo.InsOrderStandardTemplate;
 import com.ruoyi.inspect.pojo.InsOrderTemplateDevice;
 import com.ruoyi.inspect.pojo.InsProduct;
@@ -28,6 +30,8 @@
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.time.LocalDate;
+import java.time.LocalDateTime;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -48,6 +52,8 @@
     private InsOrderStandardTemplateMapper insOrderStandardTemplateMapper;
 
     private InsOrderTemplateDeviceMapper insOrderTemplateDeviceMapper;
+
+    private InsOrderDeviceRecordMapper insOrderDeviceRecordMapper;
 
     @Override
     public int selectOrderManDay(Integer orderId) {
@@ -144,6 +150,7 @@
             if (!hasSnapshotField && !dto.isDeviceIdsPresent()) {
                 throw new ErrorException("娌℃湁闇�瑕佷繚瀛樼殑妯℃澘妫�楠屾潯浠�");
             }
+            refreshPendingDeviceRecords(dto);
             return true;
         }
         InsOrder insOrder = new InsOrder();
@@ -153,6 +160,38 @@
         return true;
     }
 
+    /**
+     * 鍘熷璁板綍鐨勭幆澧冩潯浠跺悗琛ユ椂锛屽悓姝ュ埌灏氭湭濉啓鐨勪娇鐢ㄨ褰曘��
+     * 宸叉墜宸ユ敼鍔ㄨ繃鐨勮褰曪紙宸叉湁寮�濮嬫棩鏈燂級涓嶈鐩栥��
+     */
+    private void refreshPendingDeviceRecords(WriteInspectionConditionDto dto) {
+        boolean hasTemperature = dto.isTemperaturePresent()
+                && dto.getTemperature() != null && !dto.getTemperature().trim().isEmpty();
+        boolean hasDetectionRange = dto.isDetectionTimePresent() && dto.getDetectionTime() != null;
+        if (!hasTemperature && !hasDetectionRange) {
+            return;
+        }
+        List<Integer> deviceIds = insOrderTemplateDeviceMapper
+                .selectDeviceIdsByOrderAndTemplate(dto.getId(), dto.getTemplateId());
+        if (deviceIds == null || deviceIds.isEmpty()) {
+            return;
+        }
+        LocalDateTime start = null;
+        LocalDateTime end = null;
+        if (hasDetectionRange) {
+            start = dto.getDetectionTime().atTime(10, 0);
+            LocalDate endDate = dto.getDetectionEndTime() != null ? dto.getDetectionEndTime() : dto.getDetectionTime();
+            end = endDate.atTime(19, 30);
+        }
+        insOrderDeviceRecordMapper.update(null, Wrappers.<InsOrderDeviceRecord>lambdaUpdate()
+                .eq(InsOrderDeviceRecord::getInsOrderId, dto.getId())
+                .in(InsOrderDeviceRecord::getDeviceId, deviceIds)
+                .isNull(InsOrderDeviceRecord::getUseStartDate)
+                .set(hasTemperature, InsOrderDeviceRecord::getTemperature, dto.getTemperature())
+                .set(hasDetectionRange, InsOrderDeviceRecord::getUseStartDate, start)
+                .set(hasDetectionRange, InsOrderDeviceRecord::getUseEndDate, end));
+    }
+
     //鏌ヨ寰呮椤圭洰
     @Override
     public IPage<InsProduct> selectNoProducts(Page page, InsProduct insProduct, Integer orderId, String ids) {
diff --git a/sql/fix_device_maintenance_plan_duplicates.sql b/sql/fix_device_maintenance_plan_duplicates.sql
new file mode 100644
index 0000000..2af63ef
--- /dev/null
+++ b/sql/fix_device_maintenance_plan_duplicates.sql
@@ -0,0 +1,72 @@
+-- ============================================================
+-- 璁惧淇濆吇璁″垝鏄庣粏鍘婚噸锛堟暟鎹慨澶嶏紝闈炵粨鏋勫彉鏇达級
+-- 鐢熸垚鏃ユ湡锛�2026-11-06
+--
+-- 鑳屾櫙锛氬巻鍙茬増鏈ˉ璁惧/鏀规棩鏈熸椂锛屽彲鑳界粰鍚屼竴璁″垝銆佸悓涓�璁惧銆�
+--   鍚屼竴淇濆吇鏈堜唤鐣欎笅澶氭潯鏄庣粏锛屽鑷村鍑烘枃妗i噸澶嶃��
+--
+-- 鍘婚噸鍙e緞锛氬悓涓� maintenance_plan_id + device_id + 淇濆吇鏈堜唤
+--   鍙繚鐣欎竴鏉°��
+--   浼樺厛淇濈暀宸叉湁淇濆吇璁板綍鐨勬槑缁嗭紱閮芥病鏈夎褰曟椂淇濈暀鏈�鏂颁竴鏉°��
+--   涓轰繚璇佹暟鎹畨鍏紝鍙垹闄も�滄病鏈変换浣曚繚鍏昏褰曗�濈殑閲嶅琛岋紱
+--   鑻ュ悓涓�缁勯噷鏈夊鏉¢兘宸叉湁淇濆吇璁板綍锛屼笉鑷姩鍒犻櫎锛岄渶浜哄伐纭銆�
+--
+-- 鎵ц鍓嶅繀椤诲厛澶囦唤锛堣剼鏈細鑷姩鍒涘缓澶囦唤琛紝宸插瓨鍦ㄥ垯涓嶈鐩栵級銆�
+-- 鑴氭湰鍙噸澶嶆墽琛岋紙骞傜瓑锛夈��
+-- ============================================================
+
+-- 1. 澶囦唤鏄庣粏琛紙棣栨鎵ц鍒涘缓锛岄噸澶嶆墽琛屼笉瑕嗙洊锛�
+CREATE TABLE IF NOT EXISTS `device_maintenance_plan_details_bak_20261106` AS
+SELECT * FROM `device_maintenance_plan_details`;
+
+-- 2. 鏌ョ湅鍘婚噸鍓嶇殑閲嶅鎯呭喌
+SELECT maintenance_plan_id, device_id, DATE_FORMAT(maintenance_date, '%Y-%m') AS maintenance_month,
+       COUNT(*) AS duplicate_count,
+       GROUP_CONCAT(maintenance_plan_detail_id ORDER BY maintenance_plan_detail_id) AS detail_ids
+FROM device_maintenance_plan_details
+GROUP BY maintenance_plan_id, device_id, DATE_FORMAT(maintenance_date, '%Y-%m')
+HAVING COUNT(*) > 1;
+
+-- 3. 鍒犻櫎閲嶅琛岋細鎸夆�滄湁淇濆吇璁板綍浼樺厛銆佹渶鏂颁紭鍏堚�濇帓搴忥紝浠呭垹闄ゆ棤淇濆吇璁板綍鐨勯噸澶嶈
+START TRANSACTION;
+
+DELETE d
+FROM device_maintenance_plan_details d
+JOIN (
+    SELECT maintenance_plan_detail_id
+    FROM (
+        SELECT dmpd.maintenance_plan_detail_id,
+               ROW_NUMBER() OVER (
+                   PARTITION BY dmpd.maintenance_plan_id, dmpd.device_id, DATE_FORMAT(dmpd.maintenance_date, '%Y-%m')
+                   ORDER BY (SELECT COUNT(*) FROM device_maintenance dm
+                             WHERE dm.maintenance_plan_detail_id = dmpd.maintenance_plan_detail_id) DESC,
+                            dmpd.maintenance_plan_detail_id DESC
+               ) AS rn
+        FROM device_maintenance_plan_details dmpd
+    ) ranked
+    WHERE ranked.rn > 1
+) dup ON d.maintenance_plan_detail_id = dup.maintenance_plan_detail_id
+WHERE NOT EXISTS (
+    SELECT 1 FROM device_maintenance dm
+    WHERE dm.maintenance_plan_detail_id = d.maintenance_plan_detail_id
+);
+
+COMMIT;
+
+-- 4. 鏍¢獙锛堟墽琛屽畬鍙汉宸ユ牳瀵癸級
+-- 4.1 搴斾负 0 琛岋細鍚屼竴璁″垝 + 鍚屼竴璁惧 + 鍚屼竴淇濆吇鏈堜唤浠嶆湁閲嶅
+-- SELECT maintenance_plan_id, device_id, DATE_FORMAT(maintenance_date, '%Y-%m') AS maintenance_month, COUNT(*) AS c
+-- FROM device_maintenance_plan_details
+-- GROUP BY maintenance_plan_id, device_id, DATE_FORMAT(maintenance_date, '%Y-%m')
+-- HAVING c > 1;
+--
+-- 4.2 搴斾负 0 琛岋細淇濆吇璁板綍鍏宠仈鐨勮鍒掓槑缁嗗凡涓嶅瓨鍦紙闃叉璇垹瀵艰嚧璁板綍瀛ゅ効锛�
+-- SELECT dm.id, dm.maintenance_plan_detail_id
+-- FROM device_maintenance dm
+-- LEFT JOIN device_maintenance_plan_details dmpd
+--   ON dmpd.maintenance_plan_detail_id = dm.maintenance_plan_detail_id
+-- WHERE dmpd.maintenance_plan_detail_id IS NULL;
+--
+-- 4.3 娓呯悊鍓嶅悗鏁伴噺瀵规瘮锛�
+-- SELECT COUNT(*) FROM device_maintenance_plan_details;
+-- SELECT COUNT(*) FROM device_maintenance_plan_details_bak_20261106;
diff --git a/sql/fix_device_record_backfill.sql b/sql/fix_device_record_backfill.sql
new file mode 100644
index 0000000..6c91b8f
--- /dev/null
+++ b/sql/fix_device_record_backfill.sql
@@ -0,0 +1,104 @@
+-- ============================================================
+-- 璁惧浣跨敤璁板綍鍘嗗彶鏁版嵁鍥炲~锛堟暟鎹慨澶嶏紝闈炵粨鏋勫彉鏇达級
+-- 鐢熸垚鏃ユ湡锛�2026-11-06
+--
+-- 鑳屾櫙锛氭棫鐗堟湰鑷姩鐢熸垚浣跨敤璁板綍鏃跺彧鍐欒澶囥�佹牱鍝佸拰浣跨敤浜猴紝
+--   娌℃湁鍐欎娇鐢ㄦ棩鏈熴�佽捣姝㈡椂闂村拰娓╂箍搴︼紝瀵艰嚧浣跨敤璁板綍椤甸潰杩欎簺鍒椾负绌恒��
+--   鏂扮増鏈湪淇濆瓨鍘熷璁板綍鏃朵細鑷姩甯﹀嚭锛屼絾涓嶄細鍥炲ご淇敼鍘嗗彶鏁版嵁銆�
+--
+-- 鍥炲~鍙e緞锛堟寜瀹㈡埛纭鐨勮鍒欙級锛�
+--   1. 鍙鐞� use_start_date 涓虹┖鐨勫巻鍙茶褰曪紝宸叉墜宸ュ~鍐欑殑涓嶅姩锛�
+--   2. 閫氳繃 ins_order_template_device 鎵惧埌璇ヨ鍗曘�佽璁惧瀵瑰簲鐨�
+--      鍘熷璁板綍蹇収 ins_order_standard_template锛�
+--   3. 鍚屼竴璁惧鍦ㄥ悓涓�璁㈠崟瀵瑰簲澶氫釜妯℃澘鏃讹紝鍙栨娴嬫棩鏈熸渶鏃╃殑涓�鏉★紱
+--   4. 浣跨敤寮�濮嬫棩鏈� = 妫�娴嬪紑濮嬫棩鏈� 10:00:00锛�
+--      浣跨敤缁撴潫鏃ユ湡 = 妫�娴嬬粨鏉熸棩鏈� 19:30:00锛�
+--      娌℃湁妫�娴嬬粨鏉熸棩鏈熸椂鎸夊紑濮嬫棩鏈熷綋澶� 19:30:00锛�
+--   5. 娓╁害鍙栧師濮嬭褰曞揩鐓ф俯搴︼紝宸叉湁鐨勪笉瑕嗙洊锛�
+--   6. 鍘熷璁板綍娌℃湁婀垮害瀛楁锛屾箍搴︿笉鍥炲~锛�
+--   7. 鎵句笉鍒板師濮嬭褰曡澶囧叧鑱旂殑鎵嬪伐璁板綍淇濇寔涓虹┖锛岀敱瀹為獙浜哄憳琛ュ~銆�
+--
+-- 鏁版嵁鏉ユ簮鍧囦负搴撲腑宸插瓨鍦ㄧ殑鍘熷璁板綍蹇収锛屼粎 10:00 / 19:30
+-- 涓烘寜瀹㈡埛瑙勫垯濉叆鐨勯粯璁ゆ椂闂淬��
+--
+-- 鑴氭湰鍙噸澶嶆墽琛岋紙骞傜瓑锛夈��
+-- ============================================================
+
+-- 1. 澶囦唤浣跨敤璁板綍琛紙棣栨鎵ц鍒涘缓锛岄噸澶嶆墽琛屼笉瑕嗙洊锛�
+CREATE TABLE IF NOT EXISTS `device_record_bak_20261106` AS
+SELECT * FROM `device_record`;
+
+-- 2. 鍥炲~鍓嶇粺璁�
+-- 2.1 绌鸿褰曟�绘暟
+SELECT COUNT(*) AS blank_total
+FROM device_record
+WHERE use_start_date IS NULL;
+
+-- 2.2 鑳藉尮閰嶅埌鍘熷璁板綍鏉′欢鐨勭┖璁板綍鏁�
+SELECT COUNT(*) AS matchable_total
+FROM device_record dr
+WHERE dr.use_start_date IS NULL
+  AND EXISTS (
+      SELECT 1
+      FROM ins_order_template_device t
+      JOIN ins_order_standard_template s
+        ON s.ins_order_id = t.ins_order_id AND s.template_id = t.template_id
+      WHERE t.ins_order_id = dr.ins_order_id
+        AND t.device_id = dr.device_id
+        AND s.detection_time IS NOT NULL
+  );
+
+-- 2.3 鍖归厤涓嶅埌銆佸皢淇濇寔涓虹┖鐨勮褰曪紙浜哄伐琛ュ~锛�
+SELECT dr.id, dr.ins_order_id, dr.device_id, dr.sample_code
+FROM device_record dr
+WHERE dr.use_start_date IS NULL
+  AND NOT EXISTS (
+      SELECT 1
+      FROM ins_order_template_device t
+      JOIN ins_order_standard_template s
+        ON s.ins_order_id = t.ins_order_id AND s.template_id = t.template_id
+      WHERE t.ins_order_id = dr.ins_order_id
+        AND t.device_id = dr.device_id
+        AND s.detection_time IS NOT NULL
+  );
+
+-- 3. 鍥炲~
+START TRANSACTION;
+
+UPDATE device_record dr
+JOIN (
+    SELECT t.ins_order_id, t.device_id,
+           s.temperature, s.detection_time, s.detection_end_time
+    FROM ins_order_template_device t
+    JOIN ins_order_standard_template s
+      ON s.ins_order_id = t.ins_order_id AND s.template_id = t.template_id
+    WHERE s.detection_time IS NOT NULL
+      AND s.id = (
+          SELECT s2.id
+          FROM ins_order_template_device t2
+          JOIN ins_order_standard_template s2
+            ON s2.ins_order_id = t2.ins_order_id AND s2.template_id = t2.template_id
+          WHERE t2.ins_order_id = t.ins_order_id
+            AND t2.device_id = t.device_id
+            AND s2.detection_time IS NOT NULL
+          ORDER BY s2.detection_time ASC, s2.id ASC
+          LIMIT 1
+      )
+) picked ON picked.ins_order_id = dr.ins_order_id AND picked.device_id = dr.device_id
+SET dr.use_start_date = TIMESTAMP(picked.detection_time, '10:00:00'),
+    dr.use_end_date = TIMESTAMP(COALESCE(picked.detection_end_time, picked.detection_time), '19:30:00'),
+    dr.temperature = COALESCE(NULLIF(dr.temperature, ''), picked.temperature)
+WHERE dr.use_start_date IS NULL;
+
+COMMIT;
+
+-- 4. 鏍¢獙锛堟墽琛屽畬鍙汉宸ユ牳瀵癸級
+-- 4.1 鍥炲~鍓嶅悗瀵规瘮锛氬浠借〃绌烘牸鏁� - 鐜拌〃绌烘牸鏁� = 鏈鍥炲~鏉℃暟
+-- SELECT
+--     (SELECT COUNT(*) FROM device_record_bak_20261106 WHERE use_start_date IS NULL) AS blank_before,
+--     (SELECT COUNT(*) FROM device_record WHERE use_start_date IS NULL) AS blank_after;
+--
+-- 4.2 鎶芥煡鏌愯鍗曠殑鍥炲~缁撴灉
+-- SELECT id, device_id, ins_order_id, sample_code, temperature, use_start_date, use_end_date, use_person
+-- FROM device_record
+-- WHERE ins_order_id = ?;
diff --git a/sql/fix_inspection_order_detail_backfill.sql b/sql/fix_inspection_order_detail_backfill.sql
new file mode 100644
index 0000000..1311d43
--- /dev/null
+++ b/sql/fix_inspection_order_detail_backfill.sql
@@ -0,0 +1,78 @@
+-- ============================================================
+-- 妫�楠屽鎵樺崟鏃ф暟鎹槑缁嗗洖濉紙鏁版嵁淇锛岄潪缁撴瀯鍙樻洿锛�
+-- 鐢熸垚鏃ユ湡锛�2026-11-06
+--
+-- 鑳屾櫙锛氭棫鐗堣嚜鍔ㄧ敓鎴愭楠屽鎵樺崟鏃跺彧鍐欎簡琛ㄥご锛屾病鏈夊啓鍏�
+--   cnas_inspection_order_detail 鏄庣粏琛岋紝瀵艰嚧鏌ョ湅/缂栬緫寮圭獥閲�
+--   鏍峰搧鍚嶇О銆佹牱鍝佺紪鍙枫�佹娴嬮」鐩�佹暟閲忋�佺姸鎬併�佹娴嬩緷鎹�佸娉ㄣ��
+--   濮旀墭缂栧彿鍏ㄩ儴涓虹┖銆傛柊鐗堢洃鍚櫒宸蹭細鍐欐槑缁嗭紝浠呴渶鍥炲~鍘嗗彶鏁版嵁銆�
+--
+-- 鍥炲~鏉ユ簮锛�
+--   鏍峰搧鍚嶇О   cnas_inspection_order.sample_name锛堢┖鏃跺彇 ins_order锛�
+--   鏍峰搧缂栧彿   ins_sample 绗竴鏉℃牱鍝佺紪鍙�
+--   妫�娴嬮」鐩�   ins_order_standard_template.name 鍘婚噸鍚庘�溿�佲�濇嫾鎺�
+--   鏁伴噺/鐘舵��  cnas_inspection_order 琛ㄥご锛堢┖鏃跺彇 ins_order锛�
+--   妫�娴嬩緷鎹�   standard_tree -> standard_method 鐨勨�滅紪鍙� 鍚嶇О鈥濓紝
+--              鍙栦笉鍒版椂鍥為��鍒版ā鏉垮揩鐓х紪鍙�
+--   澶囨敞       ins_order.remark
+--
+-- 浠呭鐞嗏�滃凡鏈変笅鍗曡褰曚絾娌℃湁浠讳綍鏄庣粏琛屸�濈殑濮旀墭鍗曪紱
+-- 宸叉湁鏄庣粏鐨勮褰曚笉鏀广�佷笉閲嶅鎻掑叆銆傚彲閲嶅鎵ц锛堝箓绛夛級銆�
+--
+-- 鎵ц鍓嶅缓璁浠斤紙鍦� LIMS 鏁版嵁搴撳唴鎵ц锛夛細
+--   CREATE TABLE cnas_inspection_order_detail_bak_20261106 AS
+--   SELECT * FROM cnas_inspection_order_detail;
+-- ============================================================
+
+START TRANSACTION;
+
+INSERT INTO cnas_inspection_order_detail
+    (inspection_order_id, sample_number, sample_name, test_item,
+     quantity, sample_status, test_standard, remark)
+SELECT
+    io.inspection_order_id,
+    (SELECT s.sample_code
+     FROM ins_sample s
+     WHERE s.ins_order_id = o.id
+     ORDER BY s.id
+     LIMIT 1),
+    COALESCE(NULLIF(io.sample_name, ''), NULLIF(o.sample_view, ''), o.sample),
+    (SELECT GROUP_CONCAT(DISTINCT t.name ORDER BY t.sort, t.id SEPARATOR '銆�')
+     FROM ins_order_standard_template t
+     WHERE t.ins_order_id = o.id),
+    COALESCE(NULLIF(io.quantity, ''), o.test_quantity),
+    COALESCE(NULLIF(io.sample_status, ''), o.sample_status),
+    COALESCE(
+        NULLIF(TRIM(CONCAT(COALESCE(sm.code, ''), ' ', COALESCE(sm.name, ''))), ''),
+        (SELECT GROUP_CONCAT(DISTINCT t.number ORDER BY t.sort, t.id SEPARATOR '銆�')
+         FROM ins_order_standard_template t
+         WHERE t.ins_order_id = o.id)
+    ),
+    o.remark
+FROM cnas_inspection_order io
+JOIN ins_order o ON o.id = io.ins_order_id
+LEFT JOIN standard_tree st ON st.id = o.standard_tree_id
+LEFT JOIN standard_method sm ON sm.id = st.standard_method_id
+WHERE NOT EXISTS (
+    SELECT 1 FROM cnas_inspection_order_detail d
+    WHERE d.inspection_order_id = io.inspection_order_id
+);
+
+COMMIT;
+
+-- 鏍¢獙锛堟墽琛屽畬鍙汉宸ユ牳瀵癸紝搴旇繑鍥� 0 琛岋級锛�
+-- SELECT io.inspection_order_id, io.entrust_code
+-- FROM cnas_inspection_order io
+-- JOIN ins_order o ON o.id = io.ins_order_id
+-- WHERE NOT EXISTS (
+--     SELECT 1 FROM cnas_inspection_order_detail d
+--     WHERE d.inspection_order_id = io.inspection_order_id
+-- );
+--
+-- 鎶芥煡鍥炲~缁撴灉锛�
+-- SELECT d.inspection_order_detail_id, d.inspection_order_id, d.sample_name,
+--        d.sample_number, d.test_item, d.quantity, d.sample_status,
+--        d.test_standard, d.remark
+-- FROM cnas_inspection_order_detail d
+-- JOIN cnas_inspection_order io ON io.inspection_order_id = d.inspection_order_id
+-- ORDER BY d.inspection_order_id;
diff --git a/sql/fix_inspection_order_orphans.sql b/sql/fix_inspection_order_orphans.sql
new file mode 100644
index 0000000..90c3e61
--- /dev/null
+++ b/sql/fix_inspection_order_orphans.sql
@@ -0,0 +1,53 @@
+-- ============================================================
+-- 妫�楠屽鎵樺崟瀛ゅ効鏁版嵁娓呯悊锛堟暟鎹慨澶嶏紝闈炵粨鏋勫彉鏇达級
+-- 鐢熸垚鏃ユ湡锛�2026-11-06
+--
+-- 鑳屾櫙锛氬鎴峰簱鍏辨湁 24 鏉℃楠屽鎵樺崟锛屼絾濮旀墭涓嬪崟鍙湁 22 鏉°��
+--   澶氬嚭鐨� 2 鏉″鎵樺崟锛圠MSY-20260922-01銆丩MSY-20260929-璇曟牱锛�
+--   瀵瑰簲鐨勪笅鍗曡褰曪紙ins_order 10908銆�10938锛夊凡琚垹闄わ紝灞炰簬
+--   鍘嗗彶閬楃暀鐨勫鍎挎暟鎹�傚綋鍓嶅垹闄や笅鍗曠殑閫昏緫宸蹭細绾ц仈娓呯悊濮旀墭鍗曪紝
+--   杩欎袱鏉℃槸鏃х増鏈垹闄ゆ椂鐣欎笅鐨勩��
+--
+-- 宸叉牳瀹烇細杩欎袱鏉″鎵樺崟娌℃湁鏄庣粏銆佹牱鍝佹帴鏀躲�佹姤鍛娿�佽澶囦娇鐢ㄧ瓑
+--   浠讳綍鍏宠仈璁板綍锛屽垹闄や笉褰卞搷鍏朵粬鏁版嵁銆�
+--
+-- 浠呮寜濮旀墭缂栧彿绮剧‘鍖归厤锛屼笖鍐嶆鏍¢獙鈥滀笅鍗曚笉瀛樺湪 + 鏃犱换浣曞叧鑱旇褰曗��
+-- 鍚庢墠鍒犻櫎锛屽彲閲嶅鎵ц锛堝箓绛夛級銆�
+--
+-- 鎵ц鍓嶅缓璁浠斤紙鍦� LIMS 鏁版嵁搴撳唴鎵ц锛夛細
+--   CREATE TABLE cnas_inspection_order_bak_20261106 AS
+--   SELECT * FROM cnas_inspection_order;
+-- ============================================================
+
+START TRANSACTION;
+
+DELETE io
+FROM cnas_inspection_order io
+LEFT JOIN ins_order o ON o.id = io.ins_order_id
+WHERE o.id IS NULL
+  AND io.entrust_code IN ('LMSY-20260922-01', 'LMSY-20260929-璇曟牱')
+  AND NOT EXISTS (
+      SELECT 1 FROM cnas_inspection_order_detail d
+      WHERE d.inspection_order_id = io.inspection_order_id)
+  AND NOT EXISTS (
+      SELECT 1 FROM cnas_process_sample ps
+      WHERE ps.inspection_order_id = io.inspection_order_id)
+  AND NOT EXISTS (
+      SELECT 1 FROM cnas_process_report pr
+      WHERE pr.inspection_order_id = io.inspection_order_id)
+  AND NOT EXISTS (
+      SELECT 1 FROM cnas_process_order_device pod
+      WHERE pod.inspection_order_id = io.inspection_order_id);
+
+COMMIT;
+
+-- 鏍¢獙锛堟墽琛屽畬鍙汉宸ユ牳瀵癸級锛�
+-- 濮旀墭鍗曟�绘暟搴斾笌濮旀墭涓嬪崟鏁颁竴鑷达紙瀹㈡埛搴撳簲涓� 22锛夛細
+-- SELECT COUNT(*) AS inspection_order_total FROM cnas_inspection_order;
+-- SELECT COUNT(*) AS ins_order_total FROM ins_order;
+--
+-- 搴旇繑鍥� 0 琛岋細
+-- SELECT io.inspection_order_id, io.entrust_code, io.ins_order_id
+-- FROM cnas_inspection_order io
+-- LEFT JOIN ins_order o ON o.id = io.ins_order_id
+-- WHERE o.id IS NULL;
diff --git a/sql/lims_delta.sql b/sql/lims_delta.sql
index b89c9e7..7429e74 100644
--- a/sql/lims_delta.sql
+++ b/sql/lims_delta.sql
@@ -1,3 +1,22 @@
 -- 鎵归噺涓嬪崟鎵规鍙凤細鍚屼竴娆℃壒閲忎笅鍗曠殑鏍峰搧鍏变韩涓�涓壒娆″彿
 ALTER TABLE ins_order ADD COLUMN batch_no VARCHAR(64) NULL COMMENT '鎵归噺涓嬪崟鎵规鍙�' AFTER entrust_code;
 ALTER TABLE cnas_inspection_order ADD COLUMN batch_no VARCHAR(64) NULL COMMENT '鎵归噺涓嬪崟鎵规鍙�' AFTER entrust_code;
+
+-- 璁惧淇濆吇璁″垝鏄庣粏闄勪欢琛細闄勪欢鎸夎鍒掓槑缁嗭紙璁惧琛岋級鎸傝浇
+CREATE TABLE IF NOT EXISTS `device_maintenance_attachment` (
+  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '闄勪欢id',
+  `maintenance_plan_detail_id` int NOT NULL COMMENT '璁惧淇濆吇璁″垝鏄庣粏id',
+  `file_name` varchar(255) NOT NULL COMMENT '鏂囦欢鍚�',
+  `file_url` varchar(500) NOT NULL COMMENT '鏂囦欢鍦板潃',
+  `sort` int DEFAULT '0' COMMENT '鎺掑簭',
+  `create_user` int DEFAULT NULL COMMENT '鍒涘缓浜�',
+  `create_time` datetime DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
+  `update_user` int DEFAULT NULL COMMENT '淇敼浜�',
+  `update_time` datetime DEFAULT NULL COMMENT '淇敼鏃堕棿',
+  PRIMARY KEY (`id`),
+  KEY `idx_dmpa_detail_id` (`maintenance_plan_detail_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='璁惧淇濆吇璁″垝鏄庣粏闄勪欢琛�';
+
+-- 鏂囦欢鍙楁帶鐢宠锛氬鍔犲彂甯冩棩鏈熴�佸疄鏂芥棩鏈燂紙鎻愪氦鏃ユ湡鍒椾繚鐣欙紝浣嗙晫闈笉鍐嶆樉绀猴級
+ALTER TABLE cnas_manage_document_controlled ADD COLUMN publish_date DATE NULL COMMENT '鍙戝竷鏃ユ湡' AFTER submit_date;
+ALTER TABLE cnas_manage_document_controlled ADD COLUMN implement_date DATE NULL COMMENT '瀹炴柦鏃ユ湡' AFTER publish_date;

--
Gitblit v1.9.3