From 5aa8be8da9aa87bfdcaed5e620643b5acd76d632 Mon Sep 17 00:00:00 2001
From: gongchunyi <deslre0381@gmail.com>
Date: 星期五, 16 十月 2026 20:44:34 +0800
Subject: [PATCH] feat: 设备保养优化,保养项目拆分

---
 src/main/java/com/ruoyi/device/controller/MaintenanceItemController.java      |   74 ++++
 src/main/java/com/ruoyi/device/pojo/MaintenanceItem.java                      |   46 +++
 src/main/java/com/ruoyi/device/mapper/MaintenanceItemMapper.java              |    9 
 src/main/java/com/ruoyi/device/mapper/MaintenanceTaskItemMapper.java          |   16 +
 src/main/java/com/ruoyi/device/mapper/DeviceMaintenanceItemMapper.java        |    9 
 src/main/java/com/ruoyi/device/controller/MaintenanceTaskController.java      |   33 +-
 src/main/java/com/ruoyi/device/pojo/MaintenanceTask.java                      |   10 
 src/main/java/com/ruoyi/device/service/MaintenanceItemService.java            |   21 +
 src/main/java/com/ruoyi/device/service/impl/MaintenanceItemServiceImpl.java   |  205 +++++++++++++
 src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskServiceImpl.java   |  114 +++++++
 src/main/java/com/ruoyi/device/service/impl/DeviceMaintenanceServiceImpl.java |  114 +++++++
 src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskJob.java           |   61 +++
 src/main/java/com/ruoyi/device/pojo/MaintenanceTaskItem.java                  |   36 ++
 doc/20261016_设备保养项目完整脚本.sql                                                   |   64 ++++
 src/main/java/com/ruoyi/device/pojo/DeviceMaintenanceItem.java                |   41 ++
 src/main/java/com/ruoyi/device/pojo/DeviceMaintenance.java                    |    6 
 16 files changed, 828 insertions(+), 31 deletions(-)

diff --git "a/doc/20261016_\350\256\276\345\244\207\344\277\235\345\205\273\351\241\271\347\233\256\345\256\214\346\225\264\350\204\232\346\234\254.sql" "b/doc/20261016_\350\256\276\345\244\207\344\277\235\345\205\273\351\241\271\347\233\256\345\256\214\346\225\264\350\204\232\346\234\254.sql"
new file mode 100644
index 0000000..693c500
--- /dev/null
+++ "b/doc/20261016_\350\256\276\345\244\207\344\277\235\345\205\273\351\241\271\347\233\256\345\256\214\346\225\264\350\204\232\346\234\254.sql"
@@ -0,0 +1,64 @@
+DROP TABLE IF EXISTS device_maintenance_item;
+DROP TABLE IF EXISTS maintenance_task_item;
+DROP TABLE IF EXISTS maintenance_item;
+
+CREATE TABLE maintenance_item
+(
+    id               BIGINT       NOT NULL AUTO_INCREMENT COMMENT '淇濆吇鍐呭ID',
+    item_name        TEXT         NOT NULL COMMENT '淇濆吇鍐呭',
+    item_project     VARCHAR(200) NOT NULL COMMENT '淇濆吇椤圭洰',
+    device_ledger_id BIGINT NULL COMMENT '鎵�灞炶澶囧彴璐D锛屼负绌鸿〃绀洪�氱敤淇濆吇鍐呭',
+    frequency_type   VARCHAR(20)  NOT NULL COMMENT '淇濆吇棰戠巼锛欴AILY/WEEKLY/MONTHLY/QUARTERLY/ALL锛堥�氱敤棰戠巼锛�',
+    sort_num         INT          NOT NULL DEFAULT 0 COMMENT '鏄剧ず椤哄簭',
+    is_active        TINYINT      NOT NULL DEFAULT 1 COMMENT '鐘舵�侊細1鍚敤锛�0鍋滅敤',
+    remarks          VARCHAR(500) NULL COMMENT '澶囨敞',
+    deleted          TINYINT      NOT NULL DEFAULT 0 COMMENT '鍒犻櫎鏍囧織锛�0姝e父锛�1鍒犻櫎',
+    create_user      INT NULL COMMENT '鍒涘缓浜�',
+    update_user      INT NULL COMMENT '鏇存柊浜�',
+    create_time      DATETIME NULL COMMENT '鍒涘缓鏃堕棿',
+    update_time      DATETIME NULL COMMENT '鏇存柊鏃堕棿',
+    tenant_id        BIGINT NULL COMMENT '绉熸埛ID',
+    dept_id          BIGINT NULL COMMENT '閮ㄩ棬ID',
+    PRIMARY KEY (id),
+    KEY              idx_maintenance_item_scope (tenant_id, dept_id, device_ledger_id, frequency_type, is_active, deleted),
+    KEY              idx_maintenance_item_device_frequency (device_ledger_id, frequency_type, is_active, deleted)
+) COMMENT='璁惧淇濆吇鍐呭瀛楀吀';
+
+CREATE TABLE maintenance_task_item
+(
+    id                  BIGINT       NOT NULL AUTO_INCREMENT COMMENT '浠诲姟椤圭洰鍏崇郴ID',
+    maintenance_task_id BIGINT       NOT NULL COMMENT '淇濆吇浠诲姟ID',
+    maintenance_item_id BIGINT NULL COMMENT '淇濆吇鍐呭ID锛屼负绌鸿〃绀烘湰浠诲姟涓撶敤鐨勮嚜瀹氫箟鍐呭',
+    item_project        VARCHAR(200) NOT NULL COMMENT '淇濆吇椤圭洰蹇収',
+    item_name           TEXT         NOT NULL COMMENT '浠诲姟閰嶇疆鏃剁殑淇濆吇鍐呭蹇収',
+    sort_num            INT          NOT NULL DEFAULT 0 COMMENT '浠诲姟鍐呮樉绀洪『搴�',
+    create_time         DATETIME NULL COMMENT '鍒涘缓鏃堕棿',
+    update_time         DATETIME NULL COMMENT '鏇存柊鏃堕棿',
+    tenant_id           BIGINT NULL COMMENT '绉熸埛ID',
+    dept_id             BIGINT NULL COMMENT '閮ㄩ棬ID',
+    PRIMARY KEY (id),
+    UNIQUE KEY uk_task_item (maintenance_task_id, maintenance_item_id),
+    KEY                 idx_task_item_item (maintenance_item_id)
+) COMMENT='璁惧淇濆吇浠诲姟椤圭洰';
+
+CREATE TABLE device_maintenance_item
+(
+    id                       BIGINT       NOT NULL AUTO_INCREMENT COMMENT '淇濆吇璁板綍椤圭洰ID',
+    device_maintenance_id    BIGINT       NOT NULL COMMENT '淇濆吇璁板綍ID',
+    maintenance_task_item_id BIGINT NULL COMMENT '鏉ユ簮浠诲姟椤圭洰ID',
+    maintenance_item_id      BIGINT NULL COMMENT '鏉ユ簮淇濆吇鍐呭ID',
+    item_project             VARCHAR(200) NOT NULL COMMENT '鏈淇濆吇椤圭洰蹇収',
+    item_name                TEXT         NOT NULL COMMENT '鏈淇濆吇鍐呭蹇収',
+    item_status              TINYINT      NOT NULL DEFAULT 0 COMMENT '椤圭洰鐘舵�侊細0寰呭畬鎴愶紝1宸插畬鎴愶紝2寮傚父/鏈畬鎴�',
+    completed_by             VARCHAR(100) NULL COMMENT '椤圭洰瀹屾垚鎿嶄綔浜�',
+    completed_time           DATETIME NULL COMMENT '椤圭洰瀹屾垚鏃堕棿',
+    remarks                  VARCHAR(500) NULL COMMENT '鍗曢」璇存槑',
+    sort_num                 INT          NOT NULL DEFAULT 0 COMMENT '璁板綍鍐呮樉绀洪『搴�',
+    create_time              DATETIME NULL COMMENT '鍒涘缓鏃堕棿',
+    update_time              DATETIME NULL COMMENT '鏇存柊鏃堕棿',
+    tenant_id                BIGINT NULL COMMENT '绉熸埛ID',
+    dept_id                  BIGINT NULL COMMENT '閮ㄩ棬ID',
+    PRIMARY KEY (id),
+    KEY                      idx_device_maintenance_item_record (device_maintenance_id, sort_num),
+    KEY                      idx_device_maintenance_item_task_item (maintenance_task_item_id)
+) COMMENT='璁惧淇濆吇璁板綍椤圭洰鏄庣粏';
diff --git a/src/main/java/com/ruoyi/device/controller/MaintenanceItemController.java b/src/main/java/com/ruoyi/device/controller/MaintenanceItemController.java
new file mode 100644
index 0000000..f4c65bf
--- /dev/null
+++ b/src/main/java/com/ruoyi/device/controller/MaintenanceItemController.java
@@ -0,0 +1,74 @@
+package com.ruoyi.device.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.device.pojo.MaintenanceItem;
+import com.ruoyi.device.service.MaintenanceItemService;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+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.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@Tag(name = "璁惧淇濆吇椤圭洰")
+@RestController
+@RequestMapping("/deviceMaintenanceItem")
+@RequiredArgsConstructor
+public class MaintenanceItemController {
+
+    private final MaintenanceItemService maintenanceItemService;
+
+    @GetMapping("/list")
+    @Operation(summary = "璁惧淇濆吇椤圭洰鍒楄〃")
+    public AjaxResult list(MaintenanceItem query) {
+        List<MaintenanceItem> items = maintenanceItemService.list(query);
+        return AjaxResult.success(items);
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "鍒嗛〉鏌ヨ璁惧淇濆吇椤圭洰")
+    public AjaxResult page(
+            @RequestParam(defaultValue = "1") long pageNum,
+            @RequestParam(defaultValue = "20") long pageSize,
+            @RequestParam(defaultValue = "false") boolean includeCommon,
+            MaintenanceItem query
+    ) {
+        Page<MaintenanceItem> result = maintenanceItemService.page(pageNum, pageSize, includeCommon, query);
+        return AjaxResult.success(result);
+    }
+
+    @PostMapping
+    @Log(title = "鏂板璁惧淇濆吇椤圭洰", businessType = BusinessType.INSERT)
+    @Operation(summary = "鏂板璁惧淇濆吇椤圭洰")
+    public AjaxResult add(@RequestBody MaintenanceItem item) {
+        maintenanceItemService.createItem(item);
+        return AjaxResult.success();
+    }
+
+    @PutMapping
+    @Log(title = "淇敼璁惧淇濆吇椤圭洰", businessType = BusinessType.UPDATE)
+    @Operation(summary = "淇敼璁惧淇濆吇椤圭洰")
+    public AjaxResult update(@RequestBody MaintenanceItem item) {
+        maintenanceItemService.updateItem(item);
+        return AjaxResult.success();
+    }
+
+    @DeleteMapping("/{id}")
+    @Log(title = "鍒犻櫎璁惧淇濆吇椤圭洰", businessType = BusinessType.DELETE)
+    @Operation(summary = "鍒犻櫎璁惧淇濆吇椤圭洰")
+    public AjaxResult delete(@PathVariable("id") Long id) {
+        maintenanceItemService.deleteItem(id);
+        return AjaxResult.success();
+    }
+}
diff --git a/src/main/java/com/ruoyi/device/controller/MaintenanceTaskController.java b/src/main/java/com/ruoyi/device/controller/MaintenanceTaskController.java
index b6f8c27..246498a 100644
--- a/src/main/java/com/ruoyi/device/controller/MaintenanceTaskController.java
+++ b/src/main/java/com/ruoyi/device/controller/MaintenanceTaskController.java
@@ -2,39 +2,38 @@
 
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ruoyi.device.pojo.MaintenanceTask;
+import com.ruoyi.device.service.MaintenanceItemService;
 import com.ruoyi.device.service.MaintenanceTaskService;
 import com.ruoyi.framework.aspectj.lang.annotation.Log;
 import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
 import com.ruoyi.framework.web.controller.BaseController;
 import com.ruoyi.framework.web.domain.AjaxResult;
-import io.swagger.v3.oas.annotations.tags.Tag;
 import io.swagger.v3.oas.annotations.Operation;
-import lombok.AllArgsConstructor;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+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;
 
-/**
- * @author :yys
- * @date : 2025/12/22 14:58
- */
 @Tag(name = "璁惧淇濆吇瀹氭椂浠诲姟绠$悊")
 @RestController
 @RequestMapping("/deviceMaintenanceTask")
-@AllArgsConstructor
+@RequiredArgsConstructor
 public class MaintenanceTaskController extends BaseController {
 
-
-    private MaintenanceTaskService maintenanceTaskService;
-
+    private final MaintenanceTaskService maintenanceTaskService;
+    private final MaintenanceItemService maintenanceItemService;
 
     @GetMapping("/listPage")
     @Operation(summary = "璁惧淇濆吇瀹氭椂浠诲姟鍒楄〃")
     public AjaxResult listPage(Page page, MaintenanceTask maintenanceTask) {
-        return maintenanceTaskService.listPage(page,maintenanceTask);
+        return maintenanceTaskService.listPage(page, maintenanceTask);
     }
-
 
     @PostMapping("/add")
     @Operation(summary = "娣诲姞璁惧淇濆吇瀹氭椂浠诲姟")
@@ -57,5 +56,9 @@
         return maintenanceTaskService.delete(ids);
     }
 
-
+    @GetMapping("/items")
+    @Operation(summary = "鍚敤鐨勮澶囦繚鍏婚」鐩�")
+    public AjaxResult items() {
+        return AjaxResult.success(maintenanceItemService.listActive());
+    }
 }
diff --git a/src/main/java/com/ruoyi/device/mapper/DeviceMaintenanceItemMapper.java b/src/main/java/com/ruoyi/device/mapper/DeviceMaintenanceItemMapper.java
new file mode 100644
index 0000000..70183ab
--- /dev/null
+++ b/src/main/java/com/ruoyi/device/mapper/DeviceMaintenanceItemMapper.java
@@ -0,0 +1,9 @@
+package com.ruoyi.device.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.device.pojo.DeviceMaintenanceItem;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface DeviceMaintenanceItemMapper extends BaseMapper<DeviceMaintenanceItem> {
+}
diff --git a/src/main/java/com/ruoyi/device/mapper/MaintenanceItemMapper.java b/src/main/java/com/ruoyi/device/mapper/MaintenanceItemMapper.java
new file mode 100644
index 0000000..e6bf69d
--- /dev/null
+++ b/src/main/java/com/ruoyi/device/mapper/MaintenanceItemMapper.java
@@ -0,0 +1,9 @@
+package com.ruoyi.device.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.device.pojo.MaintenanceItem;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface MaintenanceItemMapper extends BaseMapper<MaintenanceItem> {
+}
diff --git a/src/main/java/com/ruoyi/device/mapper/MaintenanceTaskItemMapper.java b/src/main/java/com/ruoyi/device/mapper/MaintenanceTaskItemMapper.java
new file mode 100644
index 0000000..654ab60
--- /dev/null
+++ b/src/main/java/com/ruoyi/device/mapper/MaintenanceTaskItemMapper.java
@@ -0,0 +1,16 @@
+package com.ruoyi.device.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
+import com.ruoyi.device.pojo.MaintenanceTaskItem;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
+
+@Mapper
+public interface MaintenanceTaskItemMapper extends BaseMapper<MaintenanceTaskItem> {
+    @Select("SELECT * FROM maintenance_task_item WHERE maintenance_task_id = #{taskId} ORDER BY sort_num, id")
+    @InterceptorIgnore(tenantLine = "true")
+    List<MaintenanceTaskItem> selectByTaskId(Long taskId);
+}
diff --git a/src/main/java/com/ruoyi/device/pojo/DeviceMaintenance.java b/src/main/java/com/ruoyi/device/pojo/DeviceMaintenance.java
index b635e5f..8f37ffc 100644
--- a/src/main/java/com/ruoyi/device/pojo/DeviceMaintenance.java
+++ b/src/main/java/com/ruoyi/device/pojo/DeviceMaintenance.java
@@ -95,6 +95,12 @@
     @TableField(exist = false)
     private List<SparePartUse> sparePartsUseList;
 
+    @TableField(exist = false)
+    private List<Long> maintenanceItemIds;
+
+    @TableField(exist = false)
+    private List<DeviceMaintenanceItem> maintenanceItems;
+
     @Data
     public static class SparePartUse {
         private Long id;
diff --git a/src/main/java/com/ruoyi/device/pojo/DeviceMaintenanceItem.java b/src/main/java/com/ruoyi/device/pojo/DeviceMaintenanceItem.java
new file mode 100644
index 0000000..85dc03f
--- /dev/null
+++ b/src/main/java/com/ruoyi/device/pojo/DeviceMaintenanceItem.java
@@ -0,0 +1,41 @@
+package com.ruoyi.device.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 lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+@TableName("device_maintenance_item")
+public class DeviceMaintenanceItem {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private Long deviceMaintenanceId;
+    private Long maintenanceTaskItemId;
+    private Long maintenanceItemId;
+    private String itemProject;
+    private String itemName;
+    private Integer itemStatus;
+    private String completedBy;
+    private LocalDateTime completedTime;
+    private String remarks;
+    private Integer sortNum;
+
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Long tenantId;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+}
diff --git a/src/main/java/com/ruoyi/device/pojo/MaintenanceItem.java b/src/main/java/com/ruoyi/device/pojo/MaintenanceItem.java
new file mode 100644
index 0000000..e4e7e04
--- /dev/null
+++ b/src/main/java/com/ruoyi/device/pojo/MaintenanceItem.java
@@ -0,0 +1,46 @@
+package com.ruoyi.device.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 lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+@TableName("maintenance_item")
+public class MaintenanceItem {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private String itemName;
+    private String itemProject;
+    private Long deviceLedgerId;
+    private String frequencyType;
+
+    private Integer sortNum;
+    private Integer isActive;
+    private String remarks;
+    private Integer deleted;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Integer createUser;
+
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Integer updateUser;
+
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Long tenantId;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+}
diff --git a/src/main/java/com/ruoyi/device/pojo/MaintenanceTask.java b/src/main/java/com/ruoyi/device/pojo/MaintenanceTask.java
index 3fedf2c..14fe4b7 100644
--- a/src/main/java/com/ruoyi/device/pojo/MaintenanceTask.java
+++ b/src/main/java/com/ruoyi/device/pojo/MaintenanceTask.java
@@ -13,6 +13,7 @@
 
 import java.time.LocalDate;
 import java.time.LocalDateTime;
+import java.util.List;
 
 /**
  * @author :yys
@@ -91,6 +92,15 @@
     @TableField(exist = false)
     private String dateStr;
 
+    @TableField(exist = false)
+    private List<Long> maintenanceItemIds;
+
+    @TableField(exist = false)
+    private List<MaintenanceTaskItem> customItems;
+
+    @TableField(exist = false)
+    private List<MaintenanceTaskItem> maintenanceItems;
+
     @Schema(description = "鍒涘缓璇ヨ褰曠殑鐢ㄦ埛")
     @TableField(fill = com.baomidou.mybatisplus.annotation.FieldFill.INSERT)
     private Integer createUser;
diff --git a/src/main/java/com/ruoyi/device/pojo/MaintenanceTaskItem.java b/src/main/java/com/ruoyi/device/pojo/MaintenanceTaskItem.java
new file mode 100644
index 0000000..845d0ac
--- /dev/null
+++ b/src/main/java/com/ruoyi/device/pojo/MaintenanceTaskItem.java
@@ -0,0 +1,36 @@
+package com.ruoyi.device.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 lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+@TableName("maintenance_task_item")
+public class MaintenanceTaskItem {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private Long maintenanceTaskId;
+    private Long maintenanceItemId;
+    private String itemProject;
+    private String itemName;
+    private Integer sortNum;
+
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Long tenantId;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+}
diff --git a/src/main/java/com/ruoyi/device/service/MaintenanceItemService.java b/src/main/java/com/ruoyi/device/service/MaintenanceItemService.java
new file mode 100644
index 0000000..3d3a53f
--- /dev/null
+++ b/src/main/java/com/ruoyi/device/service/MaintenanceItemService.java
@@ -0,0 +1,21 @@
+package com.ruoyi.device.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.device.pojo.MaintenanceItem;
+
+import java.util.List;
+
+public interface MaintenanceItemService {
+
+    List<MaintenanceItem> listActive();
+
+    List<MaintenanceItem> list(MaintenanceItem query);
+
+    Page<MaintenanceItem> page(long pageNum, long pageSize, boolean includeCommon, MaintenanceItem query);
+
+    void createItem(MaintenanceItem item);
+
+    void updateItem(MaintenanceItem item);
+
+    void deleteItem(Long id);
+}
diff --git a/src/main/java/com/ruoyi/device/service/impl/DeviceMaintenanceServiceImpl.java b/src/main/java/com/ruoyi/device/service/impl/DeviceMaintenanceServiceImpl.java
index fbf03ad..6e0ab49 100644
--- a/src/main/java/com/ruoyi/device/service/impl/DeviceMaintenanceServiceImpl.java
+++ b/src/main/java/com/ruoyi/device/service/impl/DeviceMaintenanceServiceImpl.java
@@ -11,7 +11,11 @@
 import com.ruoyi.device.dto.DeviceMaintenanceDto;
 import com.ruoyi.device.execl.DeviceMaintenanceExeclDto;
 import com.ruoyi.device.mapper.DeviceMaintenanceMapper;
+import com.ruoyi.device.mapper.DeviceMaintenanceItemMapper;
+import com.ruoyi.device.mapper.MaintenanceItemMapper;
 import com.ruoyi.device.pojo.DeviceMaintenance;
+import com.ruoyi.device.pojo.DeviceMaintenanceItem;
+import com.ruoyi.device.pojo.MaintenanceItem;
 import com.ruoyi.device.service.IDeviceMaintenanceService;
 import com.ruoyi.device.vo.DeviceMaintenanceVo;
 import com.ruoyi.device.vo.DeviceRepairVo;
@@ -20,15 +24,23 @@
 import com.ruoyi.measuringinstrumentledger.pojo.SpareParts;
 import com.ruoyi.measuringinstrumentledger.pojo.SparePartsRequisitionRecord;
 import com.ruoyi.measuringinstrumentledger.service.SparePartsRequisitionRecordService;
+import com.github.xiaoymin.knife4j.core.util.CollectionUtils;
 import jakarta.servlet.http.HttpServletResponse;
 import lombok.RequiredArgsConstructor;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 
 import java.math.BigDecimal;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.time.LocalDateTime;
 
 
 @Service
@@ -39,18 +51,29 @@
     private final SparePartsMapper sparePartsMapper;
     private final SparePartsRequisitionRecordService sparePartsRequisitionRecordService;
     private final FileUtil fileUtil;
+    private final DeviceMaintenanceItemMapper deviceMaintenanceItemMapper;
+    private final MaintenanceItemMapper maintenanceItemMapper;
 
     @Override
     public IPage<DeviceMaintenanceDto> queryPage(Page page, DeviceMaintenanceDto deviceMaintenanceDto) {
-
-        return deviceMaintenanceMapper.queryPage(page, deviceMaintenanceDto);
+        IPage<DeviceMaintenanceDto> result = deviceMaintenanceMapper.queryPage(page, deviceMaintenanceDto);
+        if (result.getRecords() != null && !result.getRecords().isEmpty()) {
+            List<Long> ids = result.getRecords().stream().map(DeviceMaintenance::getId).toList();
+            List<DeviceMaintenanceItem> items = deviceMaintenanceItemMapper.selectList(new LambdaQueryWrapper<DeviceMaintenanceItem>().in(DeviceMaintenanceItem::getDeviceMaintenanceId, ids).orderByAsc(DeviceMaintenanceItem::getSortNum));
+            Map<Long, List<DeviceMaintenanceItem>> byRecord = items.stream().collect(Collectors.groupingBy(DeviceMaintenanceItem::getDeviceMaintenanceId));
+            result.getRecords().forEach(record -> record.setMaintenanceItems(byRecord.getOrDefault(record.getId(), new ArrayList<>())));
+        }
+        return result;
     }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
     public AjaxResult saveDeviceRepair(DeviceMaintenanceDto deviceMaintenance) {
+        List<MaintenanceItem> selectedItems = getSelectedItems(deviceMaintenance.getMaintenanceItemIds(), null, deviceMaintenance.getDeviceLedgerId(), deviceMaintenance.getFrequencyType());
+        if (selectedItems == null) return AjaxResult.error("璇烽�夋嫨鑷冲皯涓�涓湁鏁堢殑淇濆吇椤圭洰");
         boolean save = this.save(deviceMaintenance);
-        if (save){
+        if (save) {
+            saveRecordItems(deviceMaintenance, selectedItems);
             // 澶勭悊鍥剧墖涓婁紶
             fileUtil.saveStorageAttachmentByRecordTypeAndRecordId("file", RecordTypeEnum.DEVICE_MAINTENANCE, deviceMaintenance.getId(), deviceMaintenance.getStorageBlobDTOs());
             return AjaxResult.success();
@@ -62,8 +85,13 @@
     @Transactional(rollbackFor = Exception.class)
     public AjaxResult updateDeviceDeviceMaintenance(DeviceMaintenanceDto deviceMaintenance) {
         DeviceMaintenance oldDeviceMaintenance = this.getById(deviceMaintenance.getId());
+        if (oldDeviceMaintenance == null) return AjaxResult.error("淇濆吇璁板綍涓嶅瓨鍦�");
+        if (deviceMaintenance.getMaintenanceItems() != null) {
+            AjaxResult validation = validateRecordItems(deviceMaintenance, oldDeviceMaintenance);
+            if (validation != null) return validation;
+        }
         // 澶勭悊澶囦欢浣跨敤鎯呭喌
-        if (com.github.xiaoymin.knife4j.core.util.CollectionUtils.isNotEmpty(deviceMaintenance.getSparePartsUseList())) {
+        if (CollectionUtils.isNotEmpty(deviceMaintenance.getSparePartsUseList())) {
             List<Long> sparePartIds = new ArrayList<>();
             for (DeviceMaintenance.SparePartUse sparePartUse : deviceMaintenance.getSparePartsUseList()) {
                 // 鑾峰彇澶囦欢淇℃伅
@@ -95,6 +123,16 @@
             }
         }
 
+        if (deviceMaintenance.getMaintenanceItems() != null) {
+            AjaxResult itemResult = updateRecordItems(deviceMaintenance, oldDeviceMaintenance);
+            if (itemResult != null) return itemResult;
+        } else if (deviceMaintenance.getMaintenanceItemIds() != null) {
+            List<MaintenanceItem> selectedItems = getSelectedItems(deviceMaintenance.getMaintenanceItemIds(), deviceMaintenance.getId(), oldDeviceMaintenance.getDeviceLedgerId(), oldDeviceMaintenance.getFrequencyType());
+            if (selectedItems == null) return AjaxResult.error("璇烽�夋嫨鑷冲皯涓�涓湁鏁堢殑淇濆吇椤圭洰");
+            deviceMaintenanceItemMapper.delete(new LambdaQueryWrapper<DeviceMaintenanceItem>().eq(DeviceMaintenanceItem::getDeviceMaintenanceId, deviceMaintenance.getId()));
+            saveRecordItems(deviceMaintenance, selectedItems);
+        }
+
         if (this.updateById(deviceMaintenance)) {
             // 澶勭悊鍥剧墖涓婁紶
             fileUtil.saveStorageAttachmentByRecordTypeAndRecordId("file", RecordTypeEnum.DEVICE_MAINTENANCE, deviceMaintenance.getId(), deviceMaintenance.getStorageBlobDTOs());
@@ -109,7 +147,9 @@
         ArrayList<DeviceMaintenanceExeclDto> deviceLedgerExeclDtos = new ArrayList<>();
         supplierManageList.forEach(deviceMaintenance -> {
             DeviceMaintenanceExeclDto deviceRepairExeclDto = new DeviceMaintenanceExeclDto();
-            BeanUtils.copyProperties(deviceMaintenance,deviceRepairExeclDto);
+            BeanUtils.copyProperties(deviceMaintenance, deviceRepairExeclDto);
+            List<DeviceMaintenanceItem> recordItems = deviceMaintenanceItemMapper.selectList(new LambdaQueryWrapper<DeviceMaintenanceItem>().eq(DeviceMaintenanceItem::getDeviceMaintenanceId, deviceMaintenance.getId()).orderByAsc(DeviceMaintenanceItem::getSortNum));
+            deviceRepairExeclDto.setMachineryCategory(recordItems.stream().map(DeviceMaintenanceItem::getItemName).collect(Collectors.joining("銆�")));
             deviceRepairExeclDto.setStatus(deviceMaintenance.getStatus() == 0 ? "寰呯淮淇�" : deviceMaintenance.getStatus() == 1 ? "瀹岀粨" : "澶辫触");
 //            deviceRepairExeclDto.setMaintenanceResult(deviceMaintenance.getMaintenanceResult() != null && deviceMaintenance.getMaintenanceResult() == 0 ? "缁翠慨" : "瀹屽ソ");
             deviceLedgerExeclDtos.add(deviceRepairExeclDto);
@@ -121,7 +161,71 @@
     @Override
     public DeviceMaintenanceVo detailById(Long id) {
         DeviceMaintenanceVo vo = deviceMaintenanceMapper.detailById(id);
+        if (vo != null) {
+            vo.setMaintenanceItems(deviceMaintenanceItemMapper.selectList(new LambdaQueryWrapper<DeviceMaintenanceItem>().eq(DeviceMaintenanceItem::getDeviceMaintenanceId, id).orderByAsc(DeviceMaintenanceItem::getSortNum)));
+        }
         vo.setStorageBlobVOs(fileUtil.getStorageBlobVOsByRecordTypeAndRecordId(RecordTypeEnum.DEVICE_MAINTENANCE, id));
         return vo;
     }
+
+    private List<MaintenanceItem> getSelectedItems(List<Long> ids, Long recordId, Long deviceId, String frequencyType) {
+        if (deviceId == null || !List.of("DAILY", "WEEKLY", "MONTHLY", "QUARTERLY").contains(frequencyType) || ids == null || ids.isEmpty() || new HashSet<>(ids).size() != ids.size())
+            return null;
+        List<MaintenanceItem> items = maintenanceItemMapper.selectList(new LambdaQueryWrapper<MaintenanceItem>().in(MaintenanceItem::getId, ids));
+        if (items.size() != ids.size()) return null;
+        if (items.stream().anyMatch(item -> (item.getDeviceLedgerId() != null && !deviceId.equals(item.getDeviceLedgerId())) || !(frequencyType.equals(item.getFrequencyType()) || "ALL".equals(item.getFrequencyType()))))
+            return null;
+        Map<Long, MaintenanceItem> itemMap = items.stream().collect(Collectors.toMap(MaintenanceItem::getId, item -> item));
+        Set<Long> existingIds = recordId == null ? Collections.emptySet() : deviceMaintenanceItemMapper.selectList(new LambdaQueryWrapper<DeviceMaintenanceItem>().eq(DeviceMaintenanceItem::getDeviceMaintenanceId, recordId)).stream().map(DeviceMaintenanceItem::getMaintenanceItemId).collect(Collectors.toSet());
+        if (items.stream().anyMatch(item -> (item.getDeleted() != null && item.getDeleted() == 1 || item.getIsActive() == null || item.getIsActive() != 1) && !existingIds.contains(item.getId())))
+            return null;
+        return ids.stream().map(itemMap::get).toList();
+    }
+
+    private void saveRecordItems(DeviceMaintenance record, List<MaintenanceItem> selectedItems) {
+        for (int i = 0; i < selectedItems.size(); i++) {
+            MaintenanceItem source = selectedItems.get(i);
+            DeviceMaintenanceItem item = new DeviceMaintenanceItem();
+            item.setDeviceMaintenanceId(record.getId());
+            item.setMaintenanceItemId(source.getId());
+            item.setItemProject(source.getItemProject());
+            item.setItemName(source.getItemName());
+            item.setItemStatus(0);
+            item.setSortNum(i);
+            item.setTenantId(record.getTenantId());
+            item.setDeptId(record.getDeptId());
+            deviceMaintenanceItemMapper.insert(item);
+        }
+    }
+
+    private AjaxResult updateRecordItems(DeviceMaintenanceDto request, DeviceMaintenance record) {
+        AjaxResult validation = validateRecordItems(request, record);
+        if (validation != null) return validation;
+        List<DeviceMaintenanceItem> current = deviceMaintenanceItemMapper.selectList(new LambdaQueryWrapper<DeviceMaintenanceItem>().eq(DeviceMaintenanceItem::getDeviceMaintenanceId, record.getId()));
+        Map<Long, DeviceMaintenanceItem> currentById = current.stream().collect(Collectors.toMap(DeviceMaintenanceItem::getId, item -> item));
+        for (DeviceMaintenanceItem requested : request.getMaintenanceItems()) {
+            DeviceMaintenanceItem item = currentById.get(requested.getId());
+            int status = requested.getItemStatus() == null ? 0 : requested.getItemStatus();
+            item.setItemStatus(status);
+            if (requested.getRemarks() != null) item.setRemarks(requested.getRemarks());
+            item.setCompletedBy(status == 1 ? request.getMaintenanceActuallyName() : null);
+            item.setCompletedTime(status == 1 ? LocalDateTime.now() : null);
+            deviceMaintenanceItemMapper.updateById(item);
+        }
+        return null;
+    }
+
+    private AjaxResult validateRecordItems(DeviceMaintenanceDto request, DeviceMaintenance record) {
+        List<DeviceMaintenanceItem> requestedItems = request.getMaintenanceItems();
+        if (requestedItems.stream().anyMatch(item -> item == null || item.getId() == null || (item.getItemStatus() != null && (item.getItemStatus() < 0 || item.getItemStatus() > 2)))) {
+            return AjaxResult.error("淇濆吇椤圭洰鐘舵�佹棤鏁�");
+        }
+        List<DeviceMaintenanceItem> current = deviceMaintenanceItemMapper.selectList(new LambdaQueryWrapper<DeviceMaintenanceItem>().eq(DeviceMaintenanceItem::getDeviceMaintenanceId, record.getId()));
+        Set<Long> currentIds = current.stream().map(DeviceMaintenanceItem::getId).collect(Collectors.toSet());
+        Set<Long> requestedIds = requestedItems.stream().map(DeviceMaintenanceItem::getId).collect(Collectors.toSet());
+        if (requestedIds.size() != current.size() || !requestedIds.equals(currentIds)) {
+            return AjaxResult.error("淇濆吇椤圭洰娓呭崟宸插彉鍖栵紝璇峰埛鏂板悗閲嶈瘯");
+        }
+        return null;
+    }
 }
diff --git a/src/main/java/com/ruoyi/device/service/impl/MaintenanceItemServiceImpl.java b/src/main/java/com/ruoyi/device/service/impl/MaintenanceItemServiceImpl.java
new file mode 100644
index 0000000..b9f63b6
--- /dev/null
+++ b/src/main/java/com/ruoyi/device/service/impl/MaintenanceItemServiceImpl.java
@@ -0,0 +1,205 @@
+package com.ruoyi.device.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.device.mapper.MaintenanceItemMapper;
+import com.ruoyi.device.pojo.MaintenanceItem;
+import com.ruoyi.device.service.MaintenanceItemService;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+@Service
+@RequiredArgsConstructor
+public class MaintenanceItemServiceImpl
+        extends ServiceImpl<MaintenanceItemMapper, MaintenanceItem>
+        implements MaintenanceItemService {
+
+    private static final List<String> VALID_FREQUENCIES = List.of(
+            "DAILY",
+            "WEEKLY",
+            "MONTHLY",
+            "QUARTERLY",
+            "ALL"
+    );
+
+    private final MaintenanceItemMapper maintenanceItemMapper;
+
+    @Override
+    public List<MaintenanceItem> listActive() {
+        return list(new LambdaQueryWrapper<MaintenanceItem>()
+                .eq(MaintenanceItem::getDeleted, 0)
+                .eq(MaintenanceItem::getIsActive, 1)
+                .orderByAsc(MaintenanceItem::getSortNum)
+                .orderByAsc(MaintenanceItem::getId));
+    }
+
+    @Override
+    public List<MaintenanceItem> list(MaintenanceItem query) {
+        LambdaQueryWrapper<MaintenanceItem> wrapper = buildQueryWrapper(query, false)
+                .orderByDesc(MaintenanceItem::getId);
+        return maintenanceItemMapper.selectList(wrapper);
+    }
+
+    @Override
+    public Page<MaintenanceItem> page(
+            long pageNum,
+            long pageSize,
+            boolean includeCommon,
+            MaintenanceItem query
+    ) {
+        long safePageNum = Math.max(pageNum, 1);
+        long safePageSize = Math.min(Math.max(pageSize, 1), 100);
+
+        LambdaQueryWrapper<MaintenanceItem> wrapper = buildQueryWrapper(query, includeCommon)
+                .orderByAsc(MaintenanceItem::getSortNum)
+                .orderByAsc(MaintenanceItem::getId);
+        return maintenanceItemMapper.selectPage(new Page<>(safePageNum, safePageSize), wrapper);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void createItem(MaintenanceItem item) {
+        if (!isValid(item)) {
+            throw new ServiceException("淇濆吇椤圭洰銆佷繚鍏诲唴瀹瑰拰棰戠巼涓嶈兘涓虹┖鎴栨棤鏁�");
+        }
+
+        normalize(item);
+        item.setDeleted(0);
+        if (existsName(item, null)) {
+            throw new ServiceException("璇ヨ澶囧拰棰戠巼涓嬪凡瀛樺湪鍚屽悕淇濆吇鍐呭");
+        }
+
+        if (maintenanceItemMapper.insert(item) <= 0) {
+            throw new ServiceException("鏂板淇濆吇椤圭洰澶辫触");
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateItem(MaintenanceItem item) {
+        if (item == null || item.getId() == null || !isValid(item)) {
+            throw new ServiceException("淇濆吇椤圭洰銆佷繚鍏诲唴瀹瑰拰棰戠巼涓嶈兘涓虹┖鎴栨棤鏁�");
+        }
+
+        MaintenanceItem existingItem = maintenanceItemMapper.selectById(item.getId());
+        if (existingItem == null || Integer.valueOf(1).equals(existingItem.getDeleted())) {
+            throw new ServiceException("淇濆吇椤圭洰涓嶅瓨鍦�");
+        }
+
+        normalize(item);
+        if (existsName(item, item.getId())) {
+            throw new ServiceException("璇ヨ澶囧拰棰戠巼涓嬪凡瀛樺湪鍚屽悕淇濆吇鍐呭");
+        }
+
+        existingItem.setItemProject(item.getItemProject());
+        existingItem.setItemName(item.getItemName());
+        existingItem.setDeviceLedgerId(item.getDeviceLedgerId());
+        existingItem.setFrequencyType(item.getFrequencyType());
+        existingItem.setSortNum(item.getSortNum() == null ? 0 : item.getSortNum());
+        existingItem.setIsActive(item.getIsActive() == null ? existingItem.getIsActive() : item.getIsActive());
+        existingItem.setRemarks(item.getRemarks());
+
+        if (maintenanceItemMapper.updateById(existingItem) <= 0) {
+            throw new ServiceException("淇敼淇濆吇椤圭洰澶辫触");
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void deleteItem(Long id) {
+        if (id == null) {
+            throw new ServiceException("淇濆吇椤圭洰缂栧彿涓嶈兘涓虹┖");
+        }
+
+        MaintenanceItem item = maintenanceItemMapper.selectById(id);
+        if (item == null || Integer.valueOf(1).equals(item.getDeleted())) {
+            throw new ServiceException("淇濆吇椤圭洰涓嶅瓨鍦�");
+        }
+
+        item.setDeleted(1);
+        item.setIsActive(0);
+        if (maintenanceItemMapper.updateById(item) <= 0) {
+            throw new ServiceException("鍒犻櫎淇濆吇椤圭洰澶辫触");
+        }
+    }
+
+    private LambdaQueryWrapper<MaintenanceItem> buildQueryWrapper(
+            MaintenanceItem query,
+            boolean includeCommon
+    ) {
+        MaintenanceItem filter = query == null ? new MaintenanceItem() : query;
+        LambdaQueryWrapper<MaintenanceItem> wrapper = new LambdaQueryWrapper<MaintenanceItem>()
+                .eq(MaintenanceItem::getDeleted, 0);
+
+        if (StringUtils.isNotBlank(filter.getItemName())) {
+            wrapper.like(MaintenanceItem::getItemName, filter.getItemName().trim());
+        }
+        if (StringUtils.isNotBlank(filter.getItemProject())) {
+            wrapper.like(MaintenanceItem::getItemProject, filter.getItemProject().trim());
+        }
+        if (filter.getIsActive() != null) {
+            wrapper.eq(MaintenanceItem::getIsActive, filter.getIsActive());
+        }
+        if (filter.getDeviceLedgerId() != null) {
+            if (includeCommon) {
+                wrapper.and(condition -> condition
+                        .eq(MaintenanceItem::getDeviceLedgerId, filter.getDeviceLedgerId())
+                        .or()
+                        .isNull(MaintenanceItem::getDeviceLedgerId));
+            } else {
+                wrapper.eq(MaintenanceItem::getDeviceLedgerId, filter.getDeviceLedgerId());
+            }
+        }
+        if (StringUtils.isNotBlank(filter.getFrequencyType())) {
+            if (includeCommon) {
+                wrapper.and(condition -> condition
+                        .eq(MaintenanceItem::getFrequencyType, filter.getFrequencyType())
+                        .or()
+                        .eq(MaintenanceItem::getFrequencyType, "ALL"));
+            } else {
+                wrapper.eq(MaintenanceItem::getFrequencyType, filter.getFrequencyType());
+            }
+        }
+
+        return wrapper;
+    }
+
+    private boolean isValid(MaintenanceItem item) {
+        return item != null
+                && StringUtils.isNotBlank(item.getItemProject())
+                && StringUtils.isNotBlank(item.getItemName())
+                && StringUtils.isNotBlank(item.getFrequencyType())
+                && VALID_FREQUENCIES.contains(item.getFrequencyType());
+    }
+
+    private void normalize(MaintenanceItem item) {
+        item.setItemProject(item.getItemProject().trim());
+        item.setItemName(item.getItemName().trim());
+        item.setSortNum(item.getSortNum() == null ? 0 : item.getSortNum());
+        item.setIsActive(item.getIsActive() == null ? 1 : item.getIsActive());
+    }
+
+    private boolean existsName(MaintenanceItem item, Long excludedId) {
+        LambdaQueryWrapper<MaintenanceItem> wrapper = new LambdaQueryWrapper<MaintenanceItem>()
+                .eq(MaintenanceItem::getDeleted, 0)
+                .eq(MaintenanceItem::getItemName, item.getItemName())
+                .eq(MaintenanceItem::getFrequencyType, item.getFrequencyType());
+
+        if (item.getDeviceLedgerId() == null) {
+            wrapper.isNull(MaintenanceItem::getDeviceLedgerId);
+        } else {
+            wrapper.eq(MaintenanceItem::getDeviceLedgerId, item.getDeviceLedgerId());
+        }
+        if (excludedId != null) {
+            wrapper.ne(MaintenanceItem::getId, excludedId);
+        }
+
+        return maintenanceItemMapper.selectCount(wrapper) > 0;
+    }
+}
diff --git a/src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskJob.java b/src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskJob.java
index 204667b..58693ca 100644
--- a/src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskJob.java
+++ b/src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskJob.java
@@ -1,6 +1,11 @@
 package com.ruoyi.device.service.impl;
 
 import com.ruoyi.device.pojo.DeviceMaintenance;
+import com.ruoyi.device.mapper.MaintenanceTaskItemMapper;
+import com.ruoyi.device.mapper.DeviceMaintenanceItemMapper;
+import com.ruoyi.device.pojo.MaintenanceTaskItem;
+import com.ruoyi.device.pojo.DeviceMaintenanceItem;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.ruoyi.device.pojo.MaintenanceTask;
 import lombok.RequiredArgsConstructor;
 import org.quartz.*;
@@ -29,6 +34,12 @@
     @Autowired
     private JdbcTemplate jdbcTemplate;
 
+    @Autowired
+    private MaintenanceTaskItemMapper maintenanceTaskItemMapper;
+
+    @Autowired
+    private DeviceMaintenanceItemMapper deviceMaintenanceItemMapper;
+
     @Override
     public void execute(JobExecutionContext context) throws JobExecutionException {
         JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
@@ -49,9 +60,26 @@
                 throw new JobExecutionException("MaintenanceTaskJob鎵句笉鍒板畾鏃朵换鍔�: " + taskId);
             }
 
-            // 2. 鍒涘缓骞朵繚瀛樺贰妫�浠诲姟璁板綍 - 杩欏氨鏄偍鎻愪緵鐨勪唬鐮佸簲璇ユ斁鐨勪綅缃�
+            List<MaintenanceTaskItem> taskItems = maintenanceTaskItemMapper.selectByTaskId(timingTask.getId());
+            if (taskItems.isEmpty()) {
+                throw new JobExecutionException("淇濆吇浠诲姟鏈厤缃繚鍏婚」鐩�: " + taskId);
+            }
+            // 鎸変换鍔¢」鐩揩鐓х敓鎴愭湰娆′繚鍏昏褰曞強椤圭洰娓呭崟銆�
             DeviceMaintenance deviceMaintenance = createInspectionTask(timingTask);
             deviceMaintenanceService.save(deviceMaintenance);
+            for (MaintenanceTaskItem taskItem : taskItems) {
+                DeviceMaintenanceItem recordItem = new DeviceMaintenanceItem();
+                recordItem.setDeviceMaintenanceId(deviceMaintenance.getId());
+                recordItem.setMaintenanceTaskItemId(taskItem.getId());
+                recordItem.setMaintenanceItemId(taskItem.getMaintenanceItemId());
+                recordItem.setItemProject(taskItem.getItemProject());
+                recordItem.setItemName(taskItem.getItemName());
+                recordItem.setItemStatus(0);
+                recordItem.setSortNum(taskItem.getSortNum());
+                recordItem.setTenantId(deviceMaintenance.getTenantId());
+                recordItem.setDeptId(timingTask.getDeptId());
+                deviceMaintenanceItemMapper.insert(recordItem);
+            }
 
             // 3. 鏇存柊瀹氭椂浠诲姟鐨勬墽琛屾椂闂�
             if (!tasks.isEmpty()) {
@@ -230,14 +258,29 @@
 
         for (String dayStr : dayStrs) {
             switch (dayStr) {
-                case "MON": days.add(DayOfWeek.MONDAY); break;
-                case "TUE": days.add(DayOfWeek.TUESDAY); break;
-                case "WED": days.add(DayOfWeek.WEDNESDAY); break;
-                case "THU": days.add(DayOfWeek.THURSDAY); break;
-                case "FRI": days.add(DayOfWeek.FRIDAY); break;
-                case "SAT": days.add(DayOfWeek.SATURDAY); break;
-                case "SUN": days.add(DayOfWeek.SUNDAY); break;
-                default: throw new IllegalArgumentException("鏃犳晥鐨勬槦鏈熷嚑: " + dayStr);
+                case "MON":
+                    days.add(DayOfWeek.MONDAY);
+                    break;
+                case "TUE":
+                    days.add(DayOfWeek.TUESDAY);
+                    break;
+                case "WED":
+                    days.add(DayOfWeek.WEDNESDAY);
+                    break;
+                case "THU":
+                    days.add(DayOfWeek.THURSDAY);
+                    break;
+                case "FRI":
+                    days.add(DayOfWeek.FRIDAY);
+                    break;
+                case "SAT":
+                    days.add(DayOfWeek.SATURDAY);
+                    break;
+                case "SUN":
+                    days.add(DayOfWeek.SUNDAY);
+                    break;
+                default:
+                    throw new IllegalArgumentException("鏃犳晥鐨勬槦鏈熷嚑: " + dayStr);
             }
         }
 
diff --git a/src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskServiceImpl.java b/src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskServiceImpl.java
index 0d2b421..5808c73 100644
--- a/src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskServiceImpl.java
+++ b/src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskServiceImpl.java
@@ -5,7 +5,11 @@
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.ruoyi.common.utils.bean.BeanUtils;
 import com.ruoyi.device.mapper.MaintenanceTaskMapper;
+import com.ruoyi.device.mapper.MaintenanceItemMapper;
+import com.ruoyi.device.mapper.MaintenanceTaskItemMapper;
+import com.ruoyi.device.pojo.MaintenanceItem;
 import com.ruoyi.device.pojo.MaintenanceTask;
+import com.ruoyi.device.pojo.MaintenanceTaskItem;
 import com.ruoyi.device.service.MaintenanceTaskService;
 import com.ruoyi.framework.web.domain.AjaxResult;
 import com.ruoyi.inspectiontask.pojo.TimingTask;
@@ -15,9 +19,18 @@
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 import java.time.LocalDateTime;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * @author :yys
@@ -29,13 +42,15 @@
 public class MaintenanceTaskServiceImpl extends ServiceImpl<MaintenanceTaskMapper, MaintenanceTask> implements MaintenanceTaskService {
 
     private final MaintenanceTaskMapper maintenanceTaskMapper;
+    private final MaintenanceItemMapper maintenanceItemMapper;
+    private final MaintenanceTaskItemMapper maintenanceTaskItemMapper;
     private final SysUserMapper sysUserMapper;
     private final TimingTaskServiceImpl timingTaskService;
     private final MaintenanceTaskScheduler maintenanceTaskScheduler;
 
     @Override
     public AjaxResult listPage(Page page, MaintenanceTask maintenanceTask) {
-       LambdaQueryWrapper<MaintenanceTask> queryWrapper = new LambdaQueryWrapper<>();
+        LambdaQueryWrapper<MaintenanceTask> queryWrapper = new LambdaQueryWrapper<>();
         queryWrapper.orderByDesc(MaintenanceTask::getId);
         Page<MaintenanceTask> taskPage = maintenanceTaskMapper.selectPage(page, queryWrapper);
         // 2. 濡傛灉娌℃湁鏁版嵁锛岀洿鎺ヨ繑鍥炵┖鍒嗛〉
@@ -65,11 +80,17 @@
                 task.setRegistrant(userNickNameMap.getOrDefault(task.getRegistrantId(), "鏈煡鐢ㄦ埛"));
             }
         });
+        taskPage.getRecords().forEach(this::fillTaskItems);
         return AjaxResult.success(taskPage);
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public AjaxResult add(MaintenanceTask maintenanceTask) {
+        List<MaintenanceItem> selectedItems = getSelectedItems(maintenanceTask.getMaintenanceItemIds(), null, maintenanceTask.getTaskId(), maintenanceTask.getFrequencyType());
+        List<MaintenanceTaskItem> customItems = normalizeCustomItems(maintenanceTask.getCustomItems());
+        if (selectedItems == null || selectedItems.size() + customItems.size() == 0)
+            return AjaxResult.error("璇烽�夋嫨鑷冲皯涓�涓湁鏁堢殑淇濆吇椤圭洰");
         // 璁$畻棣栨鎵ц鏃堕棿
         TimingTask task = new TimingTask();
         task.setFrequencyType(maintenanceTask.getFrequencyType());
@@ -78,6 +99,7 @@
         maintenanceTask.setNextExecutionTime(firstExecutionTime);
         int insert = maintenanceTaskMapper.insert(maintenanceTask);
         if (insert > 0) {
+            saveTaskItems(maintenanceTask, selectedItems, customItems);
             // 鍙湁褰� isActive 涓� 1 鏃舵墠娣诲姞鍒板畾鏃朵换鍔¤皟搴﹀櫒
             if (maintenanceTask.getIsActive() != null && maintenanceTask.getIsActive() == 1) {
                 maintenanceTaskScheduler.scheduleMaintenanceTask(maintenanceTask);
@@ -87,11 +109,16 @@
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public AjaxResult updateByMaintenanceTaskId(MaintenanceTask maintenanceTask) {
         MaintenanceTask maintenanceTask1 = maintenanceTaskMapper.selectById(maintenanceTask.getId());
         if (maintenanceTask1 == null) {
             return AjaxResult.warn("娌℃湁姝ゆ暟鎹�");
         }
+        List<MaintenanceItem> selectedItems = getSelectedItems(maintenanceTask.getMaintenanceItemIds(), maintenanceTask1.getId(), maintenanceTask.getTaskId(), maintenanceTask.getFrequencyType());
+        List<MaintenanceTaskItem> customItems = normalizeCustomItems(maintenanceTask.getCustomItems());
+        if (selectedItems == null || selectedItems.size() + customItems.size() == 0)
+            return AjaxResult.error("璇烽�夋嫨鑷冲皯涓�涓湁鏁堢殑淇濆吇椤圭洰");
 
         // 淇濆瓨鏃х殑 isActive 鐘舵��
         Integer oldIsActive = maintenanceTask1.getIsActive();
@@ -100,6 +127,9 @@
         BeanUtils.copyProperties(maintenanceTask, maintenanceTask1);
         int update = maintenanceTaskMapper.updateById(maintenanceTask1);
         if (update > 0) {
+            maintenanceTaskItemMapper.delete(new LambdaQueryWrapper<MaintenanceTaskItem>()
+                    .eq(MaintenanceTaskItem::getMaintenanceTaskId, maintenanceTask1.getId()));
+            saveTaskItems(maintenanceTask1, selectedItems, customItems);
             // 澶勭悊 isActive 鐘舵�佸彉鍖�
             if (newIsActive != null && newIsActive == 1) {
                 // 鏂扮姸鎬佷负鍚敤锛氭坊鍔犲埌瀹氭椂浠诲姟璋冨害鍣�
@@ -134,8 +164,88 @@
         // 鍐嶄粠鏁版嵁搴撲腑鍒犻櫎璁板綍
         int delete = maintenanceTaskMapper.deleteBatchIds(ids);
         if (delete > 0) {
+            maintenanceTaskItemMapper.delete(new LambdaQueryWrapper<MaintenanceTaskItem>()
+                    .in(MaintenanceTaskItem::getMaintenanceTaskId, ids));
+        }
+        if (delete > 0) {
             return AjaxResult.success("鍒犻櫎鎴愬姛");
         }
         return AjaxResult.error("鍒犻櫎澶辫触");
     }
+
+    private List<MaintenanceTaskItem> normalizeCustomItems(List<MaintenanceTaskItem> customItems) {
+        if (customItems == null) return new ArrayList<>();
+        return customItems.stream().filter(Objects::nonNull)
+                .filter(item -> item.getItemProject() != null && !item.getItemProject().trim().isEmpty()
+                        && item.getItemName() != null && !item.getItemName().trim().isEmpty())
+                .peek(item -> {
+                    item.setItemProject(item.getItemProject().trim());
+                    item.setItemName(item.getItemName().trim());
+                })
+                .toList();
+    }
+
+    private List<MaintenanceItem> getSelectedItems(List<Long> ids, Long taskId, Long deviceId, String frequencyType) {
+        if (deviceId == null || !List.of("DAILY", "WEEKLY", "MONTHLY", "QUARTERLY").contains(frequencyType)
+                || (ids != null && new HashSet<>(ids).size() != ids.size())) return null;
+        if (ids == null || ids.isEmpty()) return new ArrayList<>();
+        List<MaintenanceItem> items = maintenanceItemMapper.selectList(new LambdaQueryWrapper<MaintenanceItem>()
+                .in(MaintenanceItem::getId, ids));
+        if (items.size() != ids.size()) return null;
+        if (items.stream().anyMatch(item -> (item.getDeviceLedgerId() != null && !deviceId.equals(item.getDeviceLedgerId()))
+                || !(frequencyType.equals(item.getFrequencyType()) || "ALL".equals(item.getFrequencyType()))))
+            return null;
+        Map<Long, MaintenanceItem> itemMap = new HashMap<>();
+        items.forEach(item -> itemMap.put(item.getId(), item));
+        Set<Long> existingIds = taskId == null ? Collections.emptySet() : maintenanceTaskItemMapper.selectList(
+                        new LambdaQueryWrapper<MaintenanceTaskItem>()
+                                .eq(MaintenanceTaskItem::getMaintenanceTaskId, taskId))
+                .stream().map(MaintenanceTaskItem::getMaintenanceItemId).collect(Collectors.toSet());
+        if (items.stream().anyMatch(item -> (item.getDeleted() != null && item.getDeleted() == 1
+                || item.getIsActive() == null || item.getIsActive() != 1)
+                && !existingIds.contains(item.getId()))) return null;
+        List<MaintenanceItem> sorted = new ArrayList<>();
+        ids.forEach(id -> sorted.add(itemMap.get(id)));
+        return sorted;
+    }
+
+    private void saveTaskItems(MaintenanceTask task, List<MaintenanceItem> selectedItems, List<MaintenanceTaskItem> customItems) {
+        for (int i = 0; i < selectedItems.size(); i++) {
+            MaintenanceItem source = selectedItems.get(i);
+            MaintenanceTaskItem link = new MaintenanceTaskItem();
+            link.setMaintenanceTaskId(task.getId());
+            link.setMaintenanceItemId(source.getId());
+            link.setItemProject(source.getItemProject());
+            link.setItemName(source.getItemName());
+            link.setSortNum(i);
+            link.setTenantId(task.getTenantId());
+            link.setDeptId(task.getDeptId());
+            maintenanceTaskItemMapper.insert(link);
+        }
+        for (int i = 0; i < customItems.size(); i++) {
+            MaintenanceTaskItem link = new MaintenanceTaskItem();
+            link.setMaintenanceTaskId(task.getId());
+            link.setMaintenanceItemId(null);
+            link.setItemProject(customItems.get(i).getItemProject());
+            link.setItemName(customItems.get(i).getItemName());
+            link.setSortNum(selectedItems.size() + i);
+            link.setTenantId(task.getTenantId());
+            link.setDeptId(task.getDeptId());
+            maintenanceTaskItemMapper.insert(link);
+        }
+        task.setMaintenanceItems(maintenanceTaskItemMapper.selectList(new LambdaQueryWrapper<MaintenanceTaskItem>()
+                .eq(MaintenanceTaskItem::getMaintenanceTaskId, task.getId())
+                .orderByAsc(MaintenanceTaskItem::getSortNum)));
+        task.setMaintenanceItemIds(task.getMaintenanceItems().stream()
+                .map(MaintenanceTaskItem::getMaintenanceItemId).filter(Objects::nonNull).collect(Collectors.toList()));
+    }
+
+    private void fillTaskItems(MaintenanceTask task) {
+        List<MaintenanceTaskItem> items = maintenanceTaskItemMapper.selectList(new LambdaQueryWrapper<MaintenanceTaskItem>()
+                .eq(MaintenanceTaskItem::getMaintenanceTaskId, task.getId())
+                .orderByAsc(MaintenanceTaskItem::getSortNum));
+        task.setMaintenanceItems(items);
+        task.setMaintenanceItemIds(items.stream().map(MaintenanceTaskItem::getMaintenanceItemId).filter(Objects::nonNull)
+                .collect(Collectors.toList()));
+    }
 }

--
Gitblit v1.9.3