From d2f07163fc4074f335986dd51eedbdc7be9e19ea Mon Sep 17 00:00:00 2001
From: gongchunyi <deslre0381@gmail.com>
Date: 星期四, 30 七月 2026 11:52:47 +0800
Subject: [PATCH] fix: 线路巡检调整

---
 src/main/java/com/ruoyi/safe/pojo/SafeLineInspectionTask.java                      |   99 ++++
 src/main/java/com/ruoyi/safe/controller/SafeLineInspectionTaskController.java      |   69 ++
 src/main/java/com/ruoyi/safe/service/impl/SafeLineInspectionServiceImpl.java       |  170 ++++++
 src/main/resources/mapper/safe/SafeLineInspectionTaskMapper.xml                    |   25 +
 src/main/java/com/ruoyi/safe/service/impl/SafeLineInspectionTaskServiceImpl.java   |  312 +++++++++++++
 doc/20260729_safe_line_inspection_task.sql                                         |   37 +
 src/main/resources/mapper/safe/SafeLineInspectionHazardMapper.xml                  |    2 
 src/main/java/com/ruoyi/safe/service/SafeLineInspectionService.java                |    3 
 src/main/java/com/ruoyi/safe/pojo/SafeLineInspectionHazard.java                    |   27 +
 src/main/resources/mapper/safe/SafeLineInspectionMapper.xml                        |    5 
 src/main/java/com/ruoyi/safe/controller/SafeLineInspectionController.java          |   27 
 src/main/java/com/ruoyi/safe/service/impl/SafeLineInspectionHazardServiceImpl.java |  160 ++++++
 src/main/java/com/ruoyi/safe/mapper/SafeLineInspectionTaskMapper.java              |   14 
 src/main/java/com/ruoyi/safe/controller/SafeLineInspectionHazardController.java    |    6 
 src/main/java/com/ruoyi/safe/service/impl/SafeHiddenServiceImpl.java               |    3 
 src/main/java/com/ruoyi/safe/service/impl/SafeLineInspectionTaskScheduler.java     |  170 +++++++
 src/main/java/com/ruoyi/safe/dto/SafeLineInspectionExecuteDto.java                 |   56 ++
 src/main/java/com/ruoyi/safe/pojo/SafeLineInspection.java                          |   17 
 src/main/java/com/ruoyi/safe/service/impl/SafeLineInspectionTaskJob.java           |  171 +++++++
 src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java                            |    1 
 src/main/java/com/ruoyi/safe/service/SafeLineInspectionTaskService.java            |   28 +
 21 files changed, 1,376 insertions(+), 26 deletions(-)

diff --git a/doc/20260729_safe_line_inspection_task.sql b/doc/20260729_safe_line_inspection_task.sql
new file mode 100644
index 0000000..5f34c5e
--- /dev/null
+++ b/doc/20260729_safe_line_inspection_task.sql
@@ -0,0 +1,37 @@
+CREATE TABLE IF NOT EXISTS `safe_line_inspection_task` (
+  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '浠诲姟ID',
+  `inspection_name` varchar(100) DEFAULT NULL COMMENT '宸℃浠诲姟鍚嶇О',
+  `line_name` varchar(100) DEFAULT NULL COMMENT '绾胯矾鍚嶇О',
+  `inspection_project` varchar(1000) DEFAULT NULL COMMENT '宸℃椤圭洰',
+  `inspector_id` varchar(255) DEFAULT NULL COMMENT '宸℃浜篒D锛屽涓敤閫楀彿鍒嗛殧',
+  `frequency_type` varchar(20) DEFAULT NULL COMMENT '棰戠巼绫诲瀷 DAILY/WEEKLY/MONTHLY',
+  `frequency_detail` varchar(50) DEFAULT NULL COMMENT '棰戠巼璇︽儏锛氭瘡鏃H:mm锛涙瘡鍛∕ON,HH:mm锛涙瘡鏈圖D,HH:mm',
+  `next_execution_time` datetime DEFAULT NULL COMMENT '涓嬫鎵ц鏃堕棿',
+  `last_execution_time` datetime DEFAULT NULL COMMENT '鏈�鍚庢墽琛屾椂闂�',
+  `is_enabled` tinyint DEFAULT '1' COMMENT '鏄惁鍚敤 0鍚� 1鏄�',
+  `remark` varchar(500) DEFAULT NULL COMMENT '澶囨敞',
+  `create_time` datetime DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
+  `create_user` int DEFAULT NULL COMMENT '鍒涘缓浜篒D',
+  `update_time` datetime DEFAULT NULL COMMENT '鏇存柊鏃堕棿',
+  `update_user` int DEFAULT NULL COMMENT '鏇存柊浜篒D',
+  `tenant_id` bigint DEFAULT NULL COMMENT '绉熸埛ID',
+  `dept_id` bigint DEFAULT NULL COMMENT '閮ㄩ棬ID',
+  PRIMARY KEY (`id`) USING BTREE,
+  KEY `idx_safe_line_inspection_task_enabled` (`is_enabled`, `next_execution_time`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='瀹夊叏鐢熶骇-绾胯矾瀹氭椂宸℃浠诲姟';
+
+ALTER TABLE `safe_line_inspection`
+  ADD COLUMN `schedule_task_id` bigint DEFAULT NULL COMMENT '瀹氭椂宸℃浠诲姟ID' AFTER `id`,
+  ADD COLUMN `inspection_project` varchar(1000) DEFAULT NULL COMMENT '宸℃椤圭洰' AFTER `inspection_type`,
+  ADD KEY `idx_safe_line_inspection_schedule_task` (`schedule_task_id`) USING BTREE;
+
+ALTER TABLE `safe_line_inspection`
+  ADD COLUMN `inspection_result` varchar(20) DEFAULT NULL COMMENT '宸℃缁撴灉锛氭甯�/寮傚父' AFTER `status`,
+  ADD COLUMN `actual_inspection_time` datetime DEFAULT NULL COMMENT '瀹為檯宸℃鏃堕棿' AFTER `inspection_result`,
+  ADD COLUMN `hidden_id` int DEFAULT NULL COMMENT '鍏宠仈闅愭偅鎺掓煡涓婃姤ID' AFTER `actual_inspection_time`,
+  ADD KEY `idx_safe_line_inspection_hidden` (`hidden_id`) USING BTREE;
+
+ALTER TABLE `safe_line_inspection_hazard`
+  ADD COLUMN `safe_hidden_id` int DEFAULT NULL COMMENT '鍏宠仈闅愭偅鎺掓煡涓婃姤ID' AFTER `record_id`,
+  ADD COLUMN `rectify_deadline` date DEFAULT NULL COMMENT '鏁存敼鏈熼檺' AFTER `rectify_user_id`,
+  ADD KEY `idx_safe_line_inspection_hazard_hidden` (`safe_hidden_id`) USING BTREE;
diff --git a/src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java b/src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java
index 8325355..6a8ff1d 100644
--- a/src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java
+++ b/src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java
@@ -68,6 +68,7 @@
     SAFE_TRAINING("safe_training"),
     SAFE_HIDDEN_FILE("safe_hidden_file"),
     SAFE_LINE_INSPECTION_RECORD("safe_line_inspection_record"),
+    SAFE_LINE_INSPECTION_HAZARD("safe_line_inspection_hazard"),
     SAFE_FACILITY_INSPECTION("safe_facility_inspection"),
     SPECIAL_EQUIPMENT_RECORD("special_equipment_record"),
     SPECIAL_EQUIPMENT_RECTIFICATION("special_equipment_rectification"),
diff --git a/src/main/java/com/ruoyi/safe/controller/SafeLineInspectionController.java b/src/main/java/com/ruoyi/safe/controller/SafeLineInspectionController.java
index 685aa0f..63f7766 100644
--- a/src/main/java/com/ruoyi/safe/controller/SafeLineInspectionController.java
+++ b/src/main/java/com/ruoyi/safe/controller/SafeLineInspectionController.java
@@ -5,6 +5,7 @@
 import com.ruoyi.framework.aspectj.lang.annotation.Log;
 import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
 import com.ruoyi.framework.web.domain.R;
+import com.ruoyi.safe.dto.SafeLineInspectionExecuteDto;
 import com.ruoyi.safe.pojo.SafeLineInspection;
 import com.ruoyi.safe.pojo.SafeLineInspectionHazard;
 import com.ruoyi.safe.pojo.SafeLineInspectionRecord;
@@ -16,7 +17,6 @@
 import lombok.AllArgsConstructor;
 import org.springframework.web.bind.annotation.*;
 
-import java.time.LocalDateTime;
 import java.util.List;
 
 /**
@@ -86,7 +86,7 @@
                 .count() > 0;
     }
 
-    @Operation(summary = "瀹屾垚宸℃")
+    @Operation(summary = "搴熷純鏃у贰妫�瀹屾垚鎺ュ彛")
     @Log(title = "绾胯矾宸℃", businessType = BusinessType.UPDATE)
     @PutMapping("/complete/{id}")
     public R complete(@PathVariable Integer id) {
@@ -97,21 +97,18 @@
         if ("宸插畬鎴�".equals(inspection.getStatus())) {
             return R.fail("宸℃浠诲姟宸插畬鎴愶紝璇峰嬁閲嶅鎿嶄綔");
         }
+        return R.fail("璇烽�氳繃鎵ц宸℃鎻愪氦姝e父缁撴灉锛涘紓甯歌褰曡鍦ㄦ暣鏀硅窡韪腑鐢辨暣鏀硅矗浠讳汉瀹屾垚鏁存敼");
+    }
 
-        // 鏍¢獙鏄惁鏈夋湭鏁存敼鐨勯殣鎮�
-        List<SafeLineInspectionHazard> hazards = safeLineInspectionHazardService.listByInspectionId(id);
-        boolean hasUnrectified = hazards.stream()
-                .anyMatch(h -> !"宸叉暣鏀�".equals(h.getStatus()));
-        if (hasUnrectified) {
-            return R.fail("瀛樺湪鏈暣鏀圭殑闅愭偅锛岃鍏堝畬鎴愰殣鎮f暣鏀�");
+    @Operation(summary = "鎵ц宸℃")
+    @Log(title = "绾胯矾宸℃", businessType = BusinessType.UPDATE)
+    @PutMapping("/execute")
+    public R execute(@RequestBody SafeLineInspectionExecuteDto executeDto) {
+        try {
+            return R.ok(safeLineInspectionService.executeInspection(executeDto));
+        } catch (IllegalArgumentException e) {
+            return R.fail(e.getMessage());
         }
-
-        Long currentUserId = SecurityUtils.getUserId();
-        inspection.setStatus("宸插畬鎴�");
-        inspection.setUpdateTime(LocalDateTime.now());
-        inspection.setUpdateUser(currentUserId.intValue());
-
-        return R.ok(safeLineInspectionService.updateById(inspection));
     }
 
     @Operation(summary = "鍒犻櫎绾胯矾宸℃浠诲姟")
diff --git a/src/main/java/com/ruoyi/safe/controller/SafeLineInspectionHazardController.java b/src/main/java/com/ruoyi/safe/controller/SafeLineInspectionHazardController.java
index bdf0732..cf17e17 100644
--- a/src/main/java/com/ruoyi/safe/controller/SafeLineInspectionHazardController.java
+++ b/src/main/java/com/ruoyi/safe/controller/SafeLineInspectionHazardController.java
@@ -58,7 +58,11 @@
     @Log(title = "绾胯矾宸℃闅愭偅", businessType = BusinessType.UPDATE)
     @PutMapping()
     public R update(@RequestBody SafeLineInspectionHazard safeLineInspectionHazard) {
-        return R.ok(safeLineInspectionHazardService.updateById(safeLineInspectionHazard));
+        try {
+            return R.ok(safeLineInspectionHazardService.updateById(safeLineInspectionHazard));
+        } catch (IllegalArgumentException e) {
+            return R.fail(e.getMessage());
+        }
     }
 
     @Operation(summary = "鍒犻櫎绾胯矾宸℃闅愭偅")
diff --git a/src/main/java/com/ruoyi/safe/controller/SafeLineInspectionTaskController.java b/src/main/java/com/ruoyi/safe/controller/SafeLineInspectionTaskController.java
new file mode 100644
index 0000000..3b5a769
--- /dev/null
+++ b/src/main/java/com/ruoyi/safe/controller/SafeLineInspectionTaskController.java
@@ -0,0 +1,69 @@
+package com.ruoyi.safe.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.domain.R;
+import com.ruoyi.safe.pojo.SafeLineInspectionTask;
+import com.ruoyi.safe.service.SafeLineInspectionTaskService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import org.quartz.SchedulerException;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 瀹夊叏鐢熶骇--绾胯矾瀹氭椂宸℃浠诲姟 鍓嶇鎺у埗鍣�
+ * </p>
+ */
+@RestController
+@RequestMapping("/safeLineInspectionTask")
+@AllArgsConstructor
+@Tag(name = "瀹夊叏鐢熶骇--绾胯矾瀹氭椂宸℃浠诲姟")
+public class SafeLineInspectionTaskController {
+
+    private SafeLineInspectionTaskService safeLineInspectionTaskService;
+
+    @GetMapping("/page")
+    @Operation(summary = "鍒嗛〉鏌ヨ")
+    public R page(Page<SafeLineInspectionTask> page, SafeLineInspectionTask task) {
+        return R.ok(safeLineInspectionTaskService.pageSafeLineInspectionTask(page, task));
+    }
+
+    @GetMapping("/{id}")
+    @Operation(summary = "鏍规嵁ID鏌ヨ")
+    public R getById(@PathVariable Long id) {
+        return R.ok(safeLineInspectionTaskService.getById(id));
+    }
+
+    @Operation(summary = "鏂板绾胯矾瀹氭椂宸℃浠诲姟")
+    @Log(title = "绾胯矾瀹氭椂宸℃浠诲姟", businessType = BusinessType.INSERT)
+    @PostMapping()
+    public R add(@RequestBody SafeLineInspectionTask task) throws SchedulerException {
+        return R.ok(safeLineInspectionTaskService.saveOrUpdateTask(task));
+    }
+
+    @Operation(summary = "淇敼绾胯矾瀹氭椂宸℃浠诲姟")
+    @Log(title = "绾胯矾瀹氭椂宸℃浠诲姟", businessType = BusinessType.UPDATE)
+    @PutMapping()
+    public R update(@RequestBody SafeLineInspectionTask task) throws SchedulerException {
+        return R.ok(safeLineInspectionTaskService.saveOrUpdateTask(task));
+    }
+
+    @Operation(summary = "鍒犻櫎绾胯矾瀹氭椂宸℃浠诲姟")
+    @Log(title = "绾胯矾瀹氭椂宸℃浠诲姟", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R delete(@PathVariable List<Long> ids) {
+        return R.ok(safeLineInspectionTaskService.removeTaskByIds(ids));
+    }
+}
diff --git a/src/main/java/com/ruoyi/safe/dto/SafeLineInspectionExecuteDto.java b/src/main/java/com/ruoyi/safe/dto/SafeLineInspectionExecuteDto.java
new file mode 100644
index 0000000..5546aa6
--- /dev/null
+++ b/src/main/java/com/ruoyi/safe/dto/SafeLineInspectionExecuteDto.java
@@ -0,0 +1,56 @@
+package com.ruoyi.safe.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.basic.dto.StorageBlobDTO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Data
+@Schema(name = "SafeLineInspectionExecuteDto", description = "绾胯矾宸℃鎵ц鍏ュ弬")
+public class SafeLineInspectionExecuteDto {
+
+    @Schema(description = "宸℃璁板綍瀹炰緥ID")
+    private Integer inspectionId;
+
+    @Schema(description = "宸℃缁撴灉(姝e父/寮傚父)")
+    private String checkResult;
+
+    @Schema(description = "宸℃璇存槑")
+    private String checkDesc;
+
+    @Schema(description = "宸℃鏃堕棿")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime checkTime;
+
+    @Schema(description = "鐜板満鐓х墖")
+    private List<StorageBlobDTO> storageBlobDTOs;
+
+    @Schema(description = "闅愭偅绫诲瀷")
+    private String hiddenType;
+
+    @Schema(description = "闅愭偅椋庨櫓绛夌骇")
+    private String riskLevel;
+
+    @Schema(description = "闅愭偅浣嶇疆")
+    private String hazardLocation;
+
+    @Schema(description = "寮傚父鎻忚堪/鏁存敼瑕佹眰")
+    private String hazardDesc;
+
+    @Schema(description = "鏁存敼璐d换浜篒D")
+    private Integer rectifyUserId;
+
+    @Schema(description = "鏁存敼璐d换浜烘墜鏈哄彿")
+    private String rectifyUserMobile;
+
+    @Schema(description = "鏁存敼瀹屾垚鏈熼檺")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate rectifyTime;
+}
diff --git a/src/main/java/com/ruoyi/safe/mapper/SafeLineInspectionTaskMapper.java b/src/main/java/com/ruoyi/safe/mapper/SafeLineInspectionTaskMapper.java
new file mode 100644
index 0000000..3200f55
--- /dev/null
+++ b/src/main/java/com/ruoyi/safe/mapper/SafeLineInspectionTaskMapper.java
@@ -0,0 +1,14 @@
+package com.ruoyi.safe.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.safe.pojo.SafeLineInspectionTask;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * <p>
+ * 瀹夊叏鐢熶骇--绾胯矾瀹氭椂宸℃浠诲姟 Mapper 鎺ュ彛
+ * </p>
+ */
+@Mapper
+public interface SafeLineInspectionTaskMapper extends BaseMapper<SafeLineInspectionTask> {
+}
diff --git a/src/main/java/com/ruoyi/safe/pojo/SafeLineInspection.java b/src/main/java/com/ruoyi/safe/pojo/SafeLineInspection.java
index 78d4a23..a91d45b 100644
--- a/src/main/java/com/ruoyi/safe/pojo/SafeLineInspection.java
+++ b/src/main/java/com/ruoyi/safe/pojo/SafeLineInspection.java
@@ -33,6 +33,9 @@
     @TableId(value = "id", type = IdType.AUTO)
     private Integer id;
 
+    @Schema(description = "瀹氭椂宸℃浠诲姟ID")
+    private Long scheduleTaskId;
+
     @Schema(description = "宸℃缂栧彿")
     private String inspectionCode;
 
@@ -44,6 +47,9 @@
 
     @Schema(description = "宸℃绫诲瀷")
     private String inspectionType;
+
+    @Schema(description = "宸℃椤圭洰")
+    private String inspectionProject;
 
     @Schema(description = "璁″垝宸℃鏃堕棿")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@@ -59,6 +65,17 @@
     @Schema(description = "鐘舵��(寰呭贰妫�/宸℃涓�/宸插畬鎴�)")
     private String status;
 
+    @Schema(description = "宸℃缁撴灉(姝e父/寮傚父)")
+    private String inspectionResult;
+
+    @Schema(description = "瀹為檯宸℃鏃堕棿")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime actualInspectionTime;
+
+    @Schema(description = "鍏宠仈闅愭偅鎺掓煡涓婃姤ID")
+    private Integer hiddenId;
+
     @Schema(description = "澶囨敞")
     private String remark;
 
diff --git a/src/main/java/com/ruoyi/safe/pojo/SafeLineInspectionHazard.java b/src/main/java/com/ruoyi/safe/pojo/SafeLineInspectionHazard.java
index 498b2bf..3ac64f7 100644
--- a/src/main/java/com/ruoyi/safe/pojo/SafeLineInspectionHazard.java
+++ b/src/main/java/com/ruoyi/safe/pojo/SafeLineInspectionHazard.java
@@ -5,8 +5,12 @@
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
+import com.ruoyi.basic.dto.StorageBlobDTO;
+import com.ruoyi.basic.dto.StorageBlobVO;
 import java.io.Serializable;
+import java.time.LocalDate;
 import java.time.LocalDateTime;
+import java.util.List;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
 import io.swagger.v3.oas.annotations.media.Schema;
@@ -39,6 +43,9 @@
     @Schema(description = "宸℃璁板綍ID")
     private Integer recordId;
 
+    @Schema(description = "鍏宠仈闅愭偅鎺掓煡涓婃姤ID")
+    private Integer safeHiddenId;
+
     @Schema(description = "闅愭偅缂栧彿")
     private String hazardCode;
 
@@ -59,6 +66,11 @@
 
     @Schema(description = "鏁存敼璐d换浜篒D")
     private Integer rectifyUserId;
+
+    @Schema(description = "鏁存敼鏈熼檺")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate rectifyDeadline;
 
     @Schema(description = "鏁存敼鏃堕棿")
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@@ -86,4 +98,19 @@
 
     @TableField(exist = false)
     private String rectifyUserName;
+
+    @TableField(exist = false)
+    private String inspectionCode;
+
+    @TableField(exist = false)
+    private String inspectionName;
+
+    @TableField(exist = false)
+    private String lineName;
+
+    @TableField(exist = false)
+    private List<StorageBlobDTO> storageBlobDTOs;
+
+    @TableField(exist = false)
+    private List<StorageBlobVO> storageBlobVOs;
 }
diff --git a/src/main/java/com/ruoyi/safe/pojo/SafeLineInspectionTask.java b/src/main/java/com/ruoyi/safe/pojo/SafeLineInspectionTask.java
new file mode 100644
index 0000000..901f5b6
--- /dev/null
+++ b/src/main/java/com/ruoyi/safe/pojo/SafeLineInspectionTask.java
@@ -0,0 +1,99 @@
+package com.ruoyi.safe.pojo;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * <p>
+ * 瀹夊叏鐢熶骇--绾胯矾瀹氭椂宸℃浠诲姟
+ * </p>
+ */
+@Data
+@TableName("safe_line_inspection_task")
+@Schema(name = "SafeLineInspectionTask瀵硅薄", description = "瀹夊叏鐢熶骇--绾胯矾瀹氭椂宸℃浠诲姟")
+public class SafeLineInspectionTask implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @Schema(description = "宸℃浠诲姟鍚嶇О")
+    private String inspectionName;
+
+    @Schema(description = "绾胯矾鍚嶇О")
+    private String lineName;
+
+    @Schema(description = "宸℃椤圭洰")
+    private String inspectionProject;
+
+    @Schema(description = "宸℃浜篒D锛堝涓敤閫楀彿鍒嗛殧锛�")
+    private String inspectorId;
+
+    @Schema(description = "棰戠巼绫诲瀷 DAILY/WEEKLY/MONTHLY")
+    private String frequencyType;
+
+    @Schema(description = "棰戠巼璇︽儏")
+    private String frequencyDetail;
+
+    @Schema(description = "涓嬫鎵ц鏃堕棿")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime nextExecutionTime;
+
+    @Schema(description = "鏈�鍚庢墽琛屾椂闂�")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime lastExecutionTime;
+
+    @Schema(description = "鏄惁鍚敤 0鍚� 1鏄�")
+    private Integer isEnabled;
+
+    @Schema(description = "澶囨敞")
+    private String remark;
+
+    @Schema(description = "鍒涘缓鏃堕棿")
+    @TableField(fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    @Schema(description = "鍒涘缓浜篒D")
+    @TableField(fill = FieldFill.INSERT)
+    private Integer createUser;
+
+    @Schema(description = "鏇存柊鏃堕棿")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+
+    @Schema(description = "鏇存柊浜篒D")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Integer updateUser;
+
+    @Schema(description = "绉熸埛ID")
+    @TableField(fill = FieldFill.INSERT)
+    private Long tenantId;
+
+    @Schema(description = "閮ㄩ棬ID")
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+
+    @TableField(exist = false)
+    private String inspectorName;
+
+    @TableField(exist = false)
+    private List<String> inspector;
+}
diff --git a/src/main/java/com/ruoyi/safe/service/SafeLineInspectionService.java b/src/main/java/com/ruoyi/safe/service/SafeLineInspectionService.java
index ce0c535..01ce730 100644
--- a/src/main/java/com/ruoyi/safe/service/SafeLineInspectionService.java
+++ b/src/main/java/com/ruoyi/safe/service/SafeLineInspectionService.java
@@ -2,6 +2,7 @@
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.safe.dto.SafeLineInspectionExecuteDto;
 import com.ruoyi.safe.pojo.SafeLineInspection;
 import com.baomidou.mybatisplus.extension.service.IService;
 
@@ -16,4 +17,6 @@
 public interface SafeLineInspectionService extends IService<SafeLineInspection> {
 
     IPage<SafeLineInspection> pageSafeLineInspection(Page page, SafeLineInspection safeLineInspection);
+
+    boolean executeInspection(SafeLineInspectionExecuteDto executeDto);
 }
diff --git a/src/main/java/com/ruoyi/safe/service/SafeLineInspectionTaskService.java b/src/main/java/com/ruoyi/safe/service/SafeLineInspectionTaskService.java
new file mode 100644
index 0000000..3b70b05
--- /dev/null
+++ b/src/main/java/com/ruoyi/safe/service/SafeLineInspectionTaskService.java
@@ -0,0 +1,28 @@
+package com.ruoyi.safe.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.safe.pojo.SafeLineInspectionTask;
+import org.quartz.SchedulerException;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * <p>
+ * 瀹夊叏鐢熶骇--绾胯矾瀹氭椂宸℃浠诲姟 鏈嶅姟绫�
+ * </p>
+ */
+public interface SafeLineInspectionTaskService extends IService<SafeLineInspectionTask> {
+
+    IPage<SafeLineInspectionTask> pageSafeLineInspectionTask(Page<SafeLineInspectionTask> page, SafeLineInspectionTask task);
+
+    boolean saveOrUpdateTask(SafeLineInspectionTask task) throws SchedulerException;
+
+    boolean removeTaskByIds(List<Long> ids);
+
+    LocalDateTime calculateFirstExecutionTime(String frequencyType, String frequencyDetail);
+
+    LocalDateTime calculateNextExecutionTime(String frequencyType, String frequencyDetail, LocalDateTime currentTime);
+}
diff --git a/src/main/java/com/ruoyi/safe/service/impl/SafeHiddenServiceImpl.java b/src/main/java/com/ruoyi/safe/service/impl/SafeHiddenServiceImpl.java
index 0856823..496e6f5 100644
--- a/src/main/java/com/ruoyi/safe/service/impl/SafeHiddenServiceImpl.java
+++ b/src/main/java/com/ruoyi/safe/service/impl/SafeHiddenServiceImpl.java
@@ -63,6 +63,9 @@
         safeHiddenMapper.updateById(safeHidden);
 
         String type = sysDictDataService.selectDictLabel("hidden_danger_type", safeHidden.getType());
+        if (!StringUtils.hasText(type)) {
+            type = safeHidden.getType();
+        }
         sysNoticeService.simpleNoticeByUser("闅愭偅鎺掓煡涓婃姤鏁存敼",
                 "闅愭偅绫诲瀷锛�" + type + "\t" +
                         "闅愭偅椋庨櫓绛夌骇锛�" + safeHidden.getRiskLevel() + "\t" +
diff --git a/src/main/java/com/ruoyi/safe/service/impl/SafeLineInspectionHazardServiceImpl.java b/src/main/java/com/ruoyi/safe/service/impl/SafeLineInspectionHazardServiceImpl.java
index 4b53b5c..a2d03e7 100644
--- a/src/main/java/com/ruoyi/safe/service/impl/SafeLineInspectionHazardServiceImpl.java
+++ b/src/main/java/com/ruoyi/safe/service/impl/SafeLineInspectionHazardServiceImpl.java
@@ -2,16 +2,27 @@
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.basic.dto.StorageBlobVO;
+import com.ruoyi.basic.enums.ApplicationTypeEnum;
+import com.ruoyi.basic.enums.RecordTypeEnum;
+import com.ruoyi.basic.utils.FileUtil;
 import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.project.system.domain.SysUser;
 import com.ruoyi.project.system.mapper.SysUserMapper;
 import com.ruoyi.safe.mapper.SafeLineInspectionHazardMapper;
+import com.ruoyi.safe.mapper.SafeLineInspectionMapper;
+import com.ruoyi.safe.pojo.SafeHidden;
+import com.ruoyi.safe.pojo.SafeLineInspection;
 import com.ruoyi.safe.pojo.SafeLineInspectionHazard;
+import com.ruoyi.safe.service.SafeHiddenService;
 import com.ruoyi.safe.service.SafeLineInspectionHazardService;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.io.Serializable;
+import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.util.*;
 import java.util.stream.Collectors;
@@ -29,17 +40,37 @@
 
     @Autowired
     private SysUserMapper sysUserMapper;
+    @Autowired
+    private SafeHiddenService safeHiddenService;
+    @Autowired
+    private SafeLineInspectionMapper safeLineInspectionMapper;
+    @Autowired
+    private FileUtil fileUtil;
 
     @Override
     public IPage<SafeLineInspectionHazard> pageSafeLineInspectionHazard(Page page, SafeLineInspectionHazard safeLineInspectionHazard) {
         IPage<SafeLineInspectionHazard> result = this.lambdaQuery()
                 .eq(safeLineInspectionHazard.getInspectionId() != null, SafeLineInspectionHazard::getInspectionId, safeLineInspectionHazard.getInspectionId())
-                .like(safeLineInspectionHazard.getHazardCode() != null, SafeLineInspectionHazard::getHazardCode, safeLineInspectionHazard.getHazardCode())
-                .eq(safeLineInspectionHazard.getStatus() != null, SafeLineInspectionHazard::getStatus, safeLineInspectionHazard.getStatus())
+                .like(StringUtils.isNotBlank(safeLineInspectionHazard.getHazardCode()), SafeLineInspectionHazard::getHazardCode, safeLineInspectionHazard.getHazardCode())
+                .like(StringUtils.isNotBlank(safeLineInspectionHazard.getHazardDesc()), SafeLineInspectionHazard::getHazardDesc, safeLineInspectionHazard.getHazardDesc())
+                .eq(StringUtils.isNotBlank(safeLineInspectionHazard.getStatus()), SafeLineInspectionHazard::getStatus, safeLineInspectionHazard.getStatus())
                 .orderByDesc(SafeLineInspectionHazard::getCreateTime)
                 .page(page);
         fillRectifyUserName(result.getRecords());
+        fillInspectionInfo(result.getRecords());
+        fillRectifyAttachments(result.getRecords());
         return result;
+    }
+
+    @Override
+    public SafeLineInspectionHazard getById(Serializable id) {
+        SafeLineInspectionHazard record = super.getById(id);
+        if (record != null) {
+            fillRectifyUserName(Collections.singletonList(record));
+            fillInspectionInfo(Collections.singletonList(record));
+            fillRectifyAttachments(Collections.singletonList(record));
+        }
+        return record;
     }
 
     @Override
@@ -49,6 +80,8 @@
                 .orderByDesc(SafeLineInspectionHazard::getCreateTime)
                 .list();
         fillRectifyUserName(records);
+        fillInspectionInfo(records);
+        fillRectifyAttachments(records);
         return records;
     }
 
@@ -62,12 +95,65 @@
 
     @Override
     public boolean updateById(SafeLineInspectionHazard entity) {
+        SafeLineInspectionHazard oldRecord = entity.getId() == null ? null : this.getById(entity.getId());
+        if (oldRecord == null) {
+            throw new IllegalArgumentException("鏁存敼璁板綍涓嶅瓨鍦�");
+        }
+        Integer safeHiddenId = entity.getSafeHiddenId() != null
+                ? entity.getSafeHiddenId()
+                : oldRecord.getSafeHiddenId();
         if ("宸叉暣鏀�".equals(entity.getStatus())) {
-            Long currentUserId = SecurityUtils.getUserId();
-            entity.setRectifyUserId(currentUserId.intValue());
+            validateRectify(entity, oldRecord);
+            entity.setRectifyUserId(oldRecord.getRectifyUserId());
             entity.setRectifyTime(LocalDateTime.now());
         }
-        return super.updateById(entity);
+        boolean updated = super.updateById(entity);
+        if (updated && "宸叉暣鏀�".equals(entity.getStatus())) {
+            syncRectifyAttachments(entity);
+        }
+        if (updated && "宸叉暣鏀�".equals(entity.getStatus()) && safeHiddenId != null) {
+            SafeHidden hidden = new SafeHidden();
+            hidden.setId(safeHiddenId);
+            hidden.setRectifyMeasures(entity.getRectifyDesc());
+            hidden.setRectifyActualTime(LocalDate.now());
+            safeHiddenService.updateById(hidden);
+        }
+        if (updated && "宸叉暣鏀�".equals(entity.getStatus())) {
+            Integer inspectionId = entity.getInspectionId() != null
+                    ? entity.getInspectionId()
+                    : oldRecord.getInspectionId();
+            completeInspectionIfAllRectified(inspectionId);
+        }
+        return updated;
+    }
+
+    private void validateRectify(SafeLineInspectionHazard entity, SafeLineInspectionHazard oldRecord) {
+        if ("宸叉暣鏀�".equals(oldRecord.getStatus())) {
+            throw new IllegalArgumentException("璇ラ殣鎮e凡鏁存敼锛岃鍕块噸澶嶆搷浣�");
+        }
+        Long currentUserId = SecurityUtils.getUserId();
+        if (oldRecord.getRectifyUserId() == null || currentUserId == null
+                || !oldRecord.getRectifyUserId().equals(currentUserId.intValue())) {
+            throw new IllegalArgumentException("鍙湁鏁存敼璐d换浜哄彲浠ヨ繘琛屾暣鏀�");
+        }
+        if (StringUtils.isBlank(entity.getRectifyDesc())) {
+            throw new IllegalArgumentException("璇疯緭鍏ユ暣鏀硅鏄�");
+        }
+        if (entity.getStorageBlobDTOs() == null || entity.getStorageBlobDTOs().isEmpty()) {
+            throw new IllegalArgumentException("璇蜂笂浼犳暣鏀瑰悗鐓х墖");
+        }
+    }
+
+    private void syncRectifyAttachments(SafeLineInspectionHazard entity) {
+        if (entity.getId() == null || entity.getStorageBlobDTOs() == null) {
+            return;
+        }
+        fileUtil.saveStorageAttachment(
+                ApplicationTypeEnum.IMAGE,
+                RecordTypeEnum.SAFE_LINE_INSPECTION_HAZARD,
+                entity.getId().longValue(),
+                entity.getStorageBlobDTOs()
+        );
     }
 
     private String generateHazardCode() {
@@ -96,4 +182,66 @@
             }
         });
     }
+
+    private void fillInspectionInfo(List<SafeLineInspectionHazard> records) {
+        if (records == null || records.isEmpty()) {
+            return;
+        }
+        List<Integer> inspectionIds = records.stream()
+                .map(SafeLineInspectionHazard::getInspectionId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .collect(Collectors.toList());
+        if (inspectionIds.isEmpty()) {
+            return;
+        }
+        Map<Integer, SafeLineInspection> inspectionMap = safeLineInspectionMapper.selectBatchIds(inspectionIds).stream()
+                .collect(Collectors.toMap(SafeLineInspection::getId, item -> item, (v1, v2) -> v1));
+        records.forEach(record -> {
+            SafeLineInspection inspection = inspectionMap.get(record.getInspectionId());
+            if (inspection != null) {
+                record.setInspectionCode(inspection.getInspectionCode());
+                record.setInspectionName(inspection.getInspectionName());
+                record.setLineName(inspection.getLineName());
+            }
+        });
+    }
+
+    private void fillRectifyAttachments(List<SafeLineInspectionHazard> records) {
+        if (records == null || records.isEmpty()) {
+            return;
+        }
+        records.forEach(record -> {
+            if (record.getId() == null) {
+                return;
+            }
+            List<StorageBlobVO> attachments = fileUtil.getStorageBlobVOsByRecordTypeAndRecordId(
+                    RecordTypeEnum.SAFE_LINE_INSPECTION_HAZARD,
+                    record.getId().longValue()
+            );
+            record.setStorageBlobVOs(attachments == null ? Collections.emptyList() : attachments);
+        });
+    }
+
+    private void completeInspectionIfAllRectified(Integer inspectionId) {
+        if (inspectionId == null) {
+            return;
+        }
+        long unrectifiedCount = this.lambdaQuery()
+                .eq(SafeLineInspectionHazard::getInspectionId, inspectionId)
+                .ne(SafeLineInspectionHazard::getStatus, "宸叉暣鏀�")
+                .count();
+        if (unrectifiedCount > 0) {
+            return;
+        }
+        SafeLineInspection inspection = safeLineInspectionMapper.selectById(inspectionId);
+        if (inspection == null || "宸插畬鎴�".equals(inspection.getStatus())) {
+            return;
+        }
+        Long currentUserId = SecurityUtils.getUserId();
+        inspection.setStatus("宸插畬鎴�");
+        inspection.setUpdateTime(LocalDateTime.now());
+        inspection.setUpdateUser(currentUserId == null ? null : currentUserId.intValue());
+        safeLineInspectionMapper.updateById(inspection);
+    }
 }
diff --git a/src/main/java/com/ruoyi/safe/service/impl/SafeLineInspectionServiceImpl.java b/src/main/java/com/ruoyi/safe/service/impl/SafeLineInspectionServiceImpl.java
index d86d586..0ed6055 100644
--- a/src/main/java/com/ruoyi/safe/service/impl/SafeLineInspectionServiceImpl.java
+++ b/src/main/java/com/ruoyi/safe/service/impl/SafeLineInspectionServiceImpl.java
@@ -2,15 +2,29 @@
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.basic.enums.ApplicationTypeEnum;
+import com.ruoyi.basic.enums.RecordTypeEnum;
+import com.ruoyi.basic.utils.FileUtil;
+import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.project.system.domain.SysUser;
+import com.ruoyi.safe.dto.SafeLineInspectionExecuteDto;
 import com.ruoyi.safe.pojo.SafeLineInspection;
 import com.ruoyi.safe.mapper.SafeLineInspectionMapper;
+import com.ruoyi.safe.pojo.SafeLineInspectionHazard;
+import com.ruoyi.safe.pojo.SafeLineInspectionRecord;
+import com.ruoyi.safe.pojo.SafeHidden;
+import com.ruoyi.safe.service.SafeHiddenService;
+import com.ruoyi.safe.service.SafeLineInspectionHazardService;
+import com.ruoyi.safe.service.SafeLineInspectionRecordService;
 import com.ruoyi.safe.service.SafeLineInspectionService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.project.system.mapper.SysUserMapper;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
+import java.time.LocalDateTime;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
@@ -29,14 +43,24 @@
 
     @Autowired
     private SysUserMapper sysUserMapper;
+    @Autowired
+    private SafeLineInspectionRecordService safeLineInspectionRecordService;
+    @Autowired
+    private SafeLineInspectionHazardService safeLineInspectionHazardService;
+    @Autowired
+    private SafeHiddenService safeHiddenService;
+    @Autowired
+    private FileUtil fileUtil;
 
     @Override
     public IPage<SafeLineInspection> pageSafeLineInspection(Page page, SafeLineInspection safeLineInspection) {
         IPage<SafeLineInspection> result = this.lambdaQuery()
-                .like(safeLineInspection.getInspectionCode() != null, SafeLineInspection::getInspectionCode, safeLineInspection.getInspectionCode())
-                .like(safeLineInspection.getInspectionName() != null, SafeLineInspection::getInspectionName, safeLineInspection.getInspectionName())
-                .like(safeLineInspection.getLineName() != null, SafeLineInspection::getLineName, safeLineInspection.getLineName())
-                .eq(safeLineInspection.getStatus() != null, SafeLineInspection::getStatus, safeLineInspection.getStatus())
+                .eq(safeLineInspection.getScheduleTaskId() != null, SafeLineInspection::getScheduleTaskId, safeLineInspection.getScheduleTaskId())
+                .like(StringUtils.isNotBlank(safeLineInspection.getInspectionCode()), SafeLineInspection::getInspectionCode, safeLineInspection.getInspectionCode())
+                .like(StringUtils.isNotBlank(safeLineInspection.getInspectionName()), SafeLineInspection::getInspectionName, safeLineInspection.getInspectionName())
+                .like(StringUtils.isNotBlank(safeLineInspection.getLineName()), SafeLineInspection::getLineName, safeLineInspection.getLineName())
+                .like(StringUtils.isNotBlank(safeLineInspection.getInspectionProject()), SafeLineInspection::getInspectionProject, safeLineInspection.getInspectionProject())
+                .eq(StringUtils.isNotBlank(safeLineInspection.getStatus()), SafeLineInspection::getStatus, safeLineInspection.getStatus())
                 .orderByDesc(SafeLineInspection::getCreateTime)
                 .page(page);
 
@@ -46,6 +70,144 @@
         return result;
     }
 
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean executeInspection(SafeLineInspectionExecuteDto executeDto) {
+        if (executeDto == null || executeDto.getInspectionId() == null) {
+            throw new IllegalArgumentException("宸℃璁板綍涓嶈兘涓虹┖");
+        }
+        SafeLineInspection inspection = this.getById(executeDto.getInspectionId());
+        if (inspection == null) {
+            throw new IllegalArgumentException("宸℃璁板綍涓嶅瓨鍦�");
+        }
+        if ("宸插畬鎴�".equals(inspection.getStatus())) {
+            throw new IllegalArgumentException("宸℃璁板綍宸插畬鎴愶紝璇峰嬁閲嶅鎵ц");
+        }
+        if (!"姝e父".equals(executeDto.getCheckResult()) && !"寮傚父".equals(executeDto.getCheckResult())) {
+            throw new IllegalArgumentException("璇烽�夋嫨宸℃缁撴灉");
+        }
+
+        LocalDateTime checkTime = executeDto.getCheckTime() == null ? LocalDateTime.now() : executeDto.getCheckTime();
+        SafeLineInspectionRecord record = buildInspectionRecord(inspection, executeDto, checkTime);
+        safeLineInspectionRecordService.save(record);
+
+        inspection.setInspectionResult(executeDto.getCheckResult());
+        inspection.setActualInspectionTime(checkTime);
+        inspection.setUpdateTime(LocalDateTime.now());
+        inspection.setUpdateUser(SecurityUtils.getUserId().intValue());
+        appendCurrentInspector(inspection);
+
+        if ("寮傚父".equals(executeDto.getCheckResult())) {
+            validateHazard(executeDto);
+            SafeHidden hidden = createSafeHidden(executeDto);
+            safeHiddenService.add(hidden);
+            fileUtil.saveStorageAttachment(
+                    ApplicationTypeEnum.IMAGE,
+                    RecordTypeEnum.SAFE_HIDDEN,
+                    hidden.getId().longValue(),
+                    executeDto.getStorageBlobDTOs()
+            );
+
+            SafeLineInspectionHazard hazard = buildLineHazard(inspection, record, hidden, executeDto);
+            safeLineInspectionHazardService.save(hazard);
+
+            inspection.setHiddenId(hidden.getId());
+            inspection.setStatus("寰呮暣鏀�");
+        } else {
+            inspection.setHiddenId(null);
+            inspection.setStatus("宸插畬鎴�");
+        }
+
+        return this.updateById(inspection);
+    }
+
+    private SafeLineInspectionRecord buildInspectionRecord(SafeLineInspection inspection,
+                                                           SafeLineInspectionExecuteDto executeDto,
+                                                           LocalDateTime checkTime) {
+        SafeLineInspectionRecord record = new SafeLineInspectionRecord();
+        record.setInspectionId(inspection.getId());
+        record.setCheckPoint(inspection.getLineName());
+        record.setCheckContent(inspection.getInspectionProject());
+        record.setCheckResult(executeDto.getCheckResult());
+        record.setCheckDesc("寮傚父".equals(executeDto.getCheckResult()) ? null : executeDto.getCheckDesc());
+        record.setCheckTime(checkTime);
+        record.setStorageBlobDTOs(executeDto.getStorageBlobDTOs());
+        return record;
+    }
+
+    private void validateHazard(SafeLineInspectionExecuteDto executeDto) {
+        if (executeDto.getStorageBlobDTOs() == null || executeDto.getStorageBlobDTOs().isEmpty()) {
+            throw new IllegalArgumentException("宸℃寮傚父鏃惰涓婁紶寮傚父鐓х墖");
+        }
+        if (StringUtils.isBlank(executeDto.getHiddenType())) {
+            throw new IllegalArgumentException("璇烽�夋嫨闅愭偅绫诲瀷");
+        }
+        if (StringUtils.isBlank(executeDto.getRiskLevel())) {
+            throw new IllegalArgumentException("璇烽�夋嫨闅愭偅椋庨櫓绛夌骇");
+        }
+        if (StringUtils.isBlank(executeDto.getHazardLocation())) {
+            throw new IllegalArgumentException("璇疯緭鍏ラ殣鎮d綅缃�");
+        }
+        if (StringUtils.isBlank(executeDto.getHazardDesc())) {
+            throw new IllegalArgumentException("璇疯緭鍏ュ紓甯告弿杩�/鏁存敼瑕佹眰");
+        }
+        if (executeDto.getRectifyUserId() == null) {
+            throw new IllegalArgumentException("璇烽�夋嫨鏁存敼璐d换浜�");
+        }
+        if (executeDto.getRectifyTime() == null) {
+            throw new IllegalArgumentException("璇烽�夋嫨鏁存敼瀹屾垚鏈熼檺");
+        }
+    }
+
+    private SafeHidden createSafeHidden(SafeLineInspectionExecuteDto executeDto) {
+        SafeHidden hidden = new SafeHidden();
+        hidden.setType(executeDto.getHiddenType());
+        hidden.setRiskLevel(executeDto.getRiskLevel());
+        hidden.setLocation(executeDto.getHazardLocation());
+        hidden.setHiddenDesc(executeDto.getHazardDesc());
+        hidden.setRectifyUserId(executeDto.getRectifyUserId());
+        hidden.setRectifyUserMobile(executeDto.getRectifyUserMobile());
+        hidden.setRectifyTime(executeDto.getRectifyTime());
+        return hidden;
+    }
+
+    private SafeLineInspectionHazard buildLineHazard(SafeLineInspection inspection,
+                                                     SafeLineInspectionRecord record,
+                                                     SafeHidden hidden,
+                                                     SafeLineInspectionExecuteDto executeDto) {
+        SafeLineInspectionHazard hazard = new SafeLineInspectionHazard();
+        hazard.setInspectionId(inspection.getId());
+        hazard.setRecordId(record.getId());
+        hazard.setSafeHiddenId(hidden.getId());
+        hazard.setHazardCode(hidden.getHiddenCode());
+        hazard.setHazardDesc(executeDto.getHazardDesc());
+        hazard.setHazardLevel(executeDto.getRiskLevel());
+        hazard.setHazardLocation(executeDto.getHazardLocation());
+        hazard.setStatus("寰呮暣鏀�");
+        hazard.setRectifyUserId(executeDto.getRectifyUserId());
+        hazard.setRectifyDeadline(executeDto.getRectifyTime());
+        return hazard;
+    }
+
+    private void appendCurrentInspector(SafeLineInspection inspection) {
+        Long currentUserId = SecurityUtils.getUserId();
+        if (currentUserId == null) {
+            return;
+        }
+        String currentUserIdStr = String.valueOf(currentUserId);
+        String inspectorId = inspection.getInspectorId();
+        if (StringUtils.isBlank(inspectorId)) {
+            inspection.setInspectorId(currentUserIdStr);
+            return;
+        }
+        boolean exists = Arrays.stream(inspectorId.split(","))
+                .map(String::trim)
+                .anyMatch(currentUserIdStr::equals);
+        if (!exists) {
+            inspection.setInspectorId(inspectorId + "," + currentUserIdStr);
+        }
+    }
+
     /**
      * 濉厖宸℃浜哄悕绉帮紙鏀寔澶氬贰妫�浜猴紝閫楀彿鍒嗛殧锛�
      */
diff --git a/src/main/java/com/ruoyi/safe/service/impl/SafeLineInspectionTaskJob.java b/src/main/java/com/ruoyi/safe/service/impl/SafeLineInspectionTaskJob.java
new file mode 100644
index 0000000..b3d5c49
--- /dev/null
+++ b/src/main/java/com/ruoyi/safe/service/impl/SafeLineInspectionTaskJob.java
@@ -0,0 +1,171 @@
+package com.ruoyi.safe.service.impl;
+
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.safe.mapper.SafeLineInspectionMapper;
+import com.ruoyi.safe.mapper.SafeLineInspectionTaskMapper;
+import com.ruoyi.safe.pojo.SafeLineInspection;
+import com.ruoyi.safe.pojo.SafeLineInspectionTask;
+import org.quartz.DisallowConcurrentExecution;
+import org.quartz.Job;
+import org.quartz.JobDataMap;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.Serializable;
+import java.time.DayOfWeek;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.YearMonth;
+import java.time.format.DateTimeFormatter;
+import java.util.HashSet;
+import java.util.Set;
+
+@Component
+@DisallowConcurrentExecution
+public class SafeLineInspectionTaskJob implements Job, Serializable {
+
+    private static final long serialVersionUID = 1L;
+    private static final DateTimeFormatter CODE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
+
+    @Autowired
+    private SafeLineInspectionTaskMapper safeLineInspectionTaskMapper;
+    @Autowired
+    private SafeLineInspectionMapper safeLineInspectionMapper;
+
+    @Override
+    public void execute(JobExecutionContext context) throws JobExecutionException {
+        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
+        Long taskId = jobDataMap.getLong("taskId");
+
+        try {
+            SafeLineInspectionTask task = safeLineInspectionTaskMapper.selectById(taskId);
+            if (task == null || Integer.valueOf(0).equals(task.getIsEnabled())) {
+                return;
+            }
+
+            LocalDateTime executionTime = LocalDateTime.now();
+            SafeLineInspection inspection = createLineInspection(task, executionTime);
+            safeLineInspectionMapper.insert(inspection);
+
+            task.setLastExecutionTime(executionTime);
+            task.setNextExecutionTime(calculateNextExecutionTime(
+                    task.getFrequencyType(),
+                    task.getFrequencyDetail(),
+                    executionTime
+            ));
+            safeLineInspectionTaskMapper.updateById(task);
+        } catch (Exception e) {
+            throw new JobExecutionException(e);
+        }
+    }
+
+    private SafeLineInspection createLineInspection(SafeLineInspectionTask task, LocalDateTime executionTime) {
+        SafeLineInspection inspection = new SafeLineInspection();
+        inspection.setScheduleTaskId(task.getId());
+        inspection.setInspectionCode("XLXJ-" + executionTime.format(CODE_FORMATTER) + "-" + task.getId());
+        inspection.setInspectionName(task.getInspectionName());
+        inspection.setLineName(task.getLineName());
+        inspection.setInspectionType("瀹氭湡宸℃");
+        inspection.setInspectionProject(task.getInspectionProject());
+        inspection.setPlanTime(task.getNextExecutionTime() != null ? task.getNextExecutionTime() : executionTime);
+        inspection.setAssignUserId(task.getCreateUser());
+        inspection.setInspectorId(task.getInspectorId());
+        inspection.setStatus("寰呭贰妫�");
+        inspection.setRemark(buildRemark(task));
+        inspection.setCreateUser(task.getCreateUser());
+        inspection.setUpdateUser(task.getCreateUser());
+        inspection.setCreateTime(executionTime);
+        inspection.setUpdateTime(executionTime);
+        inspection.setTenantId(task.getTenantId() == null ? null : task.getTenantId().intValue());
+        inspection.setDeptId(task.getDeptId());
+        return inspection;
+    }
+
+    private String buildRemark(SafeLineInspectionTask task) {
+        String remark = "鑷姩鐢熸垚鑷畾鏃朵换鍔D: " + task.getId();
+        if (StringUtils.isNotBlank(task.getRemark())) {
+            remark = remark + "锛�" + task.getRemark();
+        }
+        return remark;
+    }
+
+    private LocalDateTime calculateNextExecutionTime(String frequencyType,
+                                                     String frequencyDetail,
+                                                     LocalDateTime currentTime) {
+        return switch (frequencyType) {
+            case "DAILY" -> calculateDailyNextTime(frequencyDetail, currentTime);
+            case "WEEKLY" -> calculateWeeklyNextTime(frequencyDetail, currentTime);
+            case "MONTHLY" -> calculateMonthlyNextTime(frequencyDetail, currentTime);
+            default -> throw new IllegalArgumentException("涓嶆敮鎸佺殑棰戠巼绫诲瀷: " + frequencyType);
+        };
+    }
+
+    private LocalDateTime calculateDailyNextTime(String timeStr, LocalDateTime current) {
+        LocalTime executionTime = LocalTime.parse(timeStr);
+        LocalDateTime nextTime = LocalDateTime.of(current.toLocalDate(), executionTime);
+        return current.isBefore(nextTime) ? nextTime : nextTime.plusDays(1);
+    }
+
+    private LocalDateTime calculateWeeklyNextTime(String detail, LocalDateTime current) {
+        String[] parts = detail.split(",");
+        LocalTime time = LocalTime.parse(parts[1].trim());
+        Set<DayOfWeek> targetDays = parseDayOfWeeks(parts[0]);
+
+        LocalDateTime nextTime = current;
+        while (true) {
+            nextTime = nextTime.plusDays(1);
+            if (targetDays.contains(nextTime.getDayOfWeek())) {
+                return LocalDateTime.of(nextTime.toLocalDate(), time);
+            }
+            if (nextTime.isAfter(current.plusYears(1))) {
+                throw new RuntimeException("鏃犳硶鎵惧埌涓嬫鎵ц鏃堕棿");
+            }
+        }
+    }
+
+    private LocalDateTime calculateMonthlyNextTime(String detail, LocalDateTime current) {
+        String[] parts = detail.split(",");
+        int dayOfMonth = Integer.parseInt(parts[0].trim());
+        LocalTime time = LocalTime.parse(parts[1].trim());
+        YearMonth currentMonth = YearMonth.from(current);
+        for (int i = 0; i < 24; i++) {
+            YearMonth targetMonth = currentMonth.plusMonths(i);
+            if (dayOfMonth > targetMonth.lengthOfMonth()) {
+                continue;
+            }
+            LocalDateTime candidate = LocalDateTime.of(
+                    targetMonth.getYear(),
+                    targetMonth.getMonthValue(),
+                    dayOfMonth,
+                    time.getHour(),
+                    time.getMinute()
+            );
+            if (current.isBefore(candidate)) {
+                return candidate;
+            }
+        }
+        throw new IllegalArgumentException("鏃犳硶璁$畻涓嬫鎵ц鏃堕棿");
+    }
+
+    private Set<DayOfWeek> parseDayOfWeeks(String dayOfWeekStr) {
+        Set<DayOfWeek> days = new HashSet<>();
+        String[] dayStrs = dayOfWeekStr.split("\\|");
+
+        for (String dayStr : dayStrs) {
+            switch (dayStr.trim()) {
+                case "MON" -> days.add(DayOfWeek.MONDAY);
+                case "TUE" -> days.add(DayOfWeek.TUESDAY);
+                case "WED" -> days.add(DayOfWeek.WEDNESDAY);
+                case "THU" -> days.add(DayOfWeek.THURSDAY);
+                case "FRI" -> days.add(DayOfWeek.FRIDAY);
+                case "SAT" -> days.add(DayOfWeek.SATURDAY);
+                case "SUN" -> days.add(DayOfWeek.SUNDAY);
+                default -> throw new IllegalArgumentException("鏃犳晥鐨勬槦鏈熷嚑: " + dayStr);
+            }
+        }
+
+        return days;
+    }
+}
diff --git a/src/main/java/com/ruoyi/safe/service/impl/SafeLineInspectionTaskScheduler.java b/src/main/java/com/ruoyi/safe/service/impl/SafeLineInspectionTaskScheduler.java
new file mode 100644
index 0000000..4ffdb66
--- /dev/null
+++ b/src/main/java/com/ruoyi/safe/service/impl/SafeLineInspectionTaskScheduler.java
@@ -0,0 +1,170 @@
+package com.ruoyi.safe.service.impl;
+
+import com.ruoyi.safe.pojo.SafeLineInspectionTask;
+import lombok.RequiredArgsConstructor;
+import org.quartz.CronScheduleBuilder;
+import org.quartz.JobBuilder;
+import org.quartz.JobDataMap;
+import org.quartz.JobDetail;
+import org.quartz.JobKey;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.quartz.Trigger;
+import org.quartz.TriggerBuilder;
+import org.quartz.TriggerKey;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeParseException;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.stream.Collectors;
+
+@Service
+@RequiredArgsConstructor
+public class SafeLineInspectionTaskScheduler {
+
+    private static final String JOB_GROUP = "safeLineInspectionTask";
+    private static final String TRIGGER_GROUP = "safeLineInspectionTaskTrigger";
+
+    private final Scheduler scheduler;
+
+    public void scheduleTask(SafeLineInspectionTask task) throws SchedulerException {
+        JobDetail jobDetail = buildJobDetail(task);
+        if (scheduler.checkExists(jobDetail.getKey())) {
+            scheduler.deleteJob(jobDetail.getKey());
+        }
+        scheduler.scheduleJob(jobDetail, buildJobTrigger(task, jobDetail));
+    }
+
+    public void rescheduleTask(SafeLineInspectionTask task) throws SchedulerException {
+        TriggerKey triggerKey = getTriggerKey(task.getId());
+        Trigger oldTrigger = scheduler.getTrigger(triggerKey);
+        if (oldTrigger == null) {
+            scheduleTask(task);
+            return;
+        }
+        JobDetail jobDetail = scheduler.getJobDetail(oldTrigger.getJobKey());
+        if (jobDetail == null) {
+            scheduleTask(task);
+            return;
+        }
+        scheduler.rescheduleJob(triggerKey, buildJobTrigger(task, jobDetail));
+    }
+
+    public void unscheduleTask(Long taskId) {
+        try {
+            scheduler.deleteJob(getJobKey(taskId));
+        } catch (SchedulerException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private JobDetail buildJobDetail(SafeLineInspectionTask task) {
+        JobDataMap jobDataMap = new JobDataMap();
+        jobDataMap.put("taskId", task.getId());
+        jobDataMap.put("taskName", task.getInspectionName());
+        jobDataMap.put("frequencyType", task.getFrequencyType());
+
+        return JobBuilder.newJob(SafeLineInspectionTaskJob.class)
+                .withIdentity(getJobKey(task.getId()))
+                .withDescription(task.getInspectionName())
+                .usingJobData(jobDataMap)
+                .storeDurably(true)
+                .requestRecovery(true)
+                .build();
+    }
+
+    private Trigger buildJobTrigger(SafeLineInspectionTask task, JobDetail jobDetail) {
+        return TriggerBuilder.newTrigger()
+                .withIdentity(getTriggerKey(task.getId()))
+                .withDescription(task.getInspectionName() + "_TRIGGER")
+                .forJob(jobDetail)
+                .withSchedule(CronScheduleBuilder
+                        .cronSchedule(convertToCronExpression(task))
+                        .withMisfireHandlingInstructionDoNothing())
+                .startAt(task.getNextExecutionTime() != null
+                        ? Date.from(task.getNextExecutionTime().atZone(ZoneId.systemDefault()).toInstant())
+                        : new Date())
+                .build();
+    }
+
+    private JobKey getJobKey(Long taskId) {
+        return new JobKey("safeLineInspectionTask_" + taskId, JOB_GROUP);
+    }
+
+    private TriggerKey getTriggerKey(Long taskId) {
+        return new TriggerKey("safeLineInspectionTaskTrigger_" + taskId, TRIGGER_GROUP);
+    }
+
+    private String convertToCronExpression(SafeLineInspectionTask task) {
+        if (task == null || task.getFrequencyType() == null || task.getFrequencyDetail() == null) {
+            throw new IllegalArgumentException("浠诲姟棰戠巼涓嶈兘涓虹┖");
+        }
+        return switch (task.getFrequencyType().toUpperCase()) {
+            case "DAILY" -> convertDailyToCron(task.getFrequencyDetail());
+            case "WEEKLY" -> convertWeeklyToCron(task.getFrequencyDetail());
+            case "MONTHLY" -> convertMonthlyToCron(task.getFrequencyDetail());
+            default -> throw new IllegalArgumentException("涓嶆敮鎸佺殑棰戠巼绫诲瀷: " + task.getFrequencyType());
+        };
+    }
+
+    private String convertDailyToCron(String frequencyDetail) {
+        LocalTime time = parseTime(frequencyDetail);
+        return String.format("0 %d %d * * ?", time.getMinute(), time.getHour());
+    }
+
+    private String convertWeeklyToCron(String frequencyDetail) {
+        String[] parts = validateAndSplit(frequencyDetail, 2);
+        String daysOfWeek = Arrays.stream(parts[0].split("\\|"))
+                .map(this::convertSingleDayName)
+                .collect(Collectors.joining(","));
+        LocalTime time = parseTime(parts[1]);
+        return String.format("0 %d %d ? * %s", time.getMinute(), time.getHour(), daysOfWeek);
+    }
+
+    private String convertMonthlyToCron(String frequencyDetail) {
+        String[] parts = validateAndSplit(frequencyDetail, 2);
+        int day = validateDayOfMonth(parts[0]);
+        LocalTime time = parseTime(parts[1]);
+        return String.format("0 %d %d %d * ?", time.getMinute(), time.getHour(), day);
+    }
+
+    private String[] validateAndSplit(String input, int expectedParts) {
+        String[] parts = input.split(",");
+        if (parts.length != expectedParts) {
+            throw new IllegalArgumentException("浠诲姟棰戠巼璇︽儏鏍煎紡閿欒");
+        }
+        return Arrays.stream(parts).map(String::trim).toArray(String[]::new);
+    }
+
+    private LocalTime parseTime(String timeStr) {
+        try {
+            return LocalTime.parse(timeStr);
+        } catch (DateTimeParseException e) {
+            throw new IllegalArgumentException("鏃堕棿鏍煎紡蹇呴』涓篐H:mm", e);
+        }
+    }
+
+    private int validateDayOfMonth(String dayStr) {
+        int day = Integer.parseInt(dayStr);
+        if (day < 1 || day > 31) {
+            throw new IllegalArgumentException("鏃ユ湡蹇呴』鍦�1-31涔嬮棿");
+        }
+        return day;
+    }
+
+    private String convertSingleDayName(String dayName) {
+        return switch (dayName.toUpperCase()) {
+            case "MON" -> "MON";
+            case "TUE" -> "TUE";
+            case "WED" -> "WED";
+            case "THU" -> "THU";
+            case "FRI" -> "FRI";
+            case "SAT" -> "SAT";
+            case "SUN" -> "SUN";
+            default -> throw new IllegalArgumentException("鏃犳晥鐨勬槦鏈熷嚑: " + dayName);
+        };
+    }
+}
diff --git a/src/main/java/com/ruoyi/safe/service/impl/SafeLineInspectionTaskServiceImpl.java b/src/main/java/com/ruoyi/safe/service/impl/SafeLineInspectionTaskServiceImpl.java
new file mode 100644
index 0000000..277515c
--- /dev/null
+++ b/src/main/java/com/ruoyi/safe/service/impl/SafeLineInspectionTaskServiceImpl.java
@@ -0,0 +1,312 @@
+package com.ruoyi.safe.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.project.system.domain.SysUser;
+import com.ruoyi.project.system.mapper.SysUserMapper;
+import com.ruoyi.safe.mapper.SafeLineInspectionTaskMapper;
+import com.ruoyi.safe.pojo.SafeLineInspectionTask;
+import com.ruoyi.safe.service.SafeLineInspectionTaskService;
+import lombok.RequiredArgsConstructor;
+import org.quartz.SchedulerException;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.DayOfWeek;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.YearMonth;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * <p>
+ * 瀹夊叏鐢熶骇--绾胯矾瀹氭椂宸℃浠诲姟 鏈嶅姟瀹炵幇绫�
+ * </p>
+ */
+@Service
+@RequiredArgsConstructor
+public class SafeLineInspectionTaskServiceImpl extends ServiceImpl<SafeLineInspectionTaskMapper, SafeLineInspectionTask> implements SafeLineInspectionTaskService {
+
+    private static final int ENABLED = 1;
+    private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm");
+    private static final Map<String, DayOfWeek> WEEK_DAY_MAP = new HashMap<>();
+
+    static {
+        WEEK_DAY_MAP.put("MON", DayOfWeek.MONDAY);
+        WEEK_DAY_MAP.put("TUE", DayOfWeek.TUESDAY);
+        WEEK_DAY_MAP.put("WED", DayOfWeek.WEDNESDAY);
+        WEEK_DAY_MAP.put("THU", DayOfWeek.THURSDAY);
+        WEEK_DAY_MAP.put("FRI", DayOfWeek.FRIDAY);
+        WEEK_DAY_MAP.put("SAT", DayOfWeek.SATURDAY);
+        WEEK_DAY_MAP.put("SUN", DayOfWeek.SUNDAY);
+    }
+
+    private final SafeLineInspectionTaskMapper safeLineInspectionTaskMapper;
+    private final SafeLineInspectionTaskScheduler safeLineInspectionTaskScheduler;
+    private final SysUserMapper sysUserMapper;
+
+    @Override
+    public IPage<SafeLineInspectionTask> pageSafeLineInspectionTask(Page<SafeLineInspectionTask> page, SafeLineInspectionTask task) {
+        LambdaQueryWrapper<SafeLineInspectionTask> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.like(StringUtils.isNotBlank(task.getInspectionName()), SafeLineInspectionTask::getInspectionName, task.getInspectionName())
+                .like(StringUtils.isNotBlank(task.getLineName()), SafeLineInspectionTask::getLineName, task.getLineName())
+                .like(StringUtils.isNotBlank(task.getInspectionProject()), SafeLineInspectionTask::getInspectionProject, task.getInspectionProject())
+                .eq(task.getIsEnabled() != null, SafeLineInspectionTask::getIsEnabled, task.getIsEnabled())
+                .orderByDesc(SafeLineInspectionTask::getId);
+
+        IPage<SafeLineInspectionTask> result = safeLineInspectionTaskMapper.selectPage(page, queryWrapper);
+        fillInspectorName(result.getRecords());
+        return result;
+    }
+
+    @Override
+    @Transactional
+    public boolean saveOrUpdateTask(SafeLineInspectionTask task) throws SchedulerException {
+        SafeLineInspectionTask oldTask = null;
+        if (task.getId() != null) {
+            oldTask = safeLineInspectionTaskMapper.selectById(task.getId());
+            if (oldTask == null) {
+                throw new IllegalArgumentException("瀹氭椂宸℃浠诲姟涓嶅瓨鍦�");
+            }
+        }
+
+        normalizeTask(task, oldTask);
+        task.setNextExecutionTime(calculateFirstExecutionTime(task.getFrequencyType(), task.getFrequencyDetail()));
+
+        boolean result;
+        if (task.getId() == null) {
+            result = safeLineInspectionTaskMapper.insert(task) > 0;
+            if (result && isEnabled(task.getIsEnabled())) {
+                safeLineInspectionTaskScheduler.scheduleTask(task);
+            }
+            return result;
+        }
+
+        result = safeLineInspectionTaskMapper.updateById(task) > 0;
+        if (result) {
+            boolean oldEnabled = oldTask != null && isEnabled(oldTask.getIsEnabled());
+            boolean newEnabled = isEnabled(task.getIsEnabled());
+            if (!newEnabled) {
+                safeLineInspectionTaskScheduler.unscheduleTask(task.getId());
+            } else if (oldEnabled) {
+                safeLineInspectionTaskScheduler.rescheduleTask(task);
+            } else {
+                safeLineInspectionTaskScheduler.scheduleTask(task);
+            }
+        }
+        return result;
+    }
+
+    @Override
+    @Transactional
+    public boolean removeTaskByIds(List<Long> ids) {
+        boolean removed = removeBatchByIds(ids);
+        if (removed) {
+            for (Long id : ids) {
+                safeLineInspectionTaskScheduler.unscheduleTask(id);
+            }
+        }
+        return removed;
+    }
+
+    @Override
+    public LocalDateTime calculateFirstExecutionTime(String frequencyType, String frequencyDetail) {
+        LocalDateTime now = LocalDateTime.now();
+        return calculateNextExecutionTime(frequencyType, frequencyDetail, now);
+    }
+
+    @Override
+    public LocalDateTime calculateNextExecutionTime(String frequencyType, String frequencyDetail, LocalDateTime currentTime) {
+        validateFrequency(frequencyType, frequencyDetail);
+        return switch (frequencyType) {
+            case "DAILY" -> calculateDailyNextTime(frequencyDetail, currentTime);
+            case "WEEKLY" -> calculateWeeklyNextTime(frequencyDetail, currentTime);
+            case "MONTHLY" -> calculateMonthlyNextTime(frequencyDetail, currentTime);
+            default -> throw new IllegalArgumentException("涓嶆敮鎸佺殑棰戠巼绫诲瀷: " + frequencyType);
+        };
+    }
+
+    private void normalizeTask(SafeLineInspectionTask task, SafeLineInspectionTask oldTask) {
+        if (StringUtils.isBlank(task.getInspectionName())) {
+            throw new IllegalArgumentException("宸℃浠诲姟鍚嶇О涓嶈兘涓虹┖");
+        }
+        if (StringUtils.isBlank(task.getLineName())) {
+            throw new IllegalArgumentException("绾胯矾鍚嶇О涓嶈兘涓虹┖");
+        }
+        if (StringUtils.isBlank(task.getInspectionProject())) {
+            throw new IllegalArgumentException("宸℃椤圭洰涓嶈兘涓虹┖");
+        }
+        if (StringUtils.isBlank(task.getInspectorId())) {
+            throw new IllegalArgumentException("宸℃浜轰笉鑳戒负绌�");
+        }
+        task.setIsEnabled(resolveEnabledValue(task.getIsEnabled(), oldTask));
+        task.setFrequencyType(task.getFrequencyType() == null ? null : task.getFrequencyType().trim().toUpperCase());
+        task.setFrequencyDetail(task.getFrequencyDetail() == null ? null : task.getFrequencyDetail().trim());
+        validateFrequency(task.getFrequencyType(), task.getFrequencyDetail());
+    }
+
+    private void validateFrequency(String frequencyType, String frequencyDetail) {
+        if (StringUtils.isBlank(frequencyType) || StringUtils.isBlank(frequencyDetail)) {
+            throw new IllegalArgumentException("浠诲姟棰戠巼涓嶈兘涓虹┖");
+        }
+        switch (frequencyType) {
+            case "DAILY" -> parseTime(frequencyDetail);
+            case "WEEKLY" -> {
+                String[] parts = splitFrequencyDetail(frequencyDetail);
+                if (!WEEK_DAY_MAP.containsKey(parts[0])) {
+                    throw new IllegalArgumentException("鏃犳晥鐨勬槦鏈�: " + parts[0]);
+                }
+                parseTime(parts[1]);
+            }
+            case "MONTHLY" -> {
+                String[] parts = splitFrequencyDetail(frequencyDetail);
+                validateDayOfMonth(parts[0]);
+                parseTime(parts[1]);
+            }
+            default -> throw new IllegalArgumentException("涓嶆敮鎸佺殑棰戠巼绫诲瀷: " + frequencyType);
+        }
+    }
+
+    private LocalDateTime calculateDailyNextTime(String timeStr, LocalDateTime currentTime) {
+        LocalTime executionTime = parseTime(timeStr);
+        LocalDateTime candidate = LocalDateTime.of(currentTime.toLocalDate(), executionTime);
+        return currentTime.isBefore(candidate) ? candidate : candidate.plusDays(1);
+    }
+
+    private LocalDateTime calculateWeeklyNextTime(String detail, LocalDateTime currentTime) {
+        String[] parts = splitFrequencyDetail(detail);
+        DayOfWeek targetDay = WEEK_DAY_MAP.get(parts[0]);
+        LocalTime targetTime = parseTime(parts[1]);
+        int daysUntil = targetDay.getValue() - currentTime.getDayOfWeek().getValue();
+
+        if (daysUntil < 0 || (daysUntil == 0 && !currentTime.toLocalTime().isBefore(targetTime))) {
+            daysUntil += 7;
+        }
+        return LocalDateTime.of(currentTime.toLocalDate().plusDays(daysUntil), targetTime);
+    }
+
+    private LocalDateTime calculateMonthlyNextTime(String detail, LocalDateTime currentTime) {
+        String[] parts = splitFrequencyDetail(detail);
+        int dayOfMonth = validateDayOfMonth(parts[0]);
+        LocalTime targetTime = parseTime(parts[1]);
+        YearMonth currentMonth = YearMonth.from(currentTime);
+        for (int i = 0; i < 24; i++) {
+            YearMonth targetMonth = currentMonth.plusMonths(i);
+            if (dayOfMonth > targetMonth.lengthOfMonth()) {
+                continue;
+            }
+            LocalDateTime candidate = LocalDateTime.of(
+                    targetMonth.getYear(),
+                    targetMonth.getMonthValue(),
+                    dayOfMonth,
+                    targetTime.getHour(),
+                    targetTime.getMinute()
+            );
+            if (currentTime.isBefore(candidate)) {
+                return candidate;
+            }
+        }
+        throw new IllegalArgumentException("鏃犳硶璁$畻涓嬫鎵ц鏃堕棿");
+    }
+
+    private String[] splitFrequencyDetail(String detail) {
+        String[] parts = detail.split(",");
+        if (parts.length != 2) {
+            throw new IllegalArgumentException("棰戠巼璇︽儏鏍煎紡閿欒");
+        }
+        return Arrays.stream(parts).map(String::trim).toArray(String[]::new);
+    }
+
+    private LocalTime parseTime(String timeStr) {
+        try {
+            return LocalTime.parse(timeStr, TIME_FORMATTER);
+        } catch (DateTimeParseException e) {
+            throw new IllegalArgumentException("鏃堕棿鏍煎紡蹇呴』涓篐H:mm", e);
+        }
+    }
+
+    private int validateDayOfMonth(String dayStr) {
+        try {
+            int day = Integer.parseInt(dayStr);
+            if (day < 1 || day > 31) {
+                throw new IllegalArgumentException("鏃ユ湡蹇呴』鍦�1-31涔嬮棿");
+            }
+            return day;
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException("鏃ユ湡蹇呴』鍦�1-31涔嬮棿", e);
+        }
+    }
+
+    private Integer resolveEnabledValue(Integer requestEnabled, SafeLineInspectionTask oldTask) {
+        if (requestEnabled != null) {
+            return requestEnabled;
+        }
+        if (oldTask != null && oldTask.getIsEnabled() != null) {
+            return oldTask.getIsEnabled();
+        }
+        return ENABLED;
+    }
+
+    private boolean isEnabled(Integer enabledValue) {
+        return Objects.equals(enabledValue, ENABLED);
+    }
+
+    private void fillInspectorName(List<SafeLineInspectionTask> records) {
+        if (records == null || records.isEmpty()) {
+            return;
+        }
+
+        Set<Long> userIds = records.stream()
+                .map(SafeLineInspectionTask::getInspectorId)
+                .filter(StringUtils::isNotBlank)
+                .flatMap(ids -> Arrays.stream(ids.split(",")))
+                .map(String::trim)
+                .filter(StringUtils::isNotBlank)
+                .map(this::parseUserId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+
+        if (userIds.isEmpty()) {
+            return;
+        }
+
+        Map<Long, String> userNameMap = new HashMap<>();
+        List<SysUser> users = sysUserMapper.selectUserByIds(new ArrayList<>(userIds));
+        users.forEach(user -> userNameMap.put(user.getUserId(), user.getNickName()));
+
+        for (SafeLineInspectionTask record : records) {
+            if (StringUtils.isBlank(record.getInspectorId())) {
+                continue;
+            }
+            List<String> names = Arrays.stream(record.getInspectorId().split(","))
+                    .map(String::trim)
+                    .filter(StringUtils::isNotBlank)
+                    .map(this::parseUserId)
+                    .filter(Objects::nonNull)
+                    .map(id -> userNameMap.getOrDefault(id, "鏈煡鐢ㄦ埛"))
+                    .collect(Collectors.toList());
+            record.setInspector(names);
+            record.setInspectorName(String.join(",", names));
+        }
+    }
+
+    private Long parseUserId(String idStr) {
+        try {
+            return Long.parseLong(idStr);
+        } catch (NumberFormatException e) {
+            return null;
+        }
+    }
+}
diff --git a/src/main/resources/mapper/safe/SafeLineInspectionHazardMapper.xml b/src/main/resources/mapper/safe/SafeLineInspectionHazardMapper.xml
index cbe961c..3afb545 100644
--- a/src/main/resources/mapper/safe/SafeLineInspectionHazardMapper.xml
+++ b/src/main/resources/mapper/safe/SafeLineInspectionHazardMapper.xml
@@ -6,6 +6,7 @@
         <id property="id" column="id"/>
         <result property="inspectionId" column="inspection_id"/>
         <result property="recordId" column="record_id"/>
+        <result property="safeHiddenId" column="safe_hidden_id"/>
         <result property="hazardCode" column="hazard_code"/>
         <result property="hazardDesc" column="hazard_desc"/>
         <result property="hazardLevel" column="hazard_level"/>
@@ -13,6 +14,7 @@
         <result property="hazardImage" column="hazard_image"/>
         <result property="status" column="status"/>
         <result property="rectifyUserId" column="rectify_user_id"/>
+        <result property="rectifyDeadline" column="rectify_deadline"/>
         <result property="rectifyTime" column="rectify_time"/>
         <result property="rectifyDesc" column="rectify_desc"/>
         <result property="createTime" column="create_time"/>
diff --git a/src/main/resources/mapper/safe/SafeLineInspectionMapper.xml b/src/main/resources/mapper/safe/SafeLineInspectionMapper.xml
index ea740b9..8663b79 100644
--- a/src/main/resources/mapper/safe/SafeLineInspectionMapper.xml
+++ b/src/main/resources/mapper/safe/SafeLineInspectionMapper.xml
@@ -4,14 +4,19 @@
 
     <resultMap type="com.ruoyi.safe.pojo.SafeLineInspection" id="SafeLineInspectionResult">
         <id property="id" column="id"/>
+        <result property="scheduleTaskId" column="schedule_task_id"/>
         <result property="inspectionCode" column="inspection_code"/>
         <result property="inspectionName" column="inspection_name"/>
         <result property="lineName" column="line_name"/>
         <result property="inspectionType" column="inspection_type"/>
+        <result property="inspectionProject" column="inspection_project"/>
         <result property="planTime" column="plan_time"/>
         <result property="assignUserId" column="assign_user_id"/>
         <result property="inspectorId" column="inspector_id"/>
         <result property="status" column="status"/>
+        <result property="inspectionResult" column="inspection_result"/>
+        <result property="actualInspectionTime" column="actual_inspection_time"/>
+        <result property="hiddenId" column="hidden_id"/>
         <result property="remark" column="remark"/>
         <result property="createTime" column="create_time"/>
         <result property="createUser" column="create_user"/>
diff --git a/src/main/resources/mapper/safe/SafeLineInspectionTaskMapper.xml b/src/main/resources/mapper/safe/SafeLineInspectionTaskMapper.xml
new file mode 100644
index 0000000..028ef8a
--- /dev/null
+++ b/src/main/resources/mapper/safe/SafeLineInspectionTaskMapper.xml
@@ -0,0 +1,25 @@
+<?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.safe.mapper.SafeLineInspectionTaskMapper">
+
+    <resultMap type="com.ruoyi.safe.pojo.SafeLineInspectionTask" id="SafeLineInspectionTaskResult">
+        <id property="id" column="id"/>
+        <result property="inspectionName" column="inspection_name"/>
+        <result property="lineName" column="line_name"/>
+        <result property="inspectionProject" column="inspection_project"/>
+        <result property="inspectorId" column="inspector_id"/>
+        <result property="frequencyType" column="frequency_type"/>
+        <result property="frequencyDetail" column="frequency_detail"/>
+        <result property="nextExecutionTime" column="next_execution_time"/>
+        <result property="lastExecutionTime" column="last_execution_time"/>
+        <result property="isEnabled" column="is_enabled"/>
+        <result property="remark" column="remark"/>
+        <result property="createTime" column="create_time"/>
+        <result property="createUser" column="create_user"/>
+        <result property="updateTime" column="update_time"/>
+        <result property="updateUser" column="update_user"/>
+        <result property="tenantId" column="tenant_id"/>
+        <result property="deptId" column="dept_id"/>
+    </resultMap>
+
+</mapper>

--
Gitblit v1.9.3