From 341ad6f3d4f8dd85db45380a8349eada915eb924 Mon Sep 17 00:00:00 2001
From: zhuo <2089219845@qq.com>
Date: 星期五, 21 二月 2025 13:26:55 +0800
Subject: [PATCH] 设备校准计划移植

---
 cnas-device/src/main/java/com/ruoyi/device/service/DeviceCalibrationPlanService.java                |   89 ++++
 cnas-device/src/main/java/com/ruoyi/device/service/DeviceCalibrationPlanDetailService.java          |   16 
 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java                         |    4 
 cnas-device/src/main/java/com/ruoyi/device/excel/upload/DeviceExaminePlanUpload.java                |   37 +
 inspect-server/src/main/java/com/ruoyi/inspect/util/XWPFDocumentUtils.java                          |  119 +++++
 basic-server/src/main/java/com/ruoyi/basic/service/impl/CapacityScopeServiceImpl.java               |   12 
 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java                             |    2 
 inspect-server/src/main/java/com/ruoyi/inspect/util/UserUtils.java                                  |   90 ++++
 cnas-device/src/main/java/com/ruoyi/device/controller/DeviceCalibrationPlanController.java          |  173 ++++++++
 cnas-device/src/main/java/com/ruoyi/device/excel/upload/DeviceCalibrationPlanDetailUpload.java      |   36 +
 ruoyi-admin/src/main/resources/application.yml                                                      |    2 
 cnas-device/src/main/java/com/ruoyi/device/dto/DeviceCalibrationPlanDetailDto.java                  |   21 +
 cnas-device/src/main/java/com/ruoyi/device/mapper/DeviceCalibrationPlanMapper.java                  |   29 +
 cnas-device/src/main/resources/mapper/DeviceCalibrationPlanDetailMapper.xml                         |   17 
 cnas-device/src/main/java/com/ruoyi/device/pojo/DeviceCalibrationPlan.java                          |   67 +++
 cnas-device/src/main/java/com/ruoyi/device/mapper/DeviceCalibrationPlanDetailMapper.java            |   27 +
 cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceCalibrationPlanDetailServiceImpl.java |   20 
 cnas-device/src/main/resources/mapper/DeviceCalibrationPlanMapper.xml                               |   20 
 cnas-device/src/main/java/com/ruoyi/device/dto/DeviceCalibrationPlanDto.java                        |   31 +
 cnas-device/src/main/java/com/ruoyi/device/pojo/DeviceCalibrationPlanDetail.java                    |   74 +++
 cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceCalibrationPlanServiceImpl.java       |  353 ++++++++++++++++
 21 files changed, 1,230 insertions(+), 9 deletions(-)

diff --git a/basic-server/src/main/java/com/ruoyi/basic/service/impl/CapacityScopeServiceImpl.java b/basic-server/src/main/java/com/ruoyi/basic/service/impl/CapacityScopeServiceImpl.java
index f83bd97..4b436aa 100644
--- a/basic-server/src/main/java/com/ruoyi/basic/service/impl/CapacityScopeServiceImpl.java
+++ b/basic-server/src/main/java/com/ruoyi/basic/service/impl/CapacityScopeServiceImpl.java
@@ -100,12 +100,14 @@
         List<Product> products = productMapper.selectList(Wrappers.<Product>lambdaQuery()
                 .eq(Product::getObjectId, id));
         List<Integer> productIds = products.stream().map(Product::getId).collect(Collectors.toList());
-        productPartMapper.delete(Wrappers.<ProductPart>lambdaQuery()
-                .in(ProductPart::getProductId, productIds));
+        if (CollectionUtils.isNotEmpty(productIds)) {
+            productPartMapper.delete(Wrappers.<ProductPart>lambdaQuery()
+                    .in(ProductPart::getProductId, productIds));
 
-        // 鍒犻櫎浜у搧缁存姢
-        productMapper.delete(Wrappers.<Product>lambdaQuery()
-                .in(Product::getId, productIds));
+            // 鍒犻櫎浜у搧缁存姢
+            productMapper.delete(Wrappers.<Product>lambdaQuery()
+                    .in(Product::getId, productIds));
+        }
 
         return structureTestObjectMapper.deleteById(id);
     }
diff --git a/cnas-device/src/main/java/com/ruoyi/device/controller/DeviceCalibrationPlanController.java b/cnas-device/src/main/java/com/ruoyi/device/controller/DeviceCalibrationPlanController.java
new file mode 100644
index 0000000..37aed61
--- /dev/null
+++ b/cnas-device/src/main/java/com/ruoyi/device/controller/DeviceCalibrationPlanController.java
@@ -0,0 +1,173 @@
+package com.ruoyi.device.controller;
+
+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.device.dto.DeviceCalibrationPlanDto;
+import com.ruoyi.device.pojo.DeviceCalibrationPlan;
+import com.ruoyi.device.pojo.DeviceCalibrationPlanDetail;
+import com.ruoyi.device.service.DeviceCalibrationPlanDetailService;
+import com.ruoyi.device.service.DeviceCalibrationPlanService;
+import com.ruoyi.framework.exception.ErrorException;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.Map;
+
+/**
+ * <p>
+ * 璁惧鏍″噯璁″垝涓昏〃 鍓嶇鎺у埗鍣�
+ * </p>
+ *
+ * @author 姹熻嫃榈烽洀缃戠粶绉戞妧鏈夐檺鍏徃
+ * @since 2024-12-16 03:58:17
+ */
+@Api(tags = "璁惧鏍″噯璁″垝")
+@AllArgsConstructor
+@RestController
+@RequestMapping("/deviceCalibrationPlan")
+public class DeviceCalibrationPlanController {
+
+    private DeviceCalibrationPlanService deviceCalibrationPlanService;
+
+    private DeviceCalibrationPlanDetailService deviceCalibrationPlanDetailService;
+
+    /**
+     * 鏂板璁惧鏍″噯璁″垝
+     * @return
+     */
+    @ApiOperation(value = "鏂板璁惧鏍″噯璁″垝")
+    @PostMapping("/addDeviceCalibrationPlan")
+    public Result addDeviceCalibrationPlan(@RequestBody DeviceCalibrationPlanDto calibrationPlanDto){
+        return Result.success(deviceCalibrationPlanService.addDeviceCalibrationPlan(calibrationPlanDto));
+    }
+
+    /**
+     * 淇敼璁惧鏍″噯璁″垝
+     * @param calibrationPlanDto 璁惧鏍″噯璁″垝
+     */
+    @ApiOperation("鎵归噺淇敼璁惧鏍″噯璁″垝")
+    @PostMapping("/updateDeviceCalibrationPlan")
+    public Result updateDeviceCalibrationPlan(@RequestBody DeviceCalibrationPlanDto calibrationPlanDto) {
+        return Result.success(deviceCalibrationPlanService.updateDeviceCalibrationPlan(calibrationPlanDto));
+    }
+
+    /**
+     * 鏌ヨ璁惧鏍″噯璁″垝璇︽儏
+     */
+    @ApiOperation("鏌ヨ璁惧鏍″噯璁″垝璇︽儏")
+    @GetMapping("/getDeviceCalibrationPlan")
+    public Result<DeviceCalibrationPlanDto> getDeviceCalibrationPlan(Integer planId) {
+        return Result.success(deviceCalibrationPlanService.getDeviceCalibrationPlan(planId));
+    }
+
+    /**
+     * 瀵煎叆璁惧鏍″噯璁″垝
+     * @return
+     */
+    @ApiOperation(value = "瀵煎叆璁惧鏍″噯璁″垝")
+    @PostMapping("/importDeviceCalibrationPlan")
+    public Result importDeviceCalibrationPlan(MultipartFile file, String planYear){
+        return Result.success(deviceCalibrationPlanService.importDeviceCalibrationPlan(file, planYear));
+    }
+
+
+    /**
+     * 璁惧鏍″噯璁″垝鍒犻櫎
+     * @return
+     */
+    @ApiOperation(value = "璁惧鏍″噯璁″垝鍒犻櫎")
+    @DeleteMapping("/delQualitySupervise")
+    public Result delQualitySupervise(Integer planId){
+        return Result.success(deviceCalibrationPlanService.removeById(planId));
+    }
+
+    /**
+     * 鎻愪氦鎵瑰噯
+     * @return
+     */
+    @ApiOperation(value = "鎻愪氦鎵瑰噯")
+    @PostMapping("/submiatRatifyDeviceCalibrationPlan")
+    public Result submiatRatifyDeviceCalibrationPlan(@RequestBody DeviceCalibrationPlan DeviceCalibrationPlan){
+        return Result.success(deviceCalibrationPlanService.submiatRatifyDeviceCalibrationPlan(DeviceCalibrationPlan));
+    }
+
+    /**
+     * 璁惧鏍″噯璁″垝鎵瑰噯
+     * @return
+     */
+    @ApiOperation(value = "璁惧鏍″噯璁″垝鎵瑰噯")
+    @PostMapping("/ratifyDeviceCalibrationPlan")
+    public Result ratifyDeviceCalibrationPlan(@RequestBody DeviceCalibrationPlan DeviceCalibrationPlan){
+        return Result.success(deviceCalibrationPlanService.ratifyDeviceCalibrationPlan(DeviceCalibrationPlan));
+    }
+
+
+    /**
+     * 璁惧鏍″噯璁″垝鍒楄〃
+     * @return
+     */
+    @ApiOperation(value = "璁惧鏍″噯璁″垝鍒楄〃")
+    @GetMapping("/pageDeviceCalibrationPlan")
+    public Result<IPage<DeviceCalibrationPlanDto>> pageDeviceCalibrationPlan(Page page, DeviceCalibrationPlan DeviceCalibrationPlan) throws Exception {
+        return Result.success(deviceCalibrationPlanService.pageDeviceCalibrationPlan(page, DeviceCalibrationPlan));
+    }
+
+    /**
+     * 璁惧鏍″噯璁″垝璇︽儏鍒楄〃
+     * @return
+     */
+    @ApiOperation(value = "璁惧鏍″噯璁″垝璇︽儏鍒楄〃")
+    @PostMapping("/pageDeviceCalibrationPlanDetail")
+    public Result<IPage<DeviceCalibrationPlanDetail>> pageDeviceCalibrationPlanDetail(Page page, DeviceCalibrationPlanDetail deviceCalibrationPlanDetails) {
+        return Result.success(deviceCalibrationPlanService.pageDeviceCalibrationPlanDetail(page, deviceCalibrationPlanDetails));
+    }
+
+    /**
+     * 鏂板璁惧鏍″噯璁″垝璇︽儏
+     * @return
+     */
+    @ApiOperation(value = "鏂板璁惧鏍″噯璁″垝璇︽儏")
+    @PostMapping("/addDeviceCalibrationPlanDetail")
+    public Result addDeviceCalibrationPlanDetail(@RequestBody DeviceCalibrationPlanDetail deviceCalibrationPlanDetail){
+        if (deviceCalibrationPlanDetail.getPlanId() == null) {
+            throw new ErrorException("缂哄皯璁惧鏍″噯璁″垝涓昏〃id");
+        }
+        return Result.success(deviceCalibrationPlanDetailService.save(deviceCalibrationPlanDetail));
+    }
+
+    /**
+     * 淇敼璁惧鏍″噯璁″垝璇︽儏
+     * @return
+     */
+    @ApiOperation(value = "淇敼璁惧鏍″噯璁″垝璇︽儏")
+    @PostMapping("/updateDeviceCalibrationPlanDetail")
+    public Result updateDeviceCalibrationPlanDetail(@RequestBody DeviceCalibrationPlanDetail deviceCalibrationPlanDetail){
+        return Result.success(deviceCalibrationPlanDetailService.updateById(deviceCalibrationPlanDetail));
+    }
+
+    /**
+     * 鍒犻櫎璁惧鏍″噯璁″垝璇︽儏
+     * @return
+     */
+    @ApiOperation(value = "鍒犻櫎璁惧鏍″噯璁″垝璇︽儏")
+    @GetMapping("/delDeviceCalibrationPlanDetail")
+    public Result delDeviceCalibrationPlanDetail(Integer planDetailsId){
+        return Result.success(deviceCalibrationPlanDetailService.removeById(planDetailsId));
+    }
+
+    /**
+     * 瀵煎嚭璁惧鏍″噯璁″垝
+     * @return
+     */
+    @ApiOperation(value = "瀵煎嚭璁惧鏍″噯璁″垝")
+    @GetMapping("/exportDeviceCalibrationPlanDetail")
+    public void exportDeviceCalibrationPlanDetail(Integer planId, HttpServletResponse response){
+        deviceCalibrationPlanService.exportDeviceCalibrationPlanDetail(planId, response);
+    }
+
+}
diff --git a/cnas-device/src/main/java/com/ruoyi/device/dto/DeviceCalibrationPlanDetailDto.java b/cnas-device/src/main/java/com/ruoyi/device/dto/DeviceCalibrationPlanDetailDto.java
new file mode 100644
index 0000000..dd1b783
--- /dev/null
+++ b/cnas-device/src/main/java/com/ruoyi/device/dto/DeviceCalibrationPlanDetailDto.java
@@ -0,0 +1,21 @@
+package com.ruoyi.device.dto;
+
+import com.ruoyi.device.pojo.DeviceCalibrationPlanDetail;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * Author: yuan
+ * Date: 2024-12-17 鏄熸湡浜� 10:05:08
+ * Description:
+ */
+@Data
+public class DeviceCalibrationPlanDetailDto extends DeviceCalibrationPlanDetail {
+
+    @ApiModelProperty("搴忓彿")
+    private Integer index;
+    @ApiModelProperty("鏈�杩戞瀹氭椂闂碨tr")
+    private String lastDateStr;
+    @ApiModelProperty("鏈勾璁″垝鏍″噯鏃堕棿Str")
+    private String planDateStr;
+}
diff --git a/cnas-device/src/main/java/com/ruoyi/device/dto/DeviceCalibrationPlanDto.java b/cnas-device/src/main/java/com/ruoyi/device/dto/DeviceCalibrationPlanDto.java
new file mode 100644
index 0000000..76cba3a
--- /dev/null
+++ b/cnas-device/src/main/java/com/ruoyi/device/dto/DeviceCalibrationPlanDto.java
@@ -0,0 +1,31 @@
+package com.ruoyi.device.dto;
+
+import com.ruoyi.device.pojo.DeviceCalibrationPlan;
+import com.ruoyi.device.pojo.DeviceCalibrationPlanDetail;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @Author zhuo
+ * @Date 2024/12/16
+ */
+@Data
+public class DeviceCalibrationPlanDto extends DeviceCalibrationPlan {
+
+    @ApiModelProperty("缂栧埗浜�")
+    private String writeName;
+
+    @ApiModelProperty("鎵瑰噯浜�")
+    private String ratifyName;
+
+    @ApiModelProperty("缂栧埗鏃堕棿")
+    private String writeTimeStr;
+
+    @ApiModelProperty("鎵瑰噯鏃堕棿")
+    private String ratifyTimeStr;
+
+    @ApiModelProperty("鏍″噯璁″垝璇︽儏")
+    private List<DeviceCalibrationPlanDetail> calibrationPlanDetailList;
+}
diff --git a/cnas-device/src/main/java/com/ruoyi/device/excel/upload/DeviceCalibrationPlanDetailUpload.java b/cnas-device/src/main/java/com/ruoyi/device/excel/upload/DeviceCalibrationPlanDetailUpload.java
new file mode 100644
index 0000000..3636525
--- /dev/null
+++ b/cnas-device/src/main/java/com/ruoyi/device/excel/upload/DeviceCalibrationPlanDetailUpload.java
@@ -0,0 +1,36 @@
+package com.ruoyi.device.excel.upload;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * @Author zhuo
+ * @Date 2024/12/20
+ */
+@Data
+public class DeviceCalibrationPlanDetailUpload {
+
+    @ApiModelProperty("璁惧鍚嶇О鍙婂瀷鍙�")
+    private String deviceName;
+
+    @ApiModelProperty("璁惧鏁伴噺")
+    private String deviceAmount;
+
+    @ApiModelProperty("浠櫒缂栧彿")
+    private String deviceNumber;
+
+    @ApiModelProperty("妫�瀹氬崟浣�")
+    private String verificationUnit;
+
+    @ApiModelProperty("妫�瀹氬懆鏈�")
+    private String verificationCycles;
+
+    @ApiModelProperty("鏈�杩戞瀹氭椂闂�")
+    private String lastDate;
+
+    @ApiModelProperty("鏈勾璁″垝鏍″噯鏃堕棿")
+    private String planDate;
+
+    @ApiModelProperty("澶囨敞")
+    private String remark;
+}
diff --git a/cnas-device/src/main/java/com/ruoyi/device/excel/upload/DeviceExaminePlanUpload.java b/cnas-device/src/main/java/com/ruoyi/device/excel/upload/DeviceExaminePlanUpload.java
new file mode 100644
index 0000000..0434130
--- /dev/null
+++ b/cnas-device/src/main/java/com/ruoyi/device/excel/upload/DeviceExaminePlanUpload.java
@@ -0,0 +1,37 @@
+package com.ruoyi.device.excel.upload;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * @Author zhuo
+ * @Date 2024/12/20
+ */
+@Data
+public class DeviceExaminePlanUpload {
+
+    @ApiModelProperty("璁惧缂栧彿")
+    private String deviceNumber;
+
+    @ApiModelProperty("璁″垝鍚嶇О")
+    private String deviceName;
+
+    @ApiModelProperty("鏍告煡鏃堕棿")
+    private String checkTime;
+
+    @ApiModelProperty("鏍告煡鎸囨爣")
+    private String checkIndex;
+
+    @ApiModelProperty("鏍告煡鏂规硶")
+    private String checkMethod;
+
+    @ApiModelProperty("缁撴灉濡備綍鍒ゅ畾")
+    private String howResults;
+
+    @ApiModelProperty("鏍告煡璐d换浜�")
+    private String checkChargerUser;
+
+    @ApiModelProperty("澶囨敞")
+    private String remark;
+
+}
diff --git a/cnas-device/src/main/java/com/ruoyi/device/mapper/DeviceCalibrationPlanDetailMapper.java b/cnas-device/src/main/java/com/ruoyi/device/mapper/DeviceCalibrationPlanDetailMapper.java
new file mode 100644
index 0000000..3f45575
--- /dev/null
+++ b/cnas-device/src/main/java/com/ruoyi/device/mapper/DeviceCalibrationPlanDetailMapper.java
@@ -0,0 +1,27 @@
+package com.ruoyi.device.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.device.pojo.DeviceCalibrationPlanDetail;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * <p>
+ * 璁惧鏍″噯璁″垝璇︽儏琛� Mapper 鎺ュ彛
+ * </p>
+ *
+ * @author 姹熻嫃榈烽洀缃戠粶绉戞妧鏈夐檺鍏徃
+ * @since 2024-12-16 03:58:29
+ */
+public interface DeviceCalibrationPlanDetailMapper extends BaseMapper<DeviceCalibrationPlanDetail> {
+
+    /**
+     * 璁惧鏍″噯璁″垝璇︽儏鍒楄〃
+     * @param page
+     * @param ew
+     * @return
+     */
+    IPage<DeviceCalibrationPlanDetail> pageDeviceCalibrationPlanDetail(Page page, @Param("ew") QueryWrapper<DeviceCalibrationPlanDetail> ew);
+}
diff --git a/cnas-device/src/main/java/com/ruoyi/device/mapper/DeviceCalibrationPlanMapper.java b/cnas-device/src/main/java/com/ruoyi/device/mapper/DeviceCalibrationPlanMapper.java
new file mode 100644
index 0000000..deedc46
--- /dev/null
+++ b/cnas-device/src/main/java/com/ruoyi/device/mapper/DeviceCalibrationPlanMapper.java
@@ -0,0 +1,29 @@
+package com.ruoyi.device.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.device.dto.DeviceCalibrationPlanDto;
+import com.ruoyi.device.pojo.DeviceCalibrationPlan;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * <p>
+ * 璁惧鏍″噯璁″垝涓昏〃 Mapper 鎺ュ彛
+ * </p>
+ *
+ * @author 姹熻嫃榈烽洀缃戠粶绉戞妧鏈夐檺鍏徃
+ * @since 2024-12-16 03:58:17
+ */
+public interface DeviceCalibrationPlanMapper extends BaseMapper<DeviceCalibrationPlan> {
+
+    /**
+     * 璁惧鏍″噯璁″垝鍒楄〃
+     * @param page
+     * @param ew
+     * @return
+     */
+    IPage<DeviceCalibrationPlanDto> pageDeviceCalibrationPlan(Page page, @Param("ew") QueryWrapper<DeviceCalibrationPlan> ew);
+
+}
diff --git a/cnas-device/src/main/java/com/ruoyi/device/pojo/DeviceCalibrationPlan.java b/cnas-device/src/main/java/com/ruoyi/device/pojo/DeviceCalibrationPlan.java
new file mode 100644
index 0000000..2bfef94
--- /dev/null
+++ b/cnas-device/src/main/java/com/ruoyi/device/pojo/DeviceCalibrationPlan.java
@@ -0,0 +1,67 @@
+package com.ruoyi.device.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 璁惧鏍″噯璁″垝涓昏〃
+ * </p>
+ *
+ * @author 姹熻嫃榈烽洀缃戠粶绉戞妧鏈夐檺鍏徃
+ * @since 2024-12-16 03:58:17
+ */
+@Getter
+@Setter
+@TableName("device_calibration_plan")
+@ApiModel(value = "DeviceCalibrationPlan瀵硅薄", description = "璁惧鏍″噯璁″垝涓昏〃")
+public class DeviceCalibrationPlan{
+
+    @TableId(value = "plan_id", type = IdType.AUTO)
+    private Integer planId;
+
+    @ApiModelProperty("璁″垝鍚嶇О")
+    private String planName;
+
+    @ApiModelProperty("璁″垝骞翠唤")
+    private String planYear;
+
+    @ApiModelProperty("缂栧埗浜�")
+    private Integer writeUserId;
+
+    @ApiModelProperty("缂栧埗鏃堕棿")
+    private LocalDateTime writeTime;
+
+    @ApiModelProperty("鎵瑰噯浜�")
+    private Integer ratifyUserId;
+
+    @ApiModelProperty("鎵瑰噯鏃堕棿")
+    private LocalDateTime ratifyTime;
+
+    @ApiModelProperty("鎵瑰噯鐘舵��,0 涓嶉�氳繃, 1 閫氳繃")
+    private Integer ratifyStatus;
+
+    @ApiModelProperty("鎵瑰噯淇℃伅")
+    private String ratifyRemark;
+
+    @ApiModelProperty("鍒涘缓浜�")
+    @TableField(fill = FieldFill.INSERT)
+    private Integer createUser;
+
+    @ApiModelProperty("鍒涘缓鏃堕棿")
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("淇敼浜�")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Integer updateUser;
+
+    @ApiModelProperty("淇敼鏃堕棿")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+}
diff --git a/cnas-device/src/main/java/com/ruoyi/device/pojo/DeviceCalibrationPlanDetail.java b/cnas-device/src/main/java/com/ruoyi/device/pojo/DeviceCalibrationPlanDetail.java
new file mode 100644
index 0000000..01a0718
--- /dev/null
+++ b/cnas-device/src/main/java/com/ruoyi/device/pojo/DeviceCalibrationPlanDetail.java
@@ -0,0 +1,74 @@
+package com.ruoyi.device.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 璁惧鏍″噯璁″垝璇︽儏琛�
+ * </p>
+ *
+ * @author 姹熻嫃榈烽洀缃戠粶绉戞妧鏈夐檺鍏徃
+ * @since 2024-12-16 03:58:29
+ */
+@Getter
+@Setter
+@TableName("device_calibration_plan_detail")
+@ApiModel(value = "DeviceCalibrationPlanDetail瀵硅薄", description = "璁惧鏍″噯璁″垝璇︽儏琛�")
+public class DeviceCalibrationPlanDetail{
+
+    @TableId(value = "plan_detail_id", type = IdType.AUTO)
+    private Integer planDetailId;
+
+    @ApiModelProperty("璁″垝涓昏〃id")
+    private Integer planId;
+
+    @ApiModelProperty("璁惧id")
+    private Integer deviceId;
+
+    @ApiModelProperty("璁惧鍚嶇О鍙婂瀷鍙�")
+    private String deviceName;
+
+    @ApiModelProperty("璁惧鏁伴噺")
+    private String deviceAmount;
+
+    @ApiModelProperty("浠櫒缂栧彿")
+    private String deviceNumber;
+
+    @ApiModelProperty("妫�瀹氬崟浣�")
+    private String verificationUnit;
+
+    @ApiModelProperty("妫�瀹氬懆鏈�")
+    private String verificationCycles;
+
+    @ApiModelProperty("鏈�杩戞瀹氭椂闂�")
+    private LocalDate lastDate;
+
+    @ApiModelProperty("鏈勾璁″垝鏍″噯鏃堕棿")
+    private LocalDate planDate;
+
+    @ApiModelProperty("澶囨敞")
+    private String remark;
+
+    @ApiModelProperty("鍒涘缓浜�")
+    @TableField(fill = FieldFill.INSERT)
+    private Integer createUser;
+
+    @ApiModelProperty("鍒涘缓鏃堕棿")
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("淇敼浜�")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Integer updateUser;
+
+    @ApiModelProperty("淇敼鏃堕棿")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+}
diff --git a/cnas-device/src/main/java/com/ruoyi/device/service/DeviceCalibrationPlanDetailService.java b/cnas-device/src/main/java/com/ruoyi/device/service/DeviceCalibrationPlanDetailService.java
new file mode 100644
index 0000000..f696dd4
--- /dev/null
+++ b/cnas-device/src/main/java/com/ruoyi/device/service/DeviceCalibrationPlanDetailService.java
@@ -0,0 +1,16 @@
+package com.ruoyi.device.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.device.pojo.DeviceCalibrationPlanDetail;
+
+/**
+ * <p>
+ * 璁惧鏍″噯璁″垝璇︽儏琛� 鏈嶅姟绫�
+ * </p>
+ *
+ * @author 姹熻嫃榈烽洀缃戠粶绉戞妧鏈夐檺鍏徃
+ * @since 2024-12-16 03:58:29
+ */
+public interface DeviceCalibrationPlanDetailService extends IService<DeviceCalibrationPlanDetail> {
+
+}
diff --git a/cnas-device/src/main/java/com/ruoyi/device/service/DeviceCalibrationPlanService.java b/cnas-device/src/main/java/com/ruoyi/device/service/DeviceCalibrationPlanService.java
new file mode 100644
index 0000000..0e66f43
--- /dev/null
+++ b/cnas-device/src/main/java/com/ruoyi/device/service/DeviceCalibrationPlanService.java
@@ -0,0 +1,89 @@
+package com.ruoyi.device.service;
+
+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.device.dto.DeviceCalibrationPlanDto;
+import com.ruoyi.device.pojo.DeviceCalibrationPlan;
+import com.ruoyi.device.pojo.DeviceCalibrationPlanDetail;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * <p>
+ * 璁惧鏍″噯璁″垝涓昏〃 鏈嶅姟绫�
+ * </p>
+ *
+ * @author 姹熻嫃榈烽洀缃戠粶绉戞妧鏈夐檺鍏徃
+ * @since 2024-12-16 03:58:17
+ */
+public interface DeviceCalibrationPlanService extends IService<DeviceCalibrationPlan> {
+
+    /**
+     * 鏂板璁惧鏍″噯璁″垝
+     * @param calibrationPlanDto
+     * @return
+     */
+    boolean addDeviceCalibrationPlan(DeviceCalibrationPlanDto calibrationPlanDto);
+
+    /**
+     * 瀵煎叆璁惧鏍″噯璁″垝
+     * @param file
+     * @return
+     */
+    boolean importDeviceCalibrationPlan(MultipartFile file, String planYear);
+
+    /**
+     * 鎻愪氦鎵瑰噯
+     * @param deviceCalibrationPlan
+     * @return
+     */
+    boolean submiatRatifyDeviceCalibrationPlan(DeviceCalibrationPlan deviceCalibrationPlan);
+
+    /**
+     * 璁惧鏍″噯璁″垝鎵瑰噯
+     * @param DeviceCalibrationPlan
+     * @return
+     */
+    boolean ratifyDeviceCalibrationPlan(DeviceCalibrationPlan DeviceCalibrationPlan);
+
+    /**
+     * 璁惧鏍″噯璁″垝鍒楄〃
+     * @param page
+     * @param DeviceCalibrationPlan
+     * @return
+     */
+    IPage<DeviceCalibrationPlanDto> pageDeviceCalibrationPlan(Page page, DeviceCalibrationPlan deviceCalibrationPlan);
+
+    /**
+     * 璁惧鏍″噯璁″垝璇︽儏鍒楄〃
+     * @param page
+     * @param DeviceCalibrationPlanDetails
+     * @return
+     */
+    IPage<DeviceCalibrationPlanDetail> pageDeviceCalibrationPlanDetail(Page page, DeviceCalibrationPlanDetail deviceCalibrationPlanDetails);
+
+
+    /**
+     * 瀵煎嚭璁惧鏍″噯璁″垝
+     *
+     * @param DeviceCalibrationPlanId
+     * @param response
+     */
+    void exportDeviceCalibrationPlanDetail(Integer DeviceCalibrationPlanId, HttpServletResponse response);
+
+    /**
+     * 鎵归噺缂栬緫璁惧鏍″噯璇︽儏
+     * @param calibrationPlanDto
+     * @return
+     */
+    boolean updateDeviceCalibrationPlan(DeviceCalibrationPlanDto calibrationPlanDto);
+
+    /**
+     * 鏌ヨ璁惧鏍″噯璇︽儏
+     * @param planId
+     * @return
+     */
+    DeviceCalibrationPlanDto getDeviceCalibrationPlan(Integer planId);
+}
diff --git a/cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceCalibrationPlanDetailServiceImpl.java b/cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceCalibrationPlanDetailServiceImpl.java
new file mode 100644
index 0000000..8c4b2da
--- /dev/null
+++ b/cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceCalibrationPlanDetailServiceImpl.java
@@ -0,0 +1,20 @@
+package com.ruoyi.device.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.device.mapper.DeviceCalibrationPlanDetailMapper;
+import com.ruoyi.device.pojo.DeviceCalibrationPlanDetail;
+import com.ruoyi.device.service.DeviceCalibrationPlanDetailService;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * 璁惧鏍″噯璁″垝璇︽儏琛� 鏈嶅姟瀹炵幇绫�
+ * </p>
+ *
+ * @author 姹熻嫃榈烽洀缃戠粶绉戞妧鏈夐檺鍏徃
+ * @since 2024-12-16 03:58:29
+ */
+@Service
+public class DeviceCalibrationPlanDetailServiceImpl extends ServiceImpl<DeviceCalibrationPlanDetailMapper, DeviceCalibrationPlanDetail> implements DeviceCalibrationPlanDetailService {
+
+}
diff --git a/cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceCalibrationPlanServiceImpl.java b/cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceCalibrationPlanServiceImpl.java
new file mode 100644
index 0000000..a31d3da
--- /dev/null
+++ b/cnas-device/src/main/java/com/ruoyi/device/service/impl/DeviceCalibrationPlanServiceImpl.java
@@ -0,0 +1,353 @@
+package com.ruoyi.device.service.impl;
+
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.context.AnalysisContext;
+import com.alibaba.excel.event.AnalysisEventListener;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+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;
+import com.deepoove.poi.config.Configure;
+import com.ruoyi.common.core.domain.entity.InformationNotification;
+import com.ruoyi.common.core.domain.entity.User;
+import com.ruoyi.common.utils.QueryWrappers;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.WxCpUtils;
+import com.ruoyi.device.dto.DeviceCalibrationPlanDetailDto;
+import com.ruoyi.device.dto.DeviceCalibrationPlanDto;
+import com.ruoyi.device.excel.upload.DeviceCalibrationPlanDetailUpload;
+import com.ruoyi.device.mapper.DeviceCalibrationPlanDetailMapper;
+import com.ruoyi.device.mapper.DeviceCalibrationPlanMapper;
+import com.ruoyi.device.pojo.DeviceCalibrationPlan;
+import com.ruoyi.device.pojo.DeviceCalibrationPlanDetail;
+import com.ruoyi.device.service.DeviceCalibrationPlanDetailService;
+import com.ruoyi.device.service.DeviceCalibrationPlanService;
+import com.ruoyi.inspect.util.HackLoopTableRenderPolicy;
+import com.ruoyi.inspect.util.UserUtils;
+import com.ruoyi.system.mapper.UserMapper;
+import com.ruoyi.system.service.InformationNotificationService;
+import org.springframework.beans.BeanUtils;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URLEncoder;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * <p>
+ * 璁惧鏍″噯璁″垝涓昏〃 鏈嶅姟瀹炵幇绫�
+ * </p>
+ *
+ * @author 姹熻嫃榈烽洀缃戠粶绉戞妧鏈夐檺鍏徃
+ * @since 2024-12-16 03:58:17
+ */
+@Service
+@Transactional(rollbackFor = Exception.class)
+public class DeviceCalibrationPlanServiceImpl extends ServiceImpl<DeviceCalibrationPlanMapper, DeviceCalibrationPlan> implements DeviceCalibrationPlanService {
+
+    @Resource
+    private DeviceCalibrationPlanDetailMapper deviceCalibrationPlanDetailMapper;
+    @Resource
+    private DeviceCalibrationPlanDetailService deviceCalibrationPlanDetailService;
+    @Resource
+    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
+    @Resource
+    private InformationNotificationService informationNotificationService;
+    @Resource
+    private UserMapper userMapper;
+
+
+    /**
+     * 鏂板璁惧鏍″噯璁″垝
+     * @param calibrationPlanDto
+     * @return
+     */
+    @Override
+    public boolean addDeviceCalibrationPlan(DeviceCalibrationPlanDto calibrationPlanDto) {
+        // 褰撳墠鐧诲綍鐢ㄦ埛
+        Integer userId = SecurityUtils.getUserId().intValue();
+        // 缂栧埗鏃ユ湡
+        calibrationPlanDto.setWriteUserId(userId);
+        calibrationPlanDto.setWriteTime(LocalDateTime.now());
+        baseMapper.insert(calibrationPlanDto);
+
+        // 娣诲姞璇︽儏
+        if (CollectionUtils.isNotEmpty(calibrationPlanDto.getCalibrationPlanDetailList())) {
+            for (DeviceCalibrationPlanDetail calibrationPlanDetail : calibrationPlanDto.getCalibrationPlanDetailList()) {
+                calibrationPlanDetail.setPlanId(calibrationPlanDto.getPlanId());
+            }
+            deviceCalibrationPlanDetailService.saveBatch(calibrationPlanDto.getCalibrationPlanDetailList());
+        }
+
+        return true;
+    }
+
+    /**
+     * 瀵煎叆璁惧鏍″噯璁″垝
+     * @param file
+     * @return
+     */
+    @Override
+    public boolean importDeviceCalibrationPlan(MultipartFile file, String planYear) {
+        // 褰撳墠鐧诲綍鐢ㄦ埛
+        Integer userId = SecurityUtils.getUserId().intValue();
+        // 鏂囦欢鍚嶇О
+        String fileName = file.getOriginalFilename().substring(0, file.getOriginalFilename().lastIndexOf("."));
+        DeviceCalibrationPlan calibrationPlan = new DeviceCalibrationPlan();
+        calibrationPlan.setPlanName(fileName);
+        calibrationPlan.setPlanYear(planYear);
+        calibrationPlan.setWriteUserId(userId);
+        calibrationPlan.setWriteTime(LocalDateTime.now());
+        baseMapper.insert(calibrationPlan);
+
+        List<DeviceCalibrationPlanDetail> detailsUploadList = new ArrayList<>();
+        // 瀵煎叆闄勪欢鍐呭
+        try {
+            // excel瑙f瀽
+            EasyExcel.read(file.getInputStream(), DeviceCalibrationPlanDetailUpload.class, new AnalysisEventListener<DeviceCalibrationPlanDetailUpload>() {
+                @Override
+                public void invoke(DeviceCalibrationPlanDetailUpload detailsUpload, AnalysisContext analysisContext) {
+                    // 鍒ゆ柇鏄惁涓虹┖
+                    if (StringUtils.isNotBlank(detailsUpload.getDeviceName()) &&
+                            StringUtils.isNotBlank(detailsUpload.getDeviceNumber())) {
+                        // 瀵硅薄澶嶅埗
+                        DeviceCalibrationPlanDetail calibrationPlanDetail = new DeviceCalibrationPlanDetail();
+                        BeanUtils.copyProperties(detailsUpload, calibrationPlanDetail);
+                        // 鏍煎紡璇濇渶杩戞瀹氭椂闂村拰鏈勾璁″垝鏍″噯鏃堕棿
+                        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd");
+                        calibrationPlanDetail.setLastDate(LocalDate.parse(detailsUpload.getLastDate(), formatter));
+                        calibrationPlanDetail.setPlanDate(LocalDate.parse(detailsUpload.getLastDate(), formatter));
+
+                        calibrationPlanDetail.setPlanId(calibrationPlan.getPlanId());
+                        detailsUploadList.add(calibrationPlanDetail);
+                    }
+                }
+
+                @Override
+                public void doAfterAllAnalysed(AnalysisContext analysisContext) {
+
+                }
+            }).sheet().doRead();
+            deviceCalibrationPlanDetailService.saveBatch(detailsUploadList);
+
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        return true;
+    }
+
+    /**
+     * 鎻愪氦鎵瑰噯
+     * @param deviceCalibrationPlan
+     * @return
+     */
+    @Override
+    public boolean submiatRatifyDeviceCalibrationPlan(DeviceCalibrationPlan deviceCalibrationPlan) {
+        baseMapper.update(null, Wrappers.<DeviceCalibrationPlan>lambdaUpdate()
+                .eq(DeviceCalibrationPlan::getPlanId, deviceCalibrationPlan.getPlanId())
+                .set(DeviceCalibrationPlan::getRatifyUserId, deviceCalibrationPlan.getRatifyUserId())
+                .set(DeviceCalibrationPlan::getRatifyRemark, null)
+                .set(DeviceCalibrationPlan::getRatifyStatus, null)
+                .set(DeviceCalibrationPlan::getRatifyTime, null)
+        );
+
+        DeviceCalibrationPlan calibrationPlan = baseMapper.selectById(deviceCalibrationPlan.getPlanId());
+
+        // 鍙戦�佹秷鎭�
+        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(calibrationPlan.getPlanName() + "鐨勮澶囨牎鍑嗚鍒掑緟鎵瑰噯");
+        info.setSenderId(userId);
+        // 鎺ユ敹浜�
+        info.setConsigneeId(deviceCalibrationPlan.getRatifyUserId());
+        info.setJumpPath("a6-device");
+        informationNotificationService.addInformationNotification(info);
+
+        // 鍙戦�佷紒涓氬井淇¢�氱煡
+        threadPoolTaskExecutor.execute(() -> {
+            // 鏌ヨ鎺ユ敹浜�
+            User personnel = userMapper.selectById(deviceCalibrationPlan.getPlanId());
+
+            String message = "";
+            message += "CNAS璁惧鏍″噯璁″垝鎵瑰噯閫氱煡";
+            message += "\n璇峰幓璧勬簮绠$悊-璁惧涓�灞傜洰褰�-璁惧鏍″噯璁″垝濉啓";
+            message += "\n" + calibrationPlan.getPlanName() + "鐨勬牎鍑嗚鍒掑緟鎵瑰噯";
+            //鍙戦�佷紒涓氬井淇℃秷鎭�氱煡
+            try {
+                WxCpUtils.inform(personnel.getAccount(), message, null);
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        });
+        return true;
+    }
+
+    /**
+     * 璁惧鏍″噯璁″垝鎵瑰噯
+     * @param deviceCalibrationPlan
+     * @return
+     */
+    @Override
+    public boolean ratifyDeviceCalibrationPlan(DeviceCalibrationPlan deviceCalibrationPlan) {
+        baseMapper.update(null, Wrappers.<DeviceCalibrationPlan>lambdaUpdate()
+                .eq(DeviceCalibrationPlan::getPlanId, deviceCalibrationPlan.getPlanId())
+                .set(DeviceCalibrationPlan::getRatifyRemark, deviceCalibrationPlan.getRatifyRemark())
+                .set(DeviceCalibrationPlan::getRatifyStatus, deviceCalibrationPlan.getRatifyStatus())
+                .set(DeviceCalibrationPlan::getRatifyTime, LocalDateTime.now())
+        );
+        return true;
+    }
+
+    /**
+     * 璁惧鏍″噯璁″垝鍒楄〃
+     * @param page
+     * @param deviceCalibrationPlan
+     * @return
+     */
+    @Override
+    public IPage<DeviceCalibrationPlanDto> pageDeviceCalibrationPlan(Page page, DeviceCalibrationPlan deviceCalibrationPlan) {
+        return baseMapper.pageDeviceCalibrationPlan(page, QueryWrappers.queryWrappers(deviceCalibrationPlan));
+    }
+
+    /**
+     * 璁惧鏍″噯璁″垝璇︽儏鍒楄〃
+     * @param page
+     * @param deviceCalibrationPlanDetails
+     * @return
+     */
+    @Override
+    public IPage<DeviceCalibrationPlanDetail> pageDeviceCalibrationPlanDetail(Page page, DeviceCalibrationPlanDetail deviceCalibrationPlanDetails) {
+        if (deviceCalibrationPlanDetails.getPlanId() == null) {
+            return new Page();
+        }
+        return deviceCalibrationPlanDetailMapper.pageDeviceCalibrationPlanDetail(page, QueryWrappers.queryWrappers(deviceCalibrationPlanDetails));
+    }
+
+    /**
+     * 瀵煎嚭璁惧鏍″噯璁″垝
+     * @param deviceCalibrationPlanId
+     * @param response
+     */
+    @Override
+    public void exportDeviceCalibrationPlanDetail(Integer deviceCalibrationPlanId, HttpServletResponse response) {
+        // 鏌ヨ璁惧鏍″噯璁″垝
+        DeviceCalibrationPlan deviceCalibrationPlan = baseMapper.selectById(deviceCalibrationPlanId);
+        DeviceCalibrationPlanDto deviceCalibrationPlanDto = new DeviceCalibrationPlanDto();
+        BeanUtils.copyProperties(deviceCalibrationPlan, deviceCalibrationPlanDto);
+        // 璁剧疆缂栧埗鍜屾壒鍑嗘椂闂存牸寮�
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+        deviceCalibrationPlanDto.setWriteTimeStr(deviceCalibrationPlan.getWriteTime() == null ? null : deviceCalibrationPlan.getWriteTime().format(formatter));
+        deviceCalibrationPlanDto.setRatifyTimeStr(deviceCalibrationPlan.getRatifyTime() == null ? null : deviceCalibrationPlan.getRatifyTime().format(formatter));
+
+        // 鏌ヨ璁惧鏍″噯璁″垝璇︽儏
+        List<DeviceCalibrationPlanDetail> deviceCalibrationPlanDetailList = deviceCalibrationPlanDetailMapper.selectList(Wrappers.<DeviceCalibrationPlanDetail>lambdaQuery().eq(DeviceCalibrationPlanDetail::getPlanId, deviceCalibrationPlanId));
+        // 璁剧疆搴忓彿 鍜� 鏃堕棿
+        ArrayList<DeviceCalibrationPlanDetailDto> deviceCalibrationPlanDetailDtoList = new ArrayList<>();
+        deviceCalibrationPlanDetailList.forEach(deviceCalibrationPlanDetail -> {
+            DeviceCalibrationPlanDetailDto deviceCalibrationPlanDetailDto = new DeviceCalibrationPlanDetailDto();
+            BeanUtils.copyProperties(deviceCalibrationPlanDetail, deviceCalibrationPlanDetailDto);
+            deviceCalibrationPlanDetailDto.setIndex(deviceCalibrationPlanDetailList.indexOf(deviceCalibrationPlanDetail) + 1);
+            deviceCalibrationPlanDetailDto.setLastDateStr((deviceCalibrationPlanDetail.getLastDate().format(formatter)));
+            deviceCalibrationPlanDetailDto.setPlanDateStr((deviceCalibrationPlanDetail.getPlanDate().format(formatter)));
+            deviceCalibrationPlanDetailDtoList.add(deviceCalibrationPlanDetailDto);
+        });
+
+        // 鑾峰彇璺緞
+        InputStream inputStream = this.getClass().getResourceAsStream("/static/word/device-calibration-plan.docx");
+        Configure configure = Configure.builder()
+                .bind("deviceCalibrationPlanDetailDtoList", new HackLoopTableRenderPolicy())
+                .build();
+        XWPFTemplate template = XWPFTemplate.compile(inputStream, configure).render(
+                new HashMap<String, Object>() {{
+                    put("deviceCalibrationPlan", deviceCalibrationPlanDto);
+                    put("deviceCalibrationPlanDetailDtoList", deviceCalibrationPlanDetailDtoList);
+                    //鑾峰彇缂栧埗浜虹殑绛惧悕鍦板潃
+                    put("organizationUrl", UserUtils.getFinalUserSignatureUrl(deviceCalibrationPlan.getRatifyUserId()));
+                    //鑾峰彇鎵瑰噯浜虹殑绛惧悕鍦板潃
+                    put("approvedUrl", UserUtils.getFinalUserSignatureUrl(deviceCalibrationPlan.getWriteUserId()));
+                }});
+
+        try {
+            response.setContentType("application/msword");
+            String fileName = URLEncoder.encode(
+                    "璁惧鏍″噯璁″垝琛�", "UTF-8");
+            response.setHeader("Content-disposition",
+                    "attachment;filename=" + fileName + ".docx");
+            OutputStream os = response.getOutputStream();
+            template.write(os);
+            os.flush();
+            os.close();
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException("瀵煎嚭澶辫触");
+        }
+    }
+
+    /**
+     * 鎵归噺缂栬緫璁惧鏍″噯
+     * @param calibrationPlanDto
+     * @return
+     */
+    @Override
+    public boolean updateDeviceCalibrationPlan(DeviceCalibrationPlanDto calibrationPlanDto) {
+        // 褰撳墠鐧诲綍鐢ㄦ埛
+        Integer userId = SecurityUtils.getUserId().intValue();
+        // 缂栧埗鏃ユ湡
+        calibrationPlanDto.setWriteUserId(userId);
+        calibrationPlanDto.setWriteTime(LocalDateTime.now());
+        baseMapper.updateById(calibrationPlanDto);
+
+        // 鍒犻櫎鍘熸湰鐨勮鎯�
+        deviceCalibrationPlanDetailService.remove(Wrappers.<DeviceCalibrationPlanDetail>lambdaQuery().eq(DeviceCalibrationPlanDetail::getPlanId, calibrationPlanDto.getPlanId()));
+
+        // 娣诲姞璇︽儏
+        if (CollectionUtils.isNotEmpty(calibrationPlanDto.getCalibrationPlanDetailList())) {
+            for (DeviceCalibrationPlanDetail calibrationPlanDetail : calibrationPlanDto.getCalibrationPlanDetailList()) {
+                calibrationPlanDetail.setPlanId(calibrationPlanDto.getPlanId());
+            }
+            deviceCalibrationPlanDetailService.saveBatch(calibrationPlanDto.getCalibrationPlanDetailList());
+        }
+        return true;
+    }
+
+    /**
+     * 鏌ヨ璁惧鏍″噯璇︽儏
+     * @param planId
+     * @return
+     */
+    @Override
+    public DeviceCalibrationPlanDto getDeviceCalibrationPlan(Integer planId) {
+        DeviceCalibrationPlan calibrationPlan = baseMapper.selectById(planId);
+        DeviceCalibrationPlanDto deviceCalibrationPlanDto = new DeviceCalibrationPlanDto();
+        BeanUtils.copyProperties(calibrationPlan, deviceCalibrationPlanDto);
+        // 鏌ヨ璇︽儏
+        List<DeviceCalibrationPlanDetail> list = deviceCalibrationPlanDetailService.list(Wrappers.<DeviceCalibrationPlanDetail>lambdaQuery()
+                .eq(DeviceCalibrationPlanDetail::getPlanId, planId)
+                .orderByAsc(DeviceCalibrationPlanDetail::getPlanDate));
+
+        deviceCalibrationPlanDto.setCalibrationPlanDetailList(list);
+        return deviceCalibrationPlanDto;
+    }
+
+}
diff --git a/cnas-device/src/main/resources/mapper/DeviceCalibrationPlanDetailMapper.xml b/cnas-device/src/main/resources/mapper/DeviceCalibrationPlanDetailMapper.xml
new file mode 100644
index 0000000..d6bce8c
--- /dev/null
+++ b/cnas-device/src/main/resources/mapper/DeviceCalibrationPlanDetailMapper.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.device.mapper.DeviceCalibrationPlanDetailMapper">
+
+    <!-- 璁惧鏍″噯璁″垝璇︽儏鍒楄〃 -->
+    <select id="pageDeviceCalibrationPlanDetail" resultType="com.ruoyi.device.pojo.DeviceCalibrationPlanDetail">
+        select * from (
+        select *
+        from device_calibration_plan_detail
+        order by plan_date asc
+        ) a
+        <if test="ew.customSqlSegment != null and ew.customSqlSegment != ''">
+            ${ew.customSqlSegment}
+        </if>
+    </select>
+
+</mapper>
diff --git a/cnas-device/src/main/resources/mapper/DeviceCalibrationPlanMapper.xml b/cnas-device/src/main/resources/mapper/DeviceCalibrationPlanMapper.xml
new file mode 100644
index 0000000..bf7cf39
--- /dev/null
+++ b/cnas-device/src/main/resources/mapper/DeviceCalibrationPlanMapper.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.device.mapper.DeviceCalibrationPlanMapper">
+
+    <!-- 璁惧鏍″噯璁″垝鍒楄〃 -->
+    <select id="pageDeviceCalibrationPlan" resultType="com.ruoyi.device.dto.DeviceCalibrationPlanDto">
+        select *
+        from (select cqm.*,
+        u1.name write_name,
+        u3.name ratify_name
+        from device_calibration_plan cqm
+        left join user u1 on u1.id = cqm.write_user_id
+        left join user u3 on u3.id = cqm.ratify_user_id
+        order by cqm.create_time desc) a
+        <if test="ew.customSqlSegment != null and ew.customSqlSegment != ''">
+            ${ew.customSqlSegment}
+        </if>
+    </select>
+
+</mapper>
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/util/UserUtils.java b/inspect-server/src/main/java/com/ruoyi/inspect/util/UserUtils.java
new file mode 100644
index 0000000..4ea9c59
--- /dev/null
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/util/UserUtils.java
@@ -0,0 +1,90 @@
+package com.ruoyi.inspect.util;
+
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.deepoove.poi.data.PictureRenderData;
+import com.deepoove.poi.data.Pictures;
+import com.ruoyi.common.core.domain.entity.User;
+import com.ruoyi.framework.exception.ErrorException;
+import com.ruoyi.system.mapper.UserMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+/**
+ * Author: yuan
+ * Date: 2024-12-17 鏄熸湡浜� 10:35:50
+ * Description: User宸ュ叿绫�
+ */
+@Component
+public class UserUtils {
+    private static UserMapper userMapper;
+
+    private static String imgUrl;
+
+    @Autowired
+    public void setUserMapper(UserMapper userMapper) {
+        UserUtils.userMapper = userMapper;
+    }
+
+    @Autowired
+    public void setImgUrl(@Value("${file.path}") String imgUrl) {
+        UserUtils.imgUrl = imgUrl;
+    }
+
+    /**
+     * 閫氳繃浜哄憳id鑾峰彇鐢ㄦ埛绛惧悕鍦板潃
+     * @param userId 浜哄憳id
+     * @return 鐢ㄦ埛绛惧悕鍦板潃
+     */
+    public static String getUserSignatureUrl(Integer userId) {
+        String userSignatureUrl = null;
+        if (userId != null) {
+            userSignatureUrl = userMapper.selectOne(Wrappers.<User>lambdaQuery()
+                            .eq(User::getId, userId))
+                    .getSignatureUrl();
+            if (StringUtils.isBlank(userSignatureUrl)) {
+                throw new ErrorException("鎵句笉鍒拌浜哄憳绛惧悕");
+            }
+            return imgUrl + "\\" + userSignatureUrl;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * 閫氳繃浜哄憳id鑾峰彇娓叉煋Word鐢ㄦ埛绛惧悕瀵硅薄
+     * @param userId 浜哄憳id
+     * @return 鐢ㄦ埛绛惧悕瀵硅薄 or null
+     */
+    public static PictureRenderData getFinalUserSignatureUrl(Integer userId) {
+        String userSignatureUrl = null;
+        if (userId != null) {
+            userSignatureUrl = userMapper.selectById(userId)
+                    .getSignatureUrl();
+            if (StringUtils.isBlank(userSignatureUrl)) {
+                throw new ErrorException("鎵句笉鍒拌浜哄憳绛惧悕");
+            }
+        }
+        return StringUtils.isNotBlank(userSignatureUrl) ? Pictures.ofLocal(imgUrl + "/" + userSignatureUrl).create() : null;
+    }
+
+
+    /**
+     * 閫氳繃鍚嶅瓧鑾峰彇娓叉煋Word鐢ㄦ埛绛惧悕瀵硅薄
+     * @param userName 浜哄憳鍚嶅瓧
+     * @return 鐢ㄦ埛绛惧悕瀵硅薄 or null
+     */
+    public static PictureRenderData getFinalUserSignatureUrl(String userName) {
+        String userSignatureUrl = null;
+        if (userName != null) {
+            userSignatureUrl = userMapper.selectOne(Wrappers.<User>lambdaQuery()
+                            .eq(User::getName, userName))
+                    .getSignatureUrl();
+            if (StringUtils.isBlank(userSignatureUrl)) {
+                throw new ErrorException("鎵句笉鍒拌浜哄憳绛惧悕");
+            }
+        }
+        return StringUtils.isNotBlank(userSignatureUrl) ? Pictures.ofLocal(imgUrl + "/" + userSignatureUrl).create() : null;
+    }
+}
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/util/XWPFDocumentUtils.java b/inspect-server/src/main/java/com/ruoyi/inspect/util/XWPFDocumentUtils.java
new file mode 100644
index 0000000..386c987
--- /dev/null
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/util/XWPFDocumentUtils.java
@@ -0,0 +1,119 @@
+package com.ruoyi.inspect.util;
+
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import org.apache.poi.xwpf.usermodel.*;
+import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;
+
+import java.util.*;
+
+/**
+ * @Author zhuo
+ * @Date 2024/11/16
+ */
+public class XWPFDocumentUtils {
+
+    public static void updateMergeByDocument(XWPFDocument document) {
+        // 澶勭悊鍚堝苟鍗曞厓鏍肩殑闂
+        List<XWPFTable> xwpfTables = document.getTables();
+        for (int i = 0; i < xwpfTables.size(); i++) {
+            Set<String> set1 = new HashSet<>();
+            Map<String, Map<String, Integer>> maps = new HashMap<>();
+            for (int j = 0; j < xwpfTables.get(i).getRows().size(); j++) {
+                for (int k = 0; k < xwpfTables.get(i).getRows().get(j).getTableCells().size(); k++) {
+                    if (xwpfTables.get(i).getRows().get(j).getTableCells().get(k).getText().contains("鈭�")) {
+                        String[] split = xwpfTables.get(i).getRows().get(j).getTableCells().get(k).getText().split("鈭�");
+                        if (set1.add(split[1])) {
+                            Map<String, Integer> map = new HashMap<>();
+                            map.put("sr", j);
+                            map.put("sc", k);
+                            map.put("er", j + 0);
+                            map.put("ec", k + 0);
+                            maps.put(split[1], map);
+                        } else {
+                            Map<String, Integer> map1 = maps.get(split[1]);
+                            if (j == map1.get("sr")) {
+                                map1.put("ec", map1.get("ec") + 1);
+                            } else if (k == map1.get("sc")) {
+                                map1.put("er", map1.get("er") + 1);
+                            }
+                        }
+                        String str = xwpfTables.get(i).getRows().get(j).getTableCells().get(k).getText().split("鈭�")[0];
+                        xwpfTables.get(i).getRows().get(j).getTableCells().get(k).removeParagraph(0);
+                        xwpfTables.get(i).getRows().get(j).getTableCells().get(k).setText(str);
+                        xwpfTables.get(i).getRows().get(j).getTableCells().get(k).setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
+                        xwpfTables.get(i).getRows().get(j).getTableCells().get(k).getParagraphArray(0).setAlignment(ParagraphAlignment.CENTER);
+                    }
+                }
+            }
+            // 鍗曞厓鏍兼帓搴�, 閬垮厤鏍煎紡閿欎贡
+            List<Map.Entry<String, Map<String, Integer>>> entries = new ArrayList<>(maps.entrySet());
+            entries.sort((o1, o2) -> o1.getValue().get("sc") - o2.getValue().get("sc"));
+            // 鎸夌収椤哄簭娣诲姞杩涢泦鍚�
+            List<String> list = new ArrayList<>();
+            for (Map.Entry<String, Map<String, Integer>> entry : entries) {
+                list.add(entry.getKey());
+            }
+            for (int a = list.size() - 1; a >= 0; a--) {
+                Map<String, Integer> v = maps.get(list.get(a));
+                for (int j = 0; j < v.get("er") - v.get("sr") + 1; j++) {
+                    if (v.get("ec") > v.get("sc")) {
+                        try {
+                            mergeCellsHorizontally(xwpfTables.get(i), v.get("sr") + j, v.get("sc"), v.get("ec"));
+                        } catch (Exception e) {
+                        }
+                    }
+                }
+                if (v.get("er") > v.get("sr")) {
+                    try {
+                        mergeCellsVertically(xwpfTables.get(i), v.get("sc"), v.get("sr"), v.get("er"));
+                    } catch (Exception e) {
+                    }
+                }
+            }
+        }
+        //澶勭悊涓嫳鏂囨崲琛岀殑闂
+        List<XWPFTable> xwpfTables1 = document.getTables();
+        for (int i = 0; i < xwpfTables1.size(); i++) {
+            for (int j = 0; j < xwpfTables1.get(i).getRows().size(); j++) {
+                for (int k = 0; k < xwpfTables1.get(i).getRows().get(j).getTableCells().size(); k++) {
+                    if (xwpfTables1.get(i).getRows().get(j).getTableCells().get(k).getText().contains("@")) {
+                        String text = xwpfTables1.get(i).getRows().get(j).getTableCells().get(k).getText();
+                        String[] split = text.split("@");
+                        xwpfTables1.get(i).getRows().get(j).getTableCells().get(k).removeParagraph(0);
+                        XWPFParagraph xwpfParagraph = xwpfTables1.get(i).getRows().get(j).getTableCells().get(k).addParagraph();
+                        XWPFRun run = xwpfParagraph.createRun();
+                        run.setText(split[0]);
+                        if (ObjectUtils.isNotNull(split[1])) {
+                            run.addBreak();
+                            run.setText(split[1]);
+                        }
+                        xwpfParagraph.setAlignment(ParagraphAlignment.CENTER);
+                    }
+                }
+            }
+        }
+    }
+
+    // 姘村钩鍚堝苟鍗曞厓鏍�
+    private static void mergeCellsHorizontally(XWPFTable table, int row, int fromCol, int toCol) {
+        for (int i = fromCol; i <= toCol; i++) {
+            if (i == fromCol) {
+                table.getRow(row).getCell(i).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
+            } else {
+                table.getRow(row).getCell(i).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
+            }
+        }
+    }
+
+    // 鍨傜洿鍚堝苟鍗曞厓鏍�
+    private static void mergeCellsVertically(XWPFTable table, int col, int fromRow, int toRow) {
+        for (int i = fromRow; i <= toRow; i++) {
+            if (i == fromRow) {
+                table.getRow(i).getCell(col).getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
+            } else {
+                table.getRow(i).getCell(col).getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);
+            }
+        }
+    }
+}
+
diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml
index 9a14798..0e179cd 100644
--- a/ruoyi-admin/src/main/resources/application.yml
+++ b/ruoyi-admin/src/main/resources/application.yml
@@ -78,7 +78,7 @@
     # 鏁版嵁搴撶储寮�
     database: 0
     # 瀵嗙爜
-    password: 123456
+#    password: 123456
     # 杩炴帴瓒呮椂鏃堕棿
     timeout: 10s
     lettuce:
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java
index 949196f..e4785df 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java
@@ -51,7 +51,7 @@
     {
         this.id = menu.getMenuId();
         this.label = menu.getMenuName();
-        this.isRersonalButton = menu.geIsRersonalButton();
+        this.isRersonalButton = menu.getIsRersonalButton();
         this.children = menu.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList());
     }
 
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java
index 39a2cc0..eb77b98 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java
@@ -239,7 +239,7 @@
         this.icon = icon;
     }
 
-    public Integer geIsRersonalButton()
+    public Integer getIsRersonalButton()
     {
         return isRersonalButton;
     }
@@ -282,7 +282,7 @@
             .append("updateBy", getUpdateBy())
             .append("updateTime", getUpdateTime())
             .append("remark", getRemark())
-            .append("isRersonalButton", geIsRersonalButton())
+            .append("isRersonalButton", getIsRersonalButton())
             .toString();
     }
 }

--
Gitblit v1.9.3