liding
3 天以前 49e1bc66ebaf696ebd3fc3ed33d65c8795fd3cde
1.巡检定时任务 2.设备管理 3.文档查询
已修改17个文件
已添加18个文件
1600 ■■■■■ 文件已修改
main-business/pom.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/controller/ArchiveController.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/controller/EquipmentManagementController.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/controller/InputInventoryRecordController.java 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/controller/InspectionTaskController.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/controller/TimingTaskController.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/dto/ArchiveDto.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/dto/EquipmentManagementDto.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/dto/TimingTaskDto.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/entity/EquipmentManagement.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/entity/InspectionTask.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/entity/TimingTask.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/mapper/EquipmentManagementMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/mapper/TimingTaskMapper.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/mapper/TreeMapper.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/service/ArchiveService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/service/EquipmentManagementService.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/service/InspectionTaskService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/service/TimingTaskService.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/service/impl/ArchiveServiceImpl.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/service/impl/EquipmentManagementServiceImpl.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/service/impl/InspectionTaskServiceImpl.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/service/impl/ProductionMasterServiceImpl.java 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/service/impl/PurchaseRegistrationServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/service/impl/TimingTaskScheduler.java 238 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/service/impl/TimingTaskServiceImpl.java 405 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/java/com/ruoyi/business/task/TimingTaskJob.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/resources/db/migration/postgresql/V20250614134700__create_table_inspection_task.sql 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/resources/db/migration/postgresql/V20250630153900__create_table_timing_task.sql 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/resources/db/migration/postgresql/V20250701142700__create_table_equipment_management.sql 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/resources/mapper/EquipmentManagementMapper.xml 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/resources/mapper/TimingTaskMapper.xml 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/src/main/resources/mapper/TreeMapper.xml 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
main-business/pom.xml
@@ -44,6 +44,10 @@
            <groupId>com.ruoyi</groupId>
            <artifactId>basic-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
        </dependency>
    </dependencies>
    <properties>
main-business/src/main/java/com/ruoyi/business/controller/ArchiveController.java
@@ -3,6 +3,7 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.business.dto.ArchiveDto;
import com.ruoyi.business.entity.Archive;
import com.ruoyi.business.service.ArchiveService;
import com.ruoyi.common.core.domain.R;
import lombok.AllArgsConstructor;
@@ -28,7 +29,7 @@
     * æŸ¥è¯¢æ¡£æ¡ˆä¿¡æ¯è¡¨
     */
    @GetMapping("/list")
    public R<IPage<ArchiveDto>> treeList(Page page, ArchiveDto archiveDto) {
    public R<IPage<ArchiveDto>> treeList(Page<Archive> page, ArchiveDto archiveDto) {
        IPage<ArchiveDto> list = archiveService.selectArchiveList(page, archiveDto);
        return R.ok(list);
    }
main-business/src/main/java/com/ruoyi/business/controller/EquipmentManagementController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,56 @@
package com.ruoyi.business.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.business.dto.EquipmentManagementDto;
import com.ruoyi.business.dto.ProductionDto;
import com.ruoyi.business.entity.EquipmentManagement;
import com.ruoyi.business.entity.Production;
import com.ruoyi.business.service.EquipmentManagementService;
import com.ruoyi.common.core.domain.R;
import org.springframework.web.bind.annotation.*;
import lombok.AllArgsConstructor;
/**
 * <p>
 * è®¾å¤‡ç®¡ç†è¡¨  å‰ç«¯æŽ§åˆ¶å™¨
 * </p>
 *
 * @author ld
 * @since 2025-07-01
 */
@RestController
@AllArgsConstructor
@RequestMapping("/equipmentManagement")
public class EquipmentManagementController {
    private EquipmentManagementService equipmentManagementService;
    /**
     * è®¾å¤‡ç®¡ç†è¡¨æŸ¥è¯¢
     */
    @GetMapping("/list")
    public R<IPage<EquipmentManagementDto>> list(Page<EquipmentManagement> page, EquipmentManagementDto equipmentManagementDto) {
        IPage<EquipmentManagementDto> list = equipmentManagementService.selectProductionList(page, equipmentManagementDto);
        return R.ok(list);
    }
    /**
     * è®¾å¤‡ç®¡ç†è¡¨æ–°å¢žä¿®æ”¹
     */
    @PostMapping("/addOrEditEquipment")
    public R addOrEditEquipment(@RequestBody EquipmentManagementDto equipmentManagementDto) {
        return R.ok(equipmentManagementService.addOrEditEquipment(equipmentManagementDto));
    }
    /**
     * è®¾å¤‡ç®¡ç†è¡¨åˆ é™¤
     */
    @DeleteMapping("/delEquipment")
    public R remove(@RequestBody Long[] ids) {
        return R.ok(equipmentManagementService.delByIds(ids));
    }
}
main-business/src/main/java/com/ruoyi/business/controller/InputInventoryRecordController.java
main-business/src/main/java/com/ruoyi/business/controller/InspectionTaskController.java
@@ -3,6 +3,7 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.business.dto.InspectionTaskDto;
import com.ruoyi.business.entity.InspectionTask;
import com.ruoyi.business.service.InspectionTaskService;
import com.ruoyi.common.core.domain.R;
import lombok.AllArgsConstructor;
@@ -27,7 +28,7 @@
     * å·¡æ£€ä»»åŠ¡è¡¨è¡¨æŸ¥è¯¢
     */
    @GetMapping("/list")
    public R<IPage<InspectionTaskDto>> list(Page page, InspectionTaskDto inspectionTaskDto) {
    public R<IPage<InspectionTaskDto>> list(Page<InspectionTask> page, InspectionTaskDto inspectionTaskDto) {
        IPage<InspectionTaskDto> list = inspectionTaskService.selectInspectionTaskList(page,inspectionTaskDto);
        return R.ok(list);
    }
main-business/src/main/java/com/ruoyi/business/controller/TimingTaskController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,56 @@
package com.ruoyi.business.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.business.dto.SalesRecordDto;
import com.ruoyi.business.dto.TimingTaskDto;
import com.ruoyi.business.entity.SalesRecord;
import com.ruoyi.business.entity.TimingTask;
import com.ruoyi.business.service.TimingTaskService;
import com.ruoyi.common.core.domain.R;
import org.quartz.SchedulerException;
import org.springframework.web.bind.annotation.*;
import lombok.AllArgsConstructor;
/**
 * <p>
 * å®šæ—¶å·¡æ£€ä»»åŠ¡è¡¨ å‰ç«¯æŽ§åˆ¶å™¨
 * </p>
 *
 * @author ld
 * @since 2025-06-30
 */
@RestController
@AllArgsConstructor
@RequestMapping("/timingTask")
public class TimingTaskController {
    private TimingTaskService timingTaskService;
    /**
     * å®šæ—¶å·¡æ£€ä»»åŠ¡è¡¨æŸ¥è¯¢
     */
    @GetMapping("/list")
    public R<IPage<TimingTaskDto>> list(Page<TimingTask> page, TimingTask timingTask) {
        IPage<TimingTaskDto> list = timingTaskService.selectTimingTaskList(page,timingTask);
        return R.ok(list);
    }
    /**
     * å®šæ—¶å·¡æ£€ä»»åŠ¡è¡¨æ–°å¢žä¿®æ”¹
     */
    @PostMapping("/addOrEditTimingTask")
    public R addOrEditTimingTask(@RequestBody TimingTaskDto timingTaskDto) throws SchedulerException {
        return R.ok(timingTaskService.addOrEditTimingTask(timingTaskDto));
    }
    /**
     * å®šæ—¶å·¡æ£€ä»»åŠ¡è¡¨åˆ é™¤
     */
    @DeleteMapping("/delTimingTask")
    public R remove(@RequestBody Long[] ids) {
        return R.ok(timingTaskService.delByIds(ids));
    }
}
main-business/src/main/java/com/ruoyi/business/dto/ArchiveDto.java
@@ -15,4 +15,6 @@
    private List<StorageBlobDTO> storageBlobDTO;
    private List<StorageBlob> attachments;
    private String searchAll;
}
main-business/src/main/java/com/ruoyi/business/dto/EquipmentManagementDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,8 @@
package com.ruoyi.business.dto;
import com.ruoyi.business.entity.EquipmentManagement;
import lombok.Data;
@Data
public class EquipmentManagementDto extends EquipmentManagement {
}
main-business/src/main/java/com/ruoyi/business/dto/TimingTaskDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,10 @@
package com.ruoyi.business.dto;
import com.ruoyi.business.entity.TimingTask;
import lombok.Data;
@Data
public class TimingTaskDto extends TimingTask {
    private String inspector;
}
main-business/src/main/java/com/ruoyi/business/entity/EquipmentManagement.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,79 @@
package com.ruoyi.business.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import com.ruoyi.common.core.domain.MyBaseEntity;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
 * è®¾å¤‡ç®¡ç†è¡¨  å®žä½“ç±»
 *
 * @author ld
 * @date 2025-07-01
 */
@Data
@TableName("equipment_management")
public class EquipmentManagement extends MyBaseEntity {
    private static final long serialVersionUID = 1L;
    /**
     * ä¸»é”® ID
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    /**
     * è®¾å¤‡ç¼–号
     */
    @TableField(value = "equipment_id")
    private String equipmentId;
    /**
     * è®¾å¤‡åç§°
     */
    @TableField(value = "equipment_name")
    private String equipmentName;
    /**
     * æ•°é‡
     */
    @TableField(value = "quantity")
    private Integer quantity;
    /**
     * è§„格型号
     */
    @TableField(value = "specification")
    private String specification;
    /**
     * ä½¿ç”¨çŠ¶æ€
     */
    @TableField(value = "usage_status")
    private String usageStatus;
    /**
     * ä½¿ç”¨éƒ¨é—¨
     */
    @TableField(value = "using_department")
    private String usingDepartment;
    /**
     * ä½¿ç”¨äººID
     */
    @TableField(value = "user_id")
    private Long userId;
    /**
     * å­˜æ”¾ä½ç½®
     */
    @TableField(value = "storage_location")
    private String storageLocation;
    /**
     * é‡‡è´­æ—¥æœŸ
     */
    @TableField(value = "purchase_date")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate purchaseDate;
    /**
     * é‡‡è´­ä»·æ ¼
     */
    @TableField(value = "purchase_price")
    private BigDecimal purchasePrice;
}
main-business/src/main/java/com/ruoyi/business/entity/InspectionTask.java
@@ -30,7 +30,7 @@
     *
     */
    @TableField(value = "inspector_id")
    private Long inspectorId;
    private String inspectorId;
    /**
     * æ‰§è¡Œå·¡æ£€çš„人员姓名
     */
@@ -39,8 +39,8 @@
    /**
     * å·¡æ£€åœ°ç‚¹è¯¦ç»†æè¿°
     */
    @TableField(value = "port")
    private String port;
    @TableField(value = "inspection_location")
    private String inspectionLocation;
    /**
     * ä»»åŠ¡é™„åŠ è¯´æ˜Žæˆ–ç‰¹æ®Šæƒ…å†µè®°å½•
     */
@@ -56,4 +56,9 @@
     */
    @TableField(value = "registrant")
    private String registrant;
    /**
     * é¢‘次
     */
    @TableField(value = "frequency_type")
    private String frequencyType;
}
main-business/src/main/java/com/ruoyi/business/entity/TimingTask.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,96 @@
package com.ruoyi.business.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import com.ruoyi.common.core.domain.MyBaseEntity;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
 * å®šæ—¶å·¡æ£€ä»»åŠ¡è¡¨ å®žä½“ç±»
 *
 * @author ld
 * @date 2025-06-30
 */
@Data
@TableName("timing_task")
public class TimingTask extends MyBaseEntity {
    private static final long serialVersionUID = 1L;
    /**
     * ä¸»é”®ID
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    /**
     * ä»»åŠ¡åç§°
     */
    @TableField(value = "task_name")
    private String taskName;
    /**
     * å·¡æ£€äºº
     */
    @TableField(value = "inspector_ids")
    private String inspectorIds;
    /**
     * å·¡æ£€åœ°ç‚¹
     */
    @TableField(value = "inspection_location")
    private String inspectionLocation;
    /**
     * é¢‘次
     */
    @TableField(value = "frequency_type")
    private String frequencyType;
    /**
     * å…·ä½“æ—¶é—´
     */
    @TableField(value = "frequency_detail")
    private String frequencyDetail;
    /**
     * ä¸‹æ¬¡æ‰§è¡Œæ—¶é—´
     */
    @TableField(value = "next_execution_time")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime nexExecutionTime;
    /**
     * æœ€åŽæ‰§è¡Œæ—¶é—´
     */
    @TableField(value = "last_execution_time")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime lastExecuteTime;
    /**
     * æ˜¯å¦æ¿€æ´»
     */
    @TableField(value = "is_active")
    private boolean isActive;
    /**
     * å¤‡æ³¨
     */
    @TableField(value = "remarks")
    private String remarks;
    /**
     * ç™»è®°äººid
     */
    @TableField(value = "registrant_id")
    private Long registrantId;
    /**
     * ç™»è®°äºº
     */
    @TableField(value = "registrant")
    private String registrant;
    /**
     * ç™»è®°æ—¥æœŸ
     */
    @TableField(value = "registration_date")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate registrationDate;
    /**
     * ä»»åŠ¡çŠ¶æ€
     */
    @TableField(value = "status")
    private String status; // ACTIVE, PAUSED, COMPLETED等
}
main-business/src/main/java/com/ruoyi/business/mapper/EquipmentManagementMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package com.ruoyi.business.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.business.entity.EquipmentManagement;
import org.apache.ibatis.annotations.Mapper;
/**
 * <p>
 *  è®¾å¤‡ç®¡ç†è¡¨  Mapper æŽ¥å£
 * </p>
 *
 * @author ld
 * @since 2025-07-01
 */
@Mapper
public interface EquipmentManagementMapper extends BaseMapper<EquipmentManagement> {
}
main-business/src/main/java/com/ruoyi/business/mapper/TimingTaskMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package com.ruoyi.business.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.business.entity.TimingTask;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.time.LocalDateTime;
import java.util.List;
/**
 * <p>
 * å®šæ—¶å·¡æ£€ä»»åŠ¡è¡¨ Mapper æŽ¥å£
 * </p>
 *
 * @author ld
 * @since 2025-06-30
 */
@Mapper
public interface TimingTaskMapper extends BaseMapper<TimingTask> {
    @Select("SELECT * FROM timing_task WHERE next_execution_time <= #{currentTime}")
    List<TimingTask> selectActiveTasks(@Param("currentTime") LocalDateTime currentTime);}
main-business/src/main/java/com/ruoyi/business/mapper/TreeMapper.java
@@ -4,6 +4,8 @@
import com.ruoyi.business.entity.Tree;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
 * <p>
 * æ¡£æ¡ˆä¿¡æ¯è¡¨ï¼Œè®°å½•系统中各类档案的基本信息 Mapper æŽ¥å£
@@ -15,4 +17,5 @@
@Mapper
public interface TreeMapper extends BaseMapper<Tree> {
    List<Long> listRecursiveSubNodeIds(Long treeId);
}
main-business/src/main/java/com/ruoyi/business/service/ArchiveService.java
@@ -19,7 +19,7 @@
 */
public interface ArchiveService extends IService<Archive> {
    IPage<ArchiveDto> selectArchiveList(Page page, ArchiveDto archiveDto);
    IPage<ArchiveDto> selectArchiveList(Page<Archive> page, ArchiveDto archiveDto);
    int addOrEditArchive(ArchiveDto archiveDto);
main-business/src/main/java/com/ruoyi/business/service/EquipmentManagementService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package com.ruoyi.business.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.business.dto.EquipmentManagementDto;
import com.ruoyi.business.entity.EquipmentManagement;
import com.baomidou.mybatisplus.extension.service.IService;
/**
 * <p>
 *  è®¾å¤‡ç®¡ç†è¡¨  æœåŠ¡ç±»
 * </p>
 *
 * @author ld
 * @since 2025-07-01
 */
public interface EquipmentManagementService extends IService<EquipmentManagement> {
    IPage<EquipmentManagementDto> selectProductionList(Page<EquipmentManagement> page, EquipmentManagementDto equipmentManagementDto);
    int addOrEditEquipment(EquipmentManagementDto equipmentManagementDto);
    int delByIds(Long[] ids);
}
main-business/src/main/java/com/ruoyi/business/service/InspectionTaskService.java
@@ -16,7 +16,7 @@
 */
public interface InspectionTaskService extends IService<InspectionTask> {
    IPage<InspectionTaskDto> selectInspectionTaskList(Page page, InspectionTaskDto inspectionTaskDto);
    IPage<InspectionTaskDto> selectInspectionTaskList(Page<InspectionTask> page, InspectionTaskDto inspectionTaskDto);
    int addOrEditInspectionTask(InspectionTaskDto inspectionTaskDto);
main-business/src/main/java/com/ruoyi/business/service/TimingTaskService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package com.ruoyi.business.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.business.dto.TimingTaskDto;
import com.ruoyi.business.entity.TimingTask;
import com.baomidou.mybatisplus.extension.service.IService;
import org.quartz.SchedulerException;
/**
 * <p>
 * å®šæ—¶å·¡æ£€ä»»åŠ¡è¡¨ æœåŠ¡ç±»
 * </p>
 *
 * @author ld
 * @since 2025-06-30
 */
public interface TimingTaskService extends IService<TimingTask> {
    IPage<TimingTaskDto> selectTimingTaskList(Page<TimingTask> page, TimingTask timingTask);
    int addOrEditTimingTask(TimingTaskDto timingTaskDto) throws SchedulerException;
    int delByIds(Long[] ids);
    void updateTaskExecutionTime(Long taskId);
}
main-business/src/main/java/com/ruoyi/business/service/impl/ArchiveServiceImpl.java
@@ -14,7 +14,9 @@
import com.ruoyi.basic.service.StorageAttachmentService;
import com.ruoyi.business.dto.ArchiveDto;
import com.ruoyi.business.entity.Archive;
import com.ruoyi.business.entity.Tree;
import com.ruoyi.business.mapper.ArchiveMapper;
import com.ruoyi.business.mapper.TreeMapper;
import com.ruoyi.business.service.ArchiveService;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.file.MinioUtils;
@@ -43,6 +45,8 @@
    private final ArchiveMapper archiveMapper;
    private final TreeMapper treeMapper;
    private final StorageAttachmentService storageAttachmentService;
    private final StorageBlobMapper storageBlobMapper;
@@ -53,12 +57,30 @@
    @Override
    public IPage<ArchiveDto> selectArchiveList(Page page, ArchiveDto archiveDto) {
    public IPage<ArchiveDto> selectArchiveList(Page<Archive> page, ArchiveDto archiveDto) {
        // 1. åˆ†é¡µæŸ¥è¯¢ä¸»æ•°æ®
        LambdaQueryWrapper<Archive> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.orderByDesc(Archive::getCreateTime);
        if (archiveDto.getTreeId() != null) {
            queryWrapper.eq(Archive::getTreeId, archiveDto.getTreeId());
            Long treeId = archiveDto.getTreeId();
            // åˆ¤æ–­æ˜¯å¦ä¸ºä¸»ID
            Tree tree = treeMapper.selectById(archiveDto.getTreeId());
            boolean isMainId = tree != null && tree.getParentId() == null;
            if (isMainId) {
                // èŽ·å–ä¸»ID的所有递归子节点ID
                List<Long> recursiveSubIds = treeMapper.listRecursiveSubNodeIds(treeId);
                // å°†ä¸»ID本身和所有子节点ID加入查询条件
                recursiveSubIds.add(treeId);
                queryWrapper.in(Archive::getTreeId, recursiveSubIds);
            } else {
                // éžä¸»ID,直接按原条件查询
                queryWrapper.eq(Archive::getTreeId, treeId);
            }
        }
        if (archiveDto.getSearchAll() != null) {
            queryWrapper.like(Archive::getName, archiveDto.getSearchAll());
        }
        IPage<Archive> archivePage = archiveMapper.selectPage(page, queryWrapper);
main-business/src/main/java/com/ruoyi/business/service/impl/EquipmentManagementServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,57 @@
package com.ruoyi.business.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.ruoyi.business.dto.EquipmentManagementDto;
import com.ruoyi.business.dto.ProductionMasterDto;
import com.ruoyi.business.entity.EquipmentManagement;
import com.ruoyi.business.entity.Production;
import com.ruoyi.business.mapper.EquipmentManagementMapper;
import com.ruoyi.business.service.EquipmentManagementService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.utils.bean.BeanUtils;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import java.util.Arrays;
import java.util.Objects;
/**
 * <p>
 * è®¾å¤‡ç®¡ç†è¡¨  æœåŠ¡å®žçŽ°ç±»
 * </p>
 *
 * @author ld
 * @since 2025-07-01
 */
@Service
@RequiredArgsConstructor
public class EquipmentManagementServiceImpl extends ServiceImpl<EquipmentManagementMapper, EquipmentManagement> implements EquipmentManagementService {
    private final EquipmentManagementMapper equipmentManagementMapper;
    public IPage<EquipmentManagementDto> selectProductionList(Page<EquipmentManagement> page, EquipmentManagementDto equipmentManagementDto) {
        Page<EquipmentManagement> entityPage = equipmentManagementMapper.selectPage(page, null);
        IPage<EquipmentManagementDto> dtoPage = new Page<>();
        BeanUtils.copyProperties(entityPage, dtoPage);
        return dtoPage;
    }
    @Override
    public int addOrEditEquipment(EquipmentManagementDto equipmentManagementDto) {
        EquipmentManagement equipmentManagement = new EquipmentManagement();
        BeanUtils.copyProperties(equipmentManagementDto, equipmentManagement);
        if (Objects.isNull(equipmentManagementDto.getId())) {
            return equipmentManagementMapper.insert(equipmentManagement);
        } else {
            return equipmentManagementMapper.updateById(equipmentManagement);
        }
    }
    @Override
    public int delByIds(Long[] ids) {
        return equipmentManagementMapper.deleteByIds(Arrays.asList(ids));
    }
}
main-business/src/main/java/com/ruoyi/business/service/impl/InspectionTaskServiceImpl.java
@@ -7,6 +7,7 @@
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.basic.entity.StorageAttachment;
import com.ruoyi.basic.entity.StorageBlob;
import com.ruoyi.basic.entity.Supply;
import com.ruoyi.basic.entity.dto.StorageBlobDTO;
import com.ruoyi.basic.mapper.StorageAttachmentMapper;
import com.ruoyi.basic.mapper.StorageBlobMapper;
@@ -15,9 +16,12 @@
import com.ruoyi.business.entity.InspectionTask;
import com.ruoyi.business.mapper.InspectionTaskMapper;
import com.ruoyi.business.service.InspectionTaskService;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.file.MinioUtils;
import com.ruoyi.system.mapper.SysUserMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@@ -50,8 +54,10 @@
    private final MinioUtils minioUtils;
    private final SysUserMapper sysUserMapper;
    @Override
    public IPage<InspectionTaskDto> selectInspectionTaskList(Page page, InspectionTaskDto inspectionTaskDto) {
    public IPage<InspectionTaskDto> selectInspectionTaskList(Page<InspectionTask> page, InspectionTaskDto inspectionTaskDto) {
        LambdaQueryWrapper<InspectionTask> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.orderByDesc(InspectionTask::getCreateTime);
        IPage<InspectionTask> entityPage = inspectionTaskMapper.selectPage(page, queryWrapper);
@@ -61,8 +67,46 @@
            return new Page<>(entityPage.getCurrent(), entityPage.getSize(), entityPage.getTotal());
        }
        // èŽ·å–id集合
        List<Long> ids = entityPage.getRecords().stream().map(InspectionTask::getId).collect(Collectors.toList());
        List<Long> ids = entityPage.getRecords().stream().map(InspectionTask::getId).toList();
        //登记人ids
        List<Long> registrantIds = entityPage.getRecords().stream().map(InspectionTask::getRegistrantId).toList();
        // æ‰¹é‡æŸ¥è¯¢ç™»è®°äºº
        Map<Long, SysUser> sysUserMap;
        if (!registrantIds.isEmpty()) {
            List<SysUser> sysUsers = sysUserMapper.selectList(registrantIds);
            sysUserMap = sysUsers.stream().collect(Collectors.toMap(SysUser::getUserId, Function.identity()));
        } else {
            sysUserMap = new HashMap<>();
        }
        //巡检人ids
        List<String> inspectorIds = entityPage.getRecords().stream().map(InspectionTask::getInspectorId).toList();
        //获取所有不重复的用户ID
        Set<Long> allUserIds = entityPage.getRecords().stream()
                .map(InspectionTask::getInspectorId) // èŽ·å–"2,3"这样的字符串
                .filter(StringUtils::isNotBlank)
                .flatMap(idsStr -> Arrays.stream(idsStr.split(",")))
                .map(idStr -> {
                    try {
                        return Long.parseLong(idStr.trim());
                    } catch (NumberFormatException e) {
                        return null;
                    }
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        // ä½¿ç”¨SQL批量查询用户信息
        Map<Long, String> userIdToNameMap = allUserIds.isEmpty()
                ? Collections.emptyMap()
                : sysUserMapper.selectUsersByIds(new ArrayList<>(allUserIds))
                .stream()
                .collect(Collectors.toMap(
                        SysUser::getUserId,
                        SysUser::getNickName,
                        (existing, replacement) -> existing));
        //处理附件
        Map<Long, List<StorageAttachment>> attachmentsMap = storageAttachmentMapper.selectList(new LambdaQueryWrapper<StorageAttachment>().in(StorageAttachment::getRecordId, ids)
                        .eq(StorageAttachment::getRecordType, InspectionTasks.ordinal()))
                .stream()
@@ -82,6 +126,27 @@
        List<InspectionTaskDto> dtoList = entityPage.getRecords().stream().map(inspectionTask -> {
            InspectionTaskDto dto = new InspectionTaskDto();
            BeanUtils.copyProperties(inspectionTask, dto);  // å¤åˆ¶ä¸»å¯¹è±¡å±žæ€§
            // è®¾ç½®ç™»è®°äºº
            SysUser sysUser = sysUserMap.get(inspectionTask.getRegistrantId());
            if (sysUser != null) {
                dto.setRegistrant(sysUser.getNickName());
            }
            // å¤„理巡检人名称
            if (StringUtils.isNotBlank(inspectionTask.getInspectorId())) {
                String inspectorNames = Arrays.stream(inspectionTask.getInspectorId().split(","))
                        .map(String::trim)
                        .map(idStr -> {
                            try {
                                Long userId = Long.parseLong(idStr);
                                return userIdToNameMap.getOrDefault(userId, "未知用户(" + idStr + ")");
                            } catch (NumberFormatException e) {
                                return "无效ID(" + idStr + ")";
                            }
                        })
                        .collect(Collectors.joining(","));
                dto.setInspector(inspectorNames);
            }
            // åˆå§‹åŒ–三个附件列表
            dto.setBeforeProduction(new ArrayList<>());
@@ -149,7 +214,6 @@
    @Override
    public int addOrEditInspectionTask(InspectionTaskDto inspectionTaskDto) {
        SecurityUtils.getLoginUser().getUserId();
        InspectionTask inspectionTask = new InspectionTask();
        BeanUtils.copyProperties(inspectionTaskDto, inspectionTask);
        inspectionTask.setRegistrantId(SecurityUtils.getLoginUser().getUserId());
main-business/src/main/java/com/ruoyi/business/service/impl/ProductionMasterServiceImpl.java
@@ -5,12 +5,14 @@
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.basic.entity.CoalInfo;
import com.ruoyi.basic.mapper.CoalInfoMapper;
import com.ruoyi.business.dto.ProductionMasterDto;
import com.ruoyi.business.entity.*;
import com.ruoyi.business.mapper.*;
import com.ruoyi.business.service.ProductionMasterService;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@@ -49,11 +51,32 @@
    public IPage<ProductionMasterDto> selectPMList(Page page, ProductionMasterDto productionMasterDto) {
        // 1. æž„建主表查询条件
        LambdaQueryWrapper<ProductionMaster> masterQueryWrapper = new LambdaQueryWrapper<>();
        String keyword = productionMasterDto.getSearchAll();
        if (StringUtils.isNotBlank(keyword)) {
            // æŸ¥è¯¢ç…¤ç§åç§°ä¸­æ¨¡ç³ŠåŒ¹é…çš„coalId列表
            List<Long> matchedCoalIds = coalInfoMapper.selectList(
                            new LambdaQueryWrapper<CoalInfo>().like(CoalInfo::getCoal, keyword)
                    ).stream()
                    .map(CoalInfo::getId)
                    .toList();
            // ç»„装查询条件:煤种ID在匹配的列表中
            // å¦‚æžœ matchedCoalIds ä¸ºç©ºï¼Œç›´æŽ¥è¿”回 0 æ¡æ•°æ®ï¼ˆæž„造一个不可能成立的条件)
            if (matchedCoalIds.isEmpty()) {
                masterQueryWrapper.apply("1 = 0"); // å¼ºåˆ¶è¿”回空结果
            }
            // å¦‚果有匹配的 coalId,则按 coalId æŸ¥è¯¢
            else {
                String ids = matchedCoalIds.stream()
                        .map(String::valueOf)
                        .collect(Collectors.joining(","));
                masterQueryWrapper.apply(
                        "{0} = ANY(string_to_array(coal_id, ','))",
                        ids
                );
            }
        }
        // 2. æ‰§è¡Œä¸»è¡¨åˆ†é¡µæŸ¥è¯¢
        IPage<ProductionMaster> entityPage = productionMasterMapper.selectPage(page, masterQueryWrapper);
main-business/src/main/java/com/ruoyi/business/service/impl/PurchaseRegistrationServiceImpl.java
@@ -62,7 +62,7 @@
                    .map(CoalInfo::getId)
                    .collect(Collectors.toList());
            // ç»„装查询条件:煤种ID在匹配的列表中 æˆ– ä¾›åº”商名称匹配
            // ç»„装查询条件:煤种ID在匹配的列表中
            queryWrapper.and(w -> {
                if (!matchedCoalIds.isEmpty()) {
                    w.in(PurchaseRegistration::getCoalId, matchedCoalIds).or();
main-business/src/main/java/com/ruoyi/business/service/impl/TimingTaskScheduler.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,238 @@
package com.ruoyi.business.service.impl;
import com.ruoyi.business.entity.TimingTask;
import com.ruoyi.business.task.TimingTaskJob;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.DayOfWeek;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeParseException;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
@Service
public class TimingTaskScheduler {
    @Autowired
    private Scheduler scheduler;
    /**
     * æ·»åŠ æ–°ä»»åŠ¡åˆ°è°ƒåº¦å™¨
     */
    public void scheduleTimingTask(TimingTask task) throws SchedulerException {
        JobDetail jobDetail = buildJobDetail(task);
        Trigger trigger = buildJobTrigger(task, jobDetail);
        scheduler.scheduleJob(jobDetail, trigger);
    }
    /**
     * æ›´æ–°å·²æœ‰ä»»åŠ¡
     */
    public void rescheduleTimingTask(TimingTask task) throws SchedulerException {
        TriggerKey triggerKey = new TriggerKey("trigger_" + task.getId());
        // èŽ·å–çŽ°æœ‰è§¦å‘å™¨å¹¶è½¬æ¢ä¸º CronTrigger
        Trigger oldTrigger = scheduler.getTrigger(triggerKey);
        if (!(oldTrigger instanceof CronTrigger)) {
            throw new SchedulerException("Existing trigger is not a CronTrigger");
        }
        // æž„建新触发器
        Trigger newTrigger = TriggerBuilder.newTrigger()
                .withIdentity(triggerKey)
                .withDescription(task.getTaskName())
                .withSchedule(CronScheduleBuilder.cronSchedule(convertToCronExpression(task)))
                .startAt(Date.from(task.getNexExecutionTime().atZone(ZoneId.systemDefault()).toInstant()))
                .forJob(oldTrigger.getJobKey())
                .build();
        scheduler.rescheduleJob(triggerKey, newTrigger);
    }
    /**
     * æš‚停任务
     */
    public void pauseTimingTask(Long taskId) throws SchedulerException {
        JobKey jobKey = new JobKey("timingTask_" + taskId);
        scheduler.pauseJob(jobKey);
    }
    /**
     * æ¢å¤ä»»åŠ¡
     */
    public void resumeTimingTask(Long taskId) throws SchedulerException {
        JobKey jobKey = new JobKey("timingTask_" + taskId);
        scheduler.resumeJob(jobKey);
    }
    /**
     * åˆ é™¤ä»»åŠ¡
     */
    public void unscheduleTimingTask(Long taskId) throws SchedulerException {
        JobKey jobKey = new JobKey("timingTask_" + taskId);
        scheduler.deleteJob(jobKey);
    }
    private JobDetail buildJobDetail(TimingTask task) {
        JobDataMap jobDataMap = new JobDataMap();
        jobDataMap.put("taskId", task.getId());
        return JobBuilder.newJob(TimingTaskJob.class)
                .withIdentity("timingTask_" + task.getId())
                .withDescription(task.getTaskName())
                .usingJobData(jobDataMap)
                .storeDurably()
                .build();
    }
    private Trigger buildJobTrigger(TimingTask task, JobDetail jobDetail) {
        String cronExpression = convertToCronExpression(task);
            TriggerBuilder<CronTrigger> triggerBuilder = TriggerBuilder.newTrigger()
                .withIdentity("trigger_" + task.getId())
                .withDescription(task.getTaskName())
                .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression));
        if (jobDetail != null) {
            triggerBuilder.forJob(jobDetail);
        }
        if (task.getNexExecutionTime() != null) {
            triggerBuilder.startAt(Date.from(task.getNexExecutionTime().atZone(ZoneId.systemDefault()).toInstant()));
        }
        return triggerBuilder.build();
    }
    private String convertToCronExpression(TimingTask task) {
        // å‚数校验
        if (task == null || task.getFrequencyType() == null || task.getFrequencyDetail() == null) {
            throw new IllegalArgumentException("任务参数不能为空");
        }
        // ä½¿ç”¨switch确保条件互斥
        return switch (task.getFrequencyType().toUpperCase()) { // ç»Ÿä¸€è½¬ä¸ºå¤§å†™æ¯”较
            case "DAILY" -> convertDailyToCron(task.getFrequencyDetail());
            case "WEEKLY" -> convertWeeklyToCron(task.getFrequencyDetail());
            case "MONTHLY" -> convertMonthlyToCron(task.getFrequencyDetail());
            case "QUARTERLY" -> convertQuarterlyToCron(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 = convertDayNamesToCron(parts[0]);
        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 convertQuarterlyToCron(String frequencyDetail) {
        String[] parts = validateAndSplit(frequencyDetail, ",", 3);
        int month = validateMonth(parts[0]);  // éªŒè¯æœˆä»½(1-12)
        int day = validateDayOfMonth(parts[1]);  // éªŒè¯æ—¥æœŸ
        LocalTime time = parseTime(parts[2]);  // è§£æžæ—¶é—´
        // è®¡ç®—季度起始月份(1月=1, 4月=4, 7月=7, 10月=10)
        int quarterStartMonth = ((month - 1) / 3) * 3 + 1;
        return String.format("0 %d %d %d %d/3 ?",
                time.getMinute(),
                time.getHour(),
                day,
                quarterStartMonth);
    }
    // æ–°å¢žéªŒè¯æœˆä»½çš„æ–¹æ³•(1-12)
    private int validateMonth(String monthStr) {
        try {
            int month = Integer.parseInt(monthStr);
            if (month < 1 || month > 12) {
                throw new IllegalArgumentException("月份必须在1-12之间");
            }
            return month;
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException("无效的月份格式");
        }
    }
    // è¾…助方法:解析时间
    private LocalTime parseTime(String timeStr) {
        try {
            return LocalTime.parse(timeStr);
        } catch (DateTimeParseException e) {
            throw new IllegalArgumentException("时间格式必须为HH:mm", e);
        }
    }
    // è¾…助方法:验证并分割字符串
    private String[] validateAndSplit(String input, String delimiter, int expectedParts) {
        String[] parts = input.split(delimiter);
        if (parts.length != expectedParts) {
            throw new IllegalArgumentException(
                    String.format("格式错误,应为%d部分用'%s'分隔", expectedParts, delimiter));
        }
        return parts;
    }
    // è¾…助方法:验证月份中的日
    private int validateDayOfMonth(String dayStr) {
        int day = Integer.parseInt(dayStr);
        if (day < 1 || day > 31) {
            throw new IllegalArgumentException("日期必须在1-31之间");
        }
        return day;
    }
    // è¾…助方法:验证季度中的月
    private int validateMonthInQuarter(String monthStr) {
        int month = Integer.parseInt(monthStr);
        if (month < 1 || month > 3) {
            throw new IllegalArgumentException("季度月份必须是1、2或3");
        }
        return month;
    }
    // è½¬æ¢æ˜ŸæœŸå‡ åç§°
    private String convertDayNamesToCron(String dayNames) {
        return Arrays.stream(dayNames.split("\\|"))
                .map(this::convertSingleDayName)
                .collect(Collectors.joining(","));
    }
    // è½¬æ¢å•个星期几名称
    private String convertSingleDayName(String dayName) {
        switch (dayName.toUpperCase()) {
            case "MON": return "MON";
            case "TUE": return "TUE";
            case "WED": return "WED";
            case "THU": return "THU";
            case "FRI": return "FRI";
            case "SAT": return "SAT";
            case "SUN": return "SUN";
            default: throw new IllegalArgumentException("无效的星期几: " + dayName);
        }
    }
}
main-business/src/main/java/com/ruoyi/business/service/impl/TimingTaskServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,405 @@
package com.ruoyi.business.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.business.dto.TimingTaskDto;
import com.ruoyi.business.entity.TimingTask;
import com.ruoyi.business.mapper.InspectionTaskMapper;
import com.ruoyi.business.mapper.TimingTaskMapper;
import com.ruoyi.business.service.TimingTaskService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.system.mapper.SysUserMapper;
import lombok.extern.slf4j.Slf4j;
import org.quartz.SchedulerException;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import org.springframework.transaction.annotation.Transactional;
import java.time.*;
import java.util.*;
import java.util.stream.Collectors;
/**
 * <p>
 * å®šæ—¶å·¡æ£€ä»»åŠ¡è¡¨ æœåŠ¡å®žçŽ°ç±»
 * </p>
 *
 * @author ld
 * @since 2025-06-30
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class TimingTaskServiceImpl extends ServiceImpl<TimingTaskMapper, TimingTask> implements TimingTaskService {
    private final TimingTaskMapper timingTaskMapper;
    private final InspectionTaskMapper inspectionTaskMapper;
    private  final TimingTaskScheduler timingTaskScheduler;
    private  final SysUserMapper sysUserMapper;
    @Override
    public IPage<TimingTaskDto> selectTimingTaskList(Page<TimingTask> page, TimingTask timingTask) {
        // 1. å…ˆåˆ†é¡µæŸ¥è¯¢å®šæ—¶ä»»åŠ¡æ•°æ®
        IPage<TimingTask> taskPage = timingTaskMapper.selectPage(page, null);
        // 2. å¦‚果没有数据,直接返回空分页
        if (taskPage.getRecords().isEmpty()) {
            return new Page<>(taskPage.getCurrent(), taskPage.getSize(), taskPage.getTotal());
        }
        // 3. æ”¶é›†æ‰€æœ‰éœ€è¦æŸ¥è¯¢çš„用户ID
        Set<Long> userIds = new HashSet<>();
        // æ”¶é›†ç™»è®°äººID
        taskPage.getRecords().forEach(task -> {
            if (task.getRegistrantId() != null) {
                userIds.add(task.getRegistrantId());
            }
        });
        // æ”¶é›†å·¡æ£€äººID(多个ID以逗号分隔)
        taskPage.getRecords().forEach(task -> {
            if (StringUtils.isNotBlank(task.getInspectorIds())) {
                Arrays.stream(task.getInspectorIds().split(","))
                        .filter(StringUtils::isNotBlank)
                        .map(Long::valueOf)
                        .forEach(userIds::add);
            }
        });
        // 4. æ‰¹é‡æŸ¥è¯¢ç”¨æˆ·ä¿¡æ¯
        Map<Long, String> userNickNameMap = new HashMap<>();
        if (!userIds.isEmpty()) {
            List<SysUser> users = sysUserMapper.selectBatchIds(userIds);
            users.forEach(user -> userNickNameMap.put(user.getUserId(), user.getNickName()));
        }
        // 5. è½¬æ¢ä¸ºDTO
        List<TimingTaskDto> dtoList = taskPage.getRecords().stream().map(task -> {
            TimingTaskDto dto = new TimingTaskDto();
            // å¤åˆ¶åŸºæœ¬å±žæ€§
            BeanUtils.copyProperties(task, dto);
            // è®¾ç½®ç™»è®°äººæ˜µç§°
            if (task.getRegistrantId() != null) {
                dto.setRegistrant(userNickNameMap.getOrDefault(task.getRegistrantId(), "未知用户"));
            }
            // è®¾ç½®å·¡æ£€äººæ˜µç§°åˆ—表
            if (StringUtils.isNotBlank(task.getInspectorIds())) {
                List<String> inspectorNickNames = Arrays.stream(task.getInspectorIds().split(","))
                        .filter(StringUtils::isNotBlank)
                        .map(idStr -> {
                            Long id = Long.valueOf(idStr);
                            return userNickNameMap.getOrDefault(id, "未知用户");
                        })
                        .toList();
                dto.setInspector(inspectorNickNames.toString());
            }
            return dto;
        }).collect(Collectors.toList());
        // 6. æž„建返回的分页对象
        Page<TimingTaskDto> resultPage = new Page<>(taskPage.getCurrent(), taskPage.getSize(), taskPage.getTotal());
        resultPage.setRecords(dtoList);
        return resultPage;
    }
    @Override
    @Transactional
    public int addOrEditTimingTask(TimingTaskDto timingTaskDto) throws SchedulerException {
        TimingTask timingTask = new TimingTask();
        BeanUtils.copyProperties(timingTaskDto, timingTask);
        // è®¾ç½®åˆ›å»ºäººä¿¡æ¯å’Œé»˜è®¤å€¼
        if (Objects.isNull(timingTaskDto.getId())) {
            timingTask.setRegistrationDate(LocalDate.now());
            timingTask.setActive(true);
            // è®¡ç®—首次执行时间
            LocalDateTime firstExecutionTime = calculateFirstExecutionTime(timingTask);
            timingTask.setNexExecutionTime(firstExecutionTime);
            int result = timingTaskMapper.insert(timingTask);
            if (result > 0) {
                // æ–°å¢žæˆåŠŸåŽæ·»åŠ åˆ°è°ƒåº¦å™¨
                timingTaskScheduler.scheduleTimingTask(timingTask);
            }
            return result;
        } else {
            int result = timingTaskMapper.updateById(timingTask);
            if (result > 0) {
                // æ›´æ–°æˆåŠŸåŽé‡æ–°è°ƒåº¦ä»»åŠ¡
                timingTaskScheduler.rescheduleTimingTask(timingTask);
            }
            return result;
        }
    }
    private LocalDateTime calculateFirstExecutionTime(TimingTask task) {
        // æ ¹æ®é¢‘率类型和详情计算首次执行时间
        return switch (task.getFrequencyType()) {
            case "DAILY" ->
                // å¦‚果是每天执行,计算今天或明天的具体时间
                    calculateDailyFirstExecution(task.getFrequencyDetail());
            case "WEEKLY" ->
                // å¦‚果是每周执行,计算下周的具体星期几
                    calculateWeeklyFirstExecution(task.getFrequencyDetail());
            case "MONTHLY" ->
                // å¦‚果是每月执行,计算下个月的具体日期
                    calculateMonthlyFirstExecution(task.getFrequencyDetail());
            case "QUARTERLY" ->
                // è‡ªå®šä¹‰é¢‘率,如每小时、每30分钟等
                    calculateCustomFirstExecution(task.getFrequencyDetail());
            default -> throw new IllegalArgumentException("不支持的频率类型: " + task.getFrequencyType());
        };
    }
    private LocalDateTime calculateDailyFirstExecution(String frequencyDetail) {
        // frequencyDetail可能是具体时间,如 "14:30"
        LocalTime executionTime = LocalTime.parse(frequencyDetail);
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime todayExecution = LocalDateTime.of(now.toLocalDate(), executionTime);
        // å¦‚果今天的时间已过,则安排明天执行
        return now.isBefore(todayExecution) ? todayExecution : todayExecution.plusDays(1);
    }
    private LocalDateTime calculateWeeklyFirstExecution(String frequencyDetail) {
        return null;
    }
    private LocalDateTime calculateMonthlyFirstExecution(String frequencyDetail) {
        return null;
    }
    private LocalDateTime calculateCustomFirstExecution(String frequencyDetail) {
        return null;
    }
    @Override
    @Transactional
    public void updateTaskExecutionTime(Long taskId) {
        TimingTask task = timingTaskMapper.selectById(taskId);
        if (task == null) {
            throw new RuntimeException("定时任务不存在,ID: " + taskId);
        }
        // æ›´æ–°æœ€åŽæ‰§è¡Œæ—¶é—´ä¸ºå½“前时间
        task.setLastExecuteTime(LocalDateTime.now());
        // è®¡ç®—下次执行时间
        LocalDateTime nextExecutionTime = calculateNextExecutionTime(
                task.getFrequencyType(),
                task.getFrequencyDetail(),
                LocalDateTime.now()
        );
        task.setNexExecutionTime(nextExecutionTime);
        // æ›´æ–°æ•°æ®åº“
        timingTaskMapper.updateById(task);
    }
    /**
     * è®¡ç®—下次执行时间
     */
    private LocalDateTime calculateNextExecutionTime(String frequencyType,
                                                     String frequencyDetail,
                                                     LocalDateTime currentTime) {
        try {
            return switch (frequencyType) {
                case "DAILY" -> calculateDailyNextTime(frequencyDetail, currentTime);
                case "WEEKLY" -> calculateWeeklyNextTime(frequencyDetail, currentTime);
                case "MONTHLY" -> calculateMonthlyNextTime(frequencyDetail, currentTime);
                case "QUARTERLY" -> calculateQuarterlyNextTime(frequencyDetail, currentTime);
                default -> throw new IllegalArgumentException("不支持的频率类型: " + frequencyType);
            };
        } catch (Exception e) {
            throw new RuntimeException("计算下次执行时间失败: " + e.getMessage(), e);
        }
    }
    /**
     * è®¡ç®—每日任务的下次执行时间
     */
    private LocalDateTime calculateDailyNextTime(String timeStr, LocalDateTime current) {
        LocalTime executionTime = LocalTime.parse(timeStr); // è§£æžæ ¼å¼ "HH:mm"
        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(",");
        String dayOfWeekStr = parts[0];  // å¦‚ "MON" æˆ– "MON|WED|FRI"
        LocalTime time = LocalTime.parse(parts[1]); // æ—¶é—´éƒ¨åˆ†
        // è§£æžæ˜ŸæœŸå‡ (支持多个星期)
        Set<DayOfWeek> targetDays = parseDayOfWeeks(dayOfWeekStr);
        // ä»Žå½“前时间开始找下一个符合条件的星期几
        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]);
        LocalTime time = LocalTime.parse(parts[1]);
        // ä»Žä¸‹ä¸ªæœˆå¼€å§‹è®¡ç®—
        LocalDateTime nextTime = current.plusMonths(1)
                .withDayOfMonth(Math.min(dayOfMonth, current.plusMonths(1).toLocalDate().lengthOfMonth()))
                .with(time);
        return nextTime;
    }
    /**
     * è®¡ç®—每季度任务的下次执行时间
     */
    private LocalDateTime calculateQuarterlyNextTime(String detail, LocalDateTime current) {
        String[] parts = detail.split(",");
        int quarterMonth = Integer.parseInt(parts[0]); // 1=第1个月,2=第2个月,3=第3个月
        int dayOfMonth = Integer.parseInt(parts[1]);
        LocalTime time = LocalTime.parse(parts[2]);
        // è®¡ç®—当前季度
        int currentQuarter = (current.getMonthValue() - 1) / 3 + 1;
        int currentMonthInQuarter = (current.getMonthValue() - 1) % 3 + 1;
        YearMonth targetYearMonth;
        if (currentMonthInQuarter < quarterMonth) {
            // æœ¬å­£åº¦å†…还有执行机会
            targetYearMonth = YearMonth.from(current)
                    .plusMonths(quarterMonth - currentMonthInQuarter);
        } else {
            // éœ€è¦åˆ°ä¸‹ä¸ªå­£åº¦
            targetYearMonth = YearMonth.from(current)
                    .plusMonths(3 - currentMonthInQuarter + quarterMonth);
        }
        // å¤„理月末日期
        int adjustedDay = Math.min(dayOfMonth, targetYearMonth.lengthOfMonth());
        return LocalDateTime.of(
                targetYearMonth.getYear(),
                targetYearMonth.getMonthValue(),
                adjustedDay,
                time.getHour(),
                time.getMinute()
        );
    }
    /**
     * è§£æžæ˜ŸæœŸå‡ å­—符串
     */
    private Set<DayOfWeek> parseDayOfWeeks(String dayOfWeekStr) {
        Set<DayOfWeek> days = new HashSet<>();
        String[] dayStrs = dayOfWeekStr.split("\\|");
        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);
            }
        }
        return days;
    }
//    /**
//     * æ¯å¤©17:40准时触发,执行到期任务
//     */
//    @Scheduled(cron = "0 55 17 * * ?", zone = "Asia/Shanghai")
//    @Transactional
//    public void executeDueTasks() {
//        LocalDateTime now = LocalDateTime.now();
//        log.info("定时任务触发,当前时间: {}", now);
//
//        // 1. æŸ¥è¯¢æ‰€æœ‰éœ€è¦ç«‹å³æ‰§è¡Œçš„任务
//        List<TimingTask> dueTasks = timingTaskMapper.selectActiveTasks(now);
//
//        // 2. å¤„理每个任务
//        dueTasks.forEach(task -> {
//            // 2.1 è½¬æ¢ä¸ºå·¡æ£€ä»»åŠ¡å¹¶ä¿å­˜
//            InspectionTask inspectionTask = convertToInspectionTask(task);
//            inspectionTaskMapper.insert(inspectionTask);
//
//            // 2.2 è®¡ç®—并更新下次执行时间
//            updateNextExecutionTime(task, now);
//
//            log.info("任务[{}]已执行,下次执行时间: {}",
//                    task.getTaskName(),
//                    task.getNexExecutionTime());
//        });
//    }
//
//    private InspectionTask  convertToInspectionTask(TimingTask timingTask) {
//        InspectionTask inspectionTask = new InspectionTask();
//
//        // å¤åˆ¶åŸºæœ¬å±žæ€§
//        inspectionTask.setTaskName(timingTask.getTaskName());
//        inspectionTask.setInspectorId(timingTask.getInspectorIds());
//        inspectionTask.setPort(timingTask.getInspectionLocation());
//        inspectionTask.setRemarks("自动生成自定时任务ID: " + timingTask.getId());
//        inspectionTask.setRegistrantId(timingTask.getRegistrantId());
//        inspectionTask.setFrequency(timingTask.getFrequencyType());
//        return inspectionTask;
//    }
//
//    private void updateNextExecutionTime(TimingTask task, LocalDateTime currentExecutionTime) {
//        switch (task.getFrequencyType()) {
//            case "DAILY":
//                task.setNexExecutionTime(currentExecutionTime.plusDays(1));
//                break;
//            case "WEEKLY":
//                task.setNexExecutionTime(currentExecutionTime.plusWeeks(1));
//                break;
//            case "MONTHLY":
//                task.setNexExecutionTime(currentExecutionTime.plusMonths(1));
//                break;
//            default:
//                task.setNexExecutionTime(null); // å•次任务
//        }
//        timingTaskMapper.updateById(task);
//    }
    @Override
    public int delByIds(Long[] ids) {
        return timingTaskMapper.deleteByIds(Arrays.asList(ids));
    }
}
main-business/src/main/java/com/ruoyi/business/task/TimingTaskJob.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,66 @@
package com.ruoyi.business.task;
import com.ruoyi.business.entity.InspectionTask;
import com.ruoyi.business.entity.TimingTask;
import com.ruoyi.business.mapper.InspectionTaskMapper;
import com.ruoyi.business.service.TimingTaskService;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component
@DisallowConcurrentExecution // ç¦æ­¢å¹¶å‘执行同一个Job
public class TimingTaskJob implements Job {
    @Autowired
    private
    TimingTaskService timingTaskService;
    @Autowired
    private InspectionTaskMapper inspectionTaskMapper;
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
        Long taskId = jobDataMap.getLong("taskId");
        try {
            // 1. èŽ·å–å®šæ—¶ä»»åŠ¡è¯¦æƒ…
            TimingTask timingTask = timingTaskService.getById(taskId);
            if (timingTask == null || !timingTask.isActive()) {
                return;
            }
            // 2. åˆ›å»ºå¹¶ä¿å­˜å·¡æ£€ä»»åŠ¡è®°å½• - è¿™å°±æ˜¯æ‚¨æä¾›çš„代码应该放的位置
            InspectionTask inspectionTask = createInspectionTask(timingTask);
            inspectionTaskMapper.insert(inspectionTask);
            // 3. æ›´æ–°å®šæ—¶ä»»åŠ¡çš„æ‰§è¡Œæ—¶é—´
            timingTaskService.updateTaskExecutionTime(taskId);
            // 4. è®°å½•执行日志
//            timingTaskService.recordExecutionLog(taskId, true, "任务执行成功,生成巡检任务ID: " + inspectionTask.getId());
        } catch (Exception e) {
//            timingTaskService.recordExecutionLog(taskId, false, "任务执行失败: " + e.getMessage());
            throw new JobExecutionException(e);
        }
    }
    // è¿™å°±æ˜¯æ‚¨æä¾›çš„代码封装成的方法
    private InspectionTask createInspectionTask(TimingTask timingTask) {
        InspectionTask inspectionTask = new InspectionTask();
        // å¤åˆ¶åŸºæœ¬å±žæ€§
        inspectionTask.setTaskName(timingTask.getTaskName());
        inspectionTask.setInspectorId(timingTask.getInspectorIds());
        inspectionTask.setInspectionLocation(timingTask.getInspectionLocation());
        inspectionTask.setRemarks("自动生成自定时任务ID: " + timingTask.getId());
        inspectionTask.setRegistrantId(timingTask.getRegistrantId());
        inspectionTask.setFrequencyType(timingTask.getFrequencyType());
        return inspectionTask;
    }
}
main-business/src/main/resources/db/migration/postgresql/V20250614134700__create_table_inspection_task.sql
@@ -9,6 +9,7 @@
    remarks       TEXT,                        -- å¤‡æ³¨è¯´æ˜Ž
    registrant_id BIGINT,                      -- ç™»è®°äººå‘˜id
    registrant    VARCHAR(100),                -- ç™»è®°äººå‘˜
    frequency     VARCHAR(100),                -- é¢‘次
    deleted       INT NOT NULL DEFAULT 0,      -- è½¯åˆ é™¤æ ‡å¿—:0=未删除,1=已删除
    create_by     VARCHAR(255),                -- åˆ›å»ºäººç”¨æˆ·å
@@ -17,18 +18,30 @@
    update_time   TIMESTAMP WITHOUT TIME ZONE  -- æœ€åŽæ›´æ–°æ—¶é—´ï¼Œé»˜è®¤å½“前时间
);
-- æ·»åŠ è¡¨æ³¨é‡Š
COMMENT ON TABLE inspection_task IS '巡检任务表';
COMMENT
ON TABLE inspection_task IS '巡检任务表';
-- æ·»åŠ å­—æ®µæ³¨é‡Š
COMMENT ON COLUMN inspection_task.id IS '巡检任务唯一标识';
COMMENT ON COLUMN inspection_task.task_name IS '巡检任务名称';
COMMENT ON COLUMN inspection_task.inspector IS '执行巡检的人员姓名';
COMMENT ON COLUMN inspection_task.port IS '巡检地点详细描述';
COMMENT ON COLUMN inspection_task.remarks IS '任务附加说明或特殊情况记录';
COMMENT ON COLUMN inspection_task.registrant IS '任务登记人姓名';
COMMENT
ON COLUMN inspection_task.id IS '巡检任务唯一标识';
COMMENT
ON COLUMN inspection_task.task_name IS '巡检任务名称';
COMMENT
ON COLUMN inspection_task.inspector IS '执行巡检的人员姓名';
COMMENT
ON COLUMN inspection_task.port IS '巡检地点详细描述';
COMMENT
ON COLUMN inspection_task.remarks IS '任务附加说明或特殊情况记录';
COMMENT
ON COLUMN inspection_task.registrant IS '任务登记人姓名';
COMMENT ON COLUMN inspection_task.deleted IS '软删除标志,0=未删除,1=已删除';
COMMENT ON COLUMN inspection_task.create_by IS '创建该记录的用户';
COMMENT ON COLUMN inspection_task.create_time IS '记录创建时间';
COMMENT ON COLUMN inspection_task.update_by IS '最后修改该记录的用户';
COMMENT ON COLUMN inspection_task.update_time IS '记录最后更新时间';
COMMENT
ON COLUMN inspection_task.deleted IS '软删除标志,0=未删除,1=已删除';
COMMENT
ON COLUMN inspection_task.create_by IS '创建该记录的用户';
COMMENT
ON COLUMN inspection_task.create_time IS '记录创建时间';
COMMENT
ON COLUMN inspection_task.update_by IS '最后修改该记录的用户';
COMMENT
ON COLUMN inspection_task.update_time IS '记录最后更新时间';
main-business/src/main/resources/db/migration/postgresql/V20250630153900__create_table_timing_task.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,63 @@
CREATE TABLE timing_task
(
    id                  BIGSERIAL PRIMARY KEY,              -- ä¸»é”®ID,自动递增
    task_name           VARCHAR(255) NOT NULL,              -- ä»»åŠ¡åç§°ï¼Œä¸å…è®¸ä¸ºç©º
    inspector_ids       VARCHAR(255) NOT NULL,              -- å·¡æ£€äººids,不允许为空
    inspection_location VARCHAR(255) NOT NULL,              -- å·¡æ£€åœ°ç‚¹ï¼Œä¸å…è®¸ä¸ºç©º
    -- å®šæ—¶é…ç½®
    frequency_type      VARCHAR(20)  NOT NULL CHECK (frequency_type IN ('DAILY', 'WEEKLY', 'MONTHLY', 'QUARTERLY')),
    frequency_detail    VARCHAR(100),                       -- å…·ä½“时间配置
    -- æ¯æ—¥ä»»åŠ¡é…ç½®ç¤ºä¾‹ï¼šfrequency_type='DAILY', frequency_detail='14:30'
    -- æ¯å‘¨ä»»åŠ¡é…ç½®ç¤ºä¾‹ï¼šfrequency_type='WEEKLY', frequency_detail='MON;14:30'
    -- æ¯æœˆä»»åŠ¡é…ç½®ç¤ºä¾‹ï¼šfrequency_type='MONTHLY', frequency_detail='15;14:30' (每月15日14:30)
    -- æ¯å­£åº¦ä»»åŠ¡é…ç½®ç¤ºä¾‹ï¼šfrequency_type='QUARTERLY', frequency_detail='15;14:30' (每季度第一个月15日14:30)
    next_execution_time TIMESTAMP,                          -- ä¸‹æ¬¡æ‰§è¡Œæ—¶é—´
    last_execution_time TIMESTAMP,                          -- æœ€åŽæ‰§è¡Œæ—¶é—´
    is_active           BOOLEAN               DEFAULT TRUE, -- æ˜¯å¦æ¿€æ´»
    remarks             TEXT,                               -- å¤‡æ³¨
    registrant_id       BIGINT,                             -- ç™»è®°äººid,不允许为空
    registrant          VARCHAR(255) NOT NULL,              -- ç™»è®°äººï¼Œä¸å…è®¸ä¸ºç©º
    registration_date   DATE         NOT NULL,              -- ç™»è®°æ—¥æœŸï¼Œä¸å…è®¸ä¸ºç©º
    deleted             INT          NOT NULL DEFAULT 0,    -- è½¯åˆ é™¤æ ‡å¿—:0=未删除,1=已删除
    create_by           VARCHAR(255),                       -- åˆ›å»ºäººç”¨æˆ·å
    create_time         TIMESTAMP WITHOUT TIME ZONE,        -- åˆ›å»ºæ—¶é—´ï¼Œé»˜è®¤å½“前时间
    update_by           VARCHAR(255),                       -- æœ€åŽæ›´æ–°äººç”¨æˆ·å
    update_time         TIMESTAMP WITHOUT TIME ZONE         -- æœ€åŽæ›´æ–°æ—¶é—´ï¼Œé»˜è®¤å½“前时间
);
-- ä¸ºè¡¨æ·»åŠ æ³¨é‡Š
COMMENT
ON TABLE timing_task IS '定时巡检任务表';
-- ä¸ºå­—段添加注释
COMMENT
ON COLUMN timing_task.id IS '主键ID';
COMMENT
ON COLUMN timing_task.task_name IS '任务名称';
COMMENT
ON COLUMN timing_task.inspector_ids IS '巡检人';
COMMENT
ON COLUMN timing_task.inspection_location IS '巡检地点';
COMMENT
ON COLUMN timing_task.frequency IS '频次';
COMMENT
ON COLUMN timing_task.remarks IS '备注';
COMMENT
ON COLUMN timing_task.registrant_id IS '登记人id';
COMMENT
ON COLUMN timing_task.registrant IS '登记人';
COMMENT
ON COLUMN timing_task.registration_date IS '登记日期';
COMMENT
ON COLUMN timing_task.deleted IS '软删除标志,0=未删除,1=已删除';
COMMENT
ON COLUMN timing_task.create_by IS '创建该记录的用户';
COMMENT
ON COLUMN timing_task.create_time IS '记录创建时间';
COMMENT
ON COLUMN timing_task.update_by IS '最后修改该记录的用户';
COMMENT
ON COLUMN timing_task.update_time IS '记录最后更新时间';
main-business/src/main/resources/db/migration/postgresql/V20250701142700__create_table_equipment_management.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
DROP TABLE IF EXISTS equipment_management;
-- åˆ›å»ºè®¾å¤‡ç®¡ç†è¡¨
CREATE TABLE equipment_management
(
    id               BIGSERIAL PRIMARY KEY,           -- ä¸»é”® ID
    equipment_id     VARCHAR(50)  NOT NULL,           -- è®¾å¤‡ç¼–号
    equipment_name   VARCHAR(100) NOT NULL,           -- è®¾å¤‡åç§°
    quantity         INT          NOT NULL DEFAULT 0, -- æ•°é‡
    specification    VARCHAR(50)  NOT NULL,           -- è§„格型号
    usage_status     VARCHAR(20)  NOT NULL,           -- ä½¿ç”¨çŠ¶æ€
    using_department VARCHAR(50)  NOT NULL,           -- ä½¿ç”¨éƒ¨é—¨
    user_id          BIGINT,                          -- ä½¿ç”¨äºº
    storage_location VARCHAR(100) NOT NULL,           -- å­˜æ”¾ä½ç½®
    purchase_date    DATE,                            -- é‡‡è´­æ—¥æœŸ
    purchase_price   DECIMAL(10, 2),                  -- é‡‡è´­ä»·æ ¼
    deleted          INT          NOT NULL DEFAULT 0, -- è½¯åˆ é™¤æ ‡å¿—:0=未删除,1=已删除
    create_by        VARCHAR(255),                    -- åˆ›å»ºäººç”¨æˆ·å
    create_time      TIMESTAMP WITHOUT TIME ZONE,     -- åˆ›å»ºæ—¶é—´ï¼Œé»˜è®¤å½“前时间
    update_by        VARCHAR(255),                    -- æœ€åŽæ›´æ–°äººç”¨æˆ·å
    update_time      TIMESTAMP WITHOUT TIME ZONE      -- æœ€åŽæ›´æ–°æ—¶é—´ï¼Œé»˜è®¤å½“前时间
);
-- æ·»åŠ è¡¨æ³¨é‡Š
COMMENT
ON TABLE equipment_management IS ' è®¾å¤‡ç®¡ç†è¡¨ ';
-- æ·»åŠ å­—æ®µæ³¨é‡Š
COMMENT
ON COLUMN equipment_management.id IS ' ä¸»é”® ID';
COMMENT
ON COLUMN equipment_management.equipment_id IS ' è®¾å¤‡ç¼–号 ';
COMMENT
ON COLUMN equipment_management.equipment_name IS ' è®¾å¤‡åç§° ';
COMMENT
ON COLUMN equipment_management.quantity IS ' æ•°é‡ ';
COMMENT
ON COLUMN equipment_management.specification IS ' è§„格型号 ';
COMMENT
ON COLUMN equipment_management.usage_status IS ' ä½¿ç”¨çŠ¶æ€ ';
COMMENT
ON COLUMN equipment_management.using_department IS ' ä½¿ç”¨éƒ¨é—¨ ';
COMMENT
ON COLUMN equipment_management.user_id IS ' ä½¿ç”¨äººID ';
COMMENT
ON COLUMN equipment_management.storage_location IS ' å­˜æ”¾ä½ç½® ';
COMMENT
ON COLUMN equipment_management.purchase_date IS ' é‡‡è´­æ—¥æœŸ ';
COMMENT
ON COLUMN equipment_management.purchase_price IS ' é‡‡è´­ä»·æ ¼ ';
COMMENT
ON COLUMN inspection_task.deleted IS '软删除标志,0=未删除,1=已删除';
COMMENT
ON COLUMN inspection_task.create_by IS '创建该记录的用户';
COMMENT
ON COLUMN inspection_task.create_time IS '记录创建时间';
COMMENT
ON COLUMN inspection_task.update_by IS '最后修改该记录的用户';
COMMENT
ON COLUMN inspection_task.update_time IS '记录最后更新时间';
main-business/src/main/resources/mapper/EquipmentManagementMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
<?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.business.mapper.EquipmentManagementMapper">
        <!-- é€šç”¨æŸ¥è¯¢æ˜ å°„结果 -->
        <resultMap id="BaseResultMap" type="com.ruoyi.business.entity.EquipmentManagement">
                    <id column="id" property="id" />
                <result column="deleted" property="deleted" />
                <result column="create_by" property="createBy" />
                <result column="create_time" property="createTime" />
                <result column="update_by" property="updateBy" />
                <result column="update_time" property="updateTime" />
                    <result column="equipment_id" property="equipmentId" />
                    <result column="equipment_name" property="equipmentName" />
                    <result column="quantity" property="quantity" />
                    <result column="specification" property="specification" />
                    <result column="usage_status" property="usageStatus" />
                    <result column="using_department" property="usingDepartment" />
                    <result column="user_id" property="userId" />
                    <result column="storage_location" property="storageLocation" />
                    <result column="purchase_date" property="purchaseDate" />
                    <result column="purchase_price" property="purchasePrice" />
        </resultMap>
        <!-- é€šç”¨æŸ¥è¯¢ç»“果列 -->
        <sql id="Base_Column_List">
                deleted,
                create_by,
                create_time,
                update_by,
                update_time,
            id, equipment_id, equipment_name, quantity, specification, usage_status, using_department, user_id, storage_location, purchase_date, purchase_price
        </sql>
</mapper>
main-business/src/main/resources/mapper/TimingTaskMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
<?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.business.mapper.TimingTaskMapper">
        <!-- é€šç”¨æŸ¥è¯¢æ˜ å°„结果 -->
        <resultMap id="BaseResultMap" type="com.ruoyi.business.entity.TimingTask">
                    <id column="id" property="id" />
                <result column="deleted" property="deleted" />
                <result column="create_by" property="createBy" />
                <result column="create_time" property="createTime" />
                <result column="update_by" property="updateBy" />
                <result column="update_time" property="updateTime" />
                    <result column="task_name" property="taskName" />
                    <result column="inspector_ids" property="inspectorIds" />
                    <result column="inspection_location" property="inspectionLocation" />
                    <result column="frequency" property="frequency" />
                    <result column="remarks" property="remarks" />
                    <result column="registrant_id" property="registrantId" />
                    <result column="registrant" property="registrant" />
                    <result column="registration_date" property="registrationDate" />
        </resultMap>
        <!-- é€šç”¨æŸ¥è¯¢ç»“果列 -->
        <sql id="Base_Column_List">
                deleted,
                create_by,
                create_time,
                update_by,
                update_time,
            id, task_name, inspector_ids, inspection_location, frequency, remarks, registrant_id, registrant, registration_date
        </sql>
</mapper>
main-business/src/main/resources/mapper/TreeMapper.xml
@@ -23,5 +23,14 @@
                update_time,
            id, name, parent_id
        </sql>
    <select id="listRecursiveSubNodeIds" resultType="java.lang.Long">
        WITH RECURSIVE sub_nodes AS (
            SELECT id FROM tree WHERE parent_id = #{treeId}
            UNION ALL
            SELECT t.id FROM tree t
                                 JOIN sub_nodes s ON t.parent_id = s.id
        )
        SELECT id FROM sub_nodes
    </select>
</mapper>
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java
@@ -1,9 +1,13 @@
package com.ruoyi.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.common.core.domain.entity.SysUser;
import org.apache.ibatis.annotations.Param;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
 * ç”¨æˆ·è¡¨ æ•°æ®å±‚
@@ -131,4 +135,13 @@
    List<SysUser> selectUserListAll();
    List<SysUser> selectList(List<Long> registrantIds);
    List<SysUser> selectUsersByIds(@Param("userIds") List<Long> userIds);
    /**
     * æ‰¹é‡æŸ¥è¯¢ç”¨æˆ·ä¿¡æ¯
     * @param userIds ç”¨æˆ·ID集合
     * @return ç”¨æˆ·åˆ—表
     */
    List<SysUser> selectBatchIds(@Param("userIds") Set<Long> userIds);
}
ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
@@ -158,6 +158,36 @@
            </if>
        </where>
    </select>
    <select id="selectUsersByIds" resultType="com.ruoyi.common.core.domain.entity.SysUser">
        SELECT user_id, nick_name
        FROM sys_user
        WHERE user_id IN
        <foreach collection="userIds" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
    </select>
    <select id="selectBatchIds" resultType="com.ruoyi.common.core.domain.entity.SysUser">
        SELECT * FROM sys_user
        <!-- å¤„理空集合:若 userIds ä¸ºç©ºï¼Œä¸æ‰§è¡Œ WHERE æ¡ä»¶ï¼ˆé¿å… SQL è¯­æ³•错误) -->
        <where>
            <if test="userIds != null and userIds.size() > 0">
                user_id = ANY(
                <foreach collection="userIds" item="id" open="ARRAY[" separator="," close="]">
                    #{id}
                </foreach>
                )
            </if>
        </where>
        <!-- æŽ’序:按传入的 userIds é¡ºåºè¿”回结果(PostgreSQL ä¸“用) -->
        <if test="userIds != null and userIds.size() > 0">
            ORDER BY array_position(
            <foreach collection="userIds" item="id" open="ARRAY[" separator="," close="]" index="index">
                #{id}
            </foreach>,
            user_id  -- è¡¨ä¸­çš„ id å­—段,与数组中的元素匹配
            )
        </if>
    </select>
    <insert id="insertUser" parameterType="SysUser" useGeneratedKeys="true" keyProperty="userId">
         insert into sys_user(