From 0d7c3e7923c420ae6d3f8feff280bf4fcaac5ce7 Mon Sep 17 00:00:00 2001
From: buhuazhen <hua100783@gmail.com>
Date: 星期四, 28 五月 2026 11:22:06 +0800
Subject: [PATCH] feat 人员合同、设备定时保养、排班之后APP推送通知

---
 src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskJob.java        |   68 +++++++++++++
 src/main/java/com/ruoyi/staff/service/impl/StaffSchedulingServiceImpl.java |  101 ++++++++++++++++++++
 src/main/java/com/ruoyi/staff/task/StaffContractReminderTask.java          |  104 ++++++++++++++++++++
 3 files changed, 273 insertions(+), 0 deletions(-)

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..bef3be6 100644
--- a/src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskJob.java
+++ b/src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskJob.java
@@ -1,8 +1,15 @@
 package com.ruoyi.device.service.impl;
 
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.ruoyi.device.pojo.DeviceMaintenance;
 import com.ruoyi.device.pojo.MaintenanceTask;
+import com.ruoyi.project.system.domain.SysUser;
+import com.ruoyi.project.system.mapper.SysUserMapper;
+import com.ruoyi.project.system.service.ISysNoticeService;
+import com.ruoyi.staff.pojo.StaffOnJob;
+import com.ruoyi.staff.mapper.StaffOnJobMapper;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.quartz.*;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.jdbc.core.BeanPropertyRowMapper;
@@ -17,9 +24,11 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.ArrayList;
 
 @Component
 @DisallowConcurrentExecution
+@Slf4j
 public class MaintenanceTaskJob implements Job, Serializable {
     private static final long serialVersionUID = 1L;
 
@@ -28,6 +37,15 @@
 
     @Autowired
     private JdbcTemplate jdbcTemplate;
+
+    @Autowired
+    private StaffOnJobMapper staffOnJobMapper;
+
+    @Autowired
+    private SysUserMapper sysUserMapper;
+
+    @Autowired
+    private ISysNoticeService sysNoticeService;
 
     @Override
     public void execute(JobExecutionContext context) throws JobExecutionException {
@@ -52,6 +70,9 @@
             // 2. 鍒涘缓骞朵繚瀛樺贰妫�浠诲姟璁板綍 - 杩欏氨鏄偍鎻愪緵鐨勪唬鐮佸簲璇ユ斁鐨勪綅缃�
             DeviceMaintenance deviceMaintenance = createInspectionTask(timingTask);
             deviceMaintenanceService.save(deviceMaintenance);
+
+            // 鍙戦�佷繚鍏绘彁閱掗�氱煡
+            sendMaintenanceReminder(timingTask);
 
             // 3. 鏇存柊瀹氭椂浠诲姟鐨勬墽琛屾椂闂�
             if (!tasks.isEmpty()) {
@@ -243,4 +264,51 @@
 
         return days;
     }
+
+    /**
+     * 鍙戦�佽澶囦繚鍏绘彁閱掗�氱煡
+     * @param timingTask 淇濆吇浠诲姟
+     */
+    private void sendMaintenanceReminder(MaintenanceTask timingTask) {
+        try {
+            if (timingTask.getMaintenancePerson() == null || timingTask.getMaintenancePerson().isEmpty()) {
+                log.warn("淇濆吇浠诲姟 {} 鏈寚瀹氫繚鍏讳汉锛岃烦杩囨帹閫�", timingTask.getTaskName());
+                return;
+            }
+
+            // 閫氳繃淇濆吇浜哄鍚嶆煡璇㈠憳宸ユ。妗�
+            List<StaffOnJob> staffList = staffOnJobMapper.selectList(Wrappers.<StaffOnJob>lambdaQuery()
+                    .eq(StaffOnJob::getStaffName, timingTask.getMaintenancePerson())
+                    .eq(StaffOnJob::getStaffState, 1)
+                    .last("LIMIT 1"));
+
+            if (staffList.isEmpty()) {
+                log.warn("鏈壘鍒颁繚鍏讳汉 {} 鐨勫憳宸ユ。妗�", timingTask.getMaintenancePerson());
+                return;
+            }
+
+            StaffOnJob staff = staffList.get(0);
+
+            // 閫氳繃鍛樺伐缂栧彿鏌ヨ绯荤粺鐢ㄦ埛
+            SysUser sysUser = sysUserMapper.selectUserByUserName(staff.getStaffNo());
+            if (sysUser == null) {
+                log.warn("淇濆吇浜� {} 鏈壘鍒板搴旂殑绯荤粺鐢ㄦ埛璐﹀彿", staff.getStaffName());
+                return;
+            }
+
+            // 鏋勫缓閫氱煡鍐呭
+            String title = "璁惧淇濆吇鎻愰啋";
+            String message = String.format("鎮ㄥソ锛岃澶囥��%s銆戦渶瑕佽繘琛屼繚鍏伙紝璇峰強鏃跺鐞嗐��",
+                    timingTask.getTaskName());
+
+            // 鍙戦�侀�氱煡
+            List<Long> userIds = new ArrayList<>();
+            userIds.add(sysUser.getUserId());
+            sysNoticeService.simpleNoticeByUser(title, message, userIds, "/deviceMaintenance");
+
+            log.info("宸插悜淇濆吇浜� {} 鍙戦�佽澶囦繚鍏绘彁閱�", timingTask.getMaintenancePerson());
+        } catch (Exception e) {
+            log.error("鍙戦�佽澶囦繚鍏绘彁閱掑け璐ワ細{}", e.getMessage(), e);
+        }
+    }
 }
diff --git a/src/main/java/com/ruoyi/staff/service/impl/StaffSchedulingServiceImpl.java b/src/main/java/com/ruoyi/staff/service/impl/StaffSchedulingServiceImpl.java
index 4e1ca14..24d221d 100644
--- a/src/main/java/com/ruoyi/staff/service/impl/StaffSchedulingServiceImpl.java
+++ b/src/main/java/com/ruoyi/staff/service/impl/StaffSchedulingServiceImpl.java
@@ -2,17 +2,24 @@
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.framework.security.LoginUser;
+import com.ruoyi.project.system.domain.SysUser;
+import com.ruoyi.project.system.mapper.SysUserMapper;
+import com.ruoyi.project.system.service.ISysNoticeService;
 import com.ruoyi.staff.dto.SaveStaffSchedulingDto;
 import com.ruoyi.staff.dto.StaffSchedulingDto;
 import com.ruoyi.staff.mapper.StaffSchedulingMapper;
+import com.ruoyi.staff.pojo.StaffOnJob;
 import com.ruoyi.staff.pojo.StaffScheduling;
+import com.ruoyi.staff.mapper.StaffOnJobMapper;
 import com.ruoyi.staff.service.StaffSchedulingService;
 import com.ruoyi.staff.vo.SearchSchedulingVo;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
@@ -20,18 +27,27 @@
 import jakarta.annotation.Resource;
 import java.math.BigDecimal;
 import java.time.Duration;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * @author buhuazhen
  * @description 閽堝琛ㄣ�恠taff_scheduling銆戠殑鏁版嵁搴撴搷浣淪ervice瀹炵幇
  * @createDate 2025-09-03 14:50:34
  */
+@Slf4j
 @Service
 @RequiredArgsConstructor
 public class StaffSchedulingServiceImpl extends ServiceImpl<StaffSchedulingMapper, StaffScheduling>
         implements StaffSchedulingService {
 
     private final StaffSchedulingMapper staffSchedulingMapper;
+    private final StaffOnJobMapper staffOnJobMapper;
+    private final SysUserMapper sysUserMapper;
+    private final ISysNoticeService sysNoticeService;
 
     @Lazy
     @Resource
@@ -49,6 +65,91 @@
         // minutes = minutes < 0.5 ? 0 : 0.5; 鍏徃涓�鑸互0.5涓烘爣鍑嗚绠�
         staffScheduling.setWorkHours(BigDecimal.valueOf(hours + minutes));
         staffSchedulingService.saveOrUpdate(staffScheduling);
+
+        // 鍙戦�佹帓鐝�氱煡
+        sendSchedulingNotification(staffScheduling);
+    }
+
+    /**
+     * 鍙戦�佹帓鐝�氱煡缁欒鎺掔彮浜哄憳
+     * @param staffScheduling 鎺掔彮淇℃伅
+     */
+    private void sendSchedulingNotification(StaffScheduling staffScheduling) {
+        try {
+            if (staffScheduling.getStaffId() == null || staffScheduling.getStaffId().isEmpty()) {
+                return;
+            }
+
+            // 瑙f瀽鍛樺伐ID鍒楄〃
+            List<Long> staffIds = Arrays.stream(staffScheduling.getStaffId().split(","))
+                    .map(String::trim)
+                    .filter(s -> !s.isEmpty())
+                    .map(Long::parseLong)
+                    .collect(Collectors.toList());
+
+            if (staffIds.isEmpty()) {
+                return;
+            }
+
+            // 鏌ヨ鍛樺伐妗f
+            List<StaffOnJob> staffList = staffOnJobMapper.selectList(Wrappers.<StaffOnJob>lambdaQuery()
+                    .in(StaffOnJob::getId, staffIds)
+                    .eq(StaffOnJob::getStaffState, 1));
+
+            if (staffList.isEmpty()) {
+                return;
+            }
+
+            // 鑾峰彇鍛樺伐缂栧彿鍒楄〃
+            List<String> staffNos = staffList.stream()
+                    .map(StaffOnJob::getStaffNo)
+                    .filter(s -> s != null && !s.isEmpty())
+                    .collect(Collectors.toList());
+
+            if (staffNos.isEmpty()) {
+                return;
+            }
+
+            // 鏌ヨ绯荤粺鐢ㄦ埛
+            List<SysUser> users = sysUserMapper.selectList(Wrappers.<SysUser>lambdaQuery()
+                    .in(SysUser::getUserName, staffNos)
+                    .eq(SysUser::getStatus, "0"));
+
+            if (users.isEmpty()) {
+                log.warn("鎺掔彮閫氱煡锛氭湭鎵惧埌瀵瑰簲鐨勭郴缁熺敤鎴�");
+                return;
+            }
+
+            // 鏍煎紡鍖栨棩鏈熸椂闂�
+            DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+            DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm");
+
+            String workDateStr = staffScheduling.getWorkDate() != null
+                    ? new java.text.SimpleDateFormat("yyyy-MM-dd").format(staffScheduling.getWorkDate())
+                    : "";
+            String startTimeStr = staffScheduling.getWorkStartTime() != null
+                    ? staffScheduling.getWorkStartTime().format(timeFormatter)
+                    : "";
+            String endTimeStr = staffScheduling.getWorkEndTime() != null
+                    ? staffScheduling.getWorkEndTime().format(timeFormatter)
+                    : "";
+
+            // 鏋勫缓閫氱煡鍐呭
+            String title = "鎺掔彮閫氱煡";
+            String message = String.format("鎮ㄥソ锛屾偍宸茶瀹夋帓鍦� %s 涓婄彮锛屽伐浣滄椂闂达細%s - %s",
+                    workDateStr, startTimeStr, endTimeStr);
+
+            // 鍙戦�侀�氱煡缁欐墍鏈夎鎺掔彮浜哄憳
+            List<Long> userIds = users.stream()
+                    .map(SysUser::getUserId)
+                    .collect(Collectors.toList());
+
+            sysNoticeService.simpleNoticeByUser(title, message, userIds, "/scheduling");
+
+            log.info("宸插悜 {} 鍚嶅憳宸ュ彂閫佹帓鐝�氱煡", userIds.size());
+        } catch (Exception e) {
+            log.error("鍙戦�佹帓鐝�氱煡澶辫触锛歿}", e.getMessage(), e);
+        }
     }
 
     @Override
diff --git a/src/main/java/com/ruoyi/staff/task/StaffContractReminderTask.java b/src/main/java/com/ruoyi/staff/task/StaffContractReminderTask.java
new file mode 100644
index 0000000..c10712d
--- /dev/null
+++ b/src/main/java/com/ruoyi/staff/task/StaffContractReminderTask.java
@@ -0,0 +1,104 @@
+package com.ruoyi.staff.task;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.ruoyi.project.system.domain.SysUser;
+import com.ruoyi.project.system.mapper.SysUserMapper;
+import com.ruoyi.project.system.service.ISysNoticeService;
+import com.ruoyi.staff.pojo.StaffOnJob;
+import com.ruoyi.staff.mapper.StaffOnJobMapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 浜哄憳鍚堝悓鍒版湡鎻愰啋瀹氭椂浠诲姟
+ * 妫�鏌ュ嵆灏嗗埌鏈熺殑鍚堝悓锛屾帹閫侀�氱煡缁欑浉鍏冲憳宸�
+ *
+ * @author system
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class StaffContractReminderTask {
+
+    private final StaffOnJobMapper staffOnJobMapper;
+    private final SysUserMapper sysUserMapper;
+    private final ISysNoticeService sysNoticeService;
+
+    /**
+     * 姣忓ぉ鏃╀笂8鐐规墽琛屽悎鍚屽埌鏈熸彁閱�
+     * 鎻愬墠30澶┿��7澶┿��1澶╂彁閱�
+     */
+    @Scheduled(cron = "0 0 8 * * ?")
+    public void contractExpirationReminder() {
+        log.info("寮�濮嬫墽琛屽悎鍚屽埌鏈熸彁閱掍换鍔�...");
+        try {
+            LocalDate today = LocalDate.now();
+
+            // 鎻愬墠30澶╂彁閱�
+            sendReminder(today.plusDays(30), "30澶╁悗鍒版湡");
+
+            // 鎻愬墠7澶╂彁閱�
+            sendReminder(today.plusDays(7), "7澶╁悗鍒版湡");
+
+            // 鎻愬墠1澶╂彁閱�
+            sendReminder(today.plusDays(1), "1澶╁悗鍒版湡");
+
+            log.info("鍚堝悓鍒版湡鎻愰啋浠诲姟鎵ц瀹屾垚");
+        } catch (Exception e) {
+            log.error("鍚堝悓鍒版湡鎻愰啋浠诲姟鎵ц澶辫触锛歿}", e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 鍙戦�佸悎鍚屽埌鏈熸彁閱�
+     * @param expireDate 鍒版湡鏃ユ湡
+     * @param daysDesc 鍓╀綑澶╂暟鎻忚堪
+     */
+    private void sendReminder(LocalDate expireDate, String daysDesc) {
+        Date targetDate = Date.from(expireDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
+
+        // 鏌ヨ鍚堝悓鍒版湡鏃ユ湡绛変簬鐩爣鏃ユ湡鐨勫湪鑱屽憳宸�
+        List<StaffOnJob> staffList = staffOnJobMapper.selectList(Wrappers.<StaffOnJob>lambdaQuery()
+                .eq(StaffOnJob::getStaffState, 1)
+                .eq(StaffOnJob::getContractExpireTime, targetDate));
+
+        if (CollectionUtil.isEmpty(staffList)) {
+            return;
+        }
+
+        for (StaffOnJob staff : staffList) {
+            try {
+                // 閫氳繃鍛樺伐缂栧彿鏌ヨ绯荤粺鐢ㄦ埛
+                SysUser sysUser = sysUserMapper.selectUserByUserName(staff.getStaffNo());
+                if (sysUser == null) {
+                    log.warn("鍛樺伐 {} 鏈壘鍒板搴旂殑绯荤粺鐢ㄦ埛璐﹀彿", staff.getStaffName());
+                    continue;
+                }
+
+                // 鏋勫缓閫氱煡鍐呭
+                String title = "鍚堝悓鍒版湡鎻愰啋";
+                String message = String.format("鎮ㄥソ锛�%s 鐨勫悎鍚屽皢浜�%s鍒版湡锛岃鍙婃椂澶勭悊鐩稿叧浜嬪疁銆�",
+                        staff.getStaffName(), daysDesc);
+
+                // 鍙戦�侀�氱煡
+                List<Long> userIds = new ArrayList<>();
+                userIds.add(sysUser.getUserId());
+                sysNoticeService.simpleNoticeByUser(title, message, userIds, "/staffOnJob");
+
+                log.info("宸插悜鍛樺伐 {} 鍙戦�佸悎鍚屽埌鏈熸彁閱�", staff.getStaffName());
+            } catch (Exception e) {
+                log.error("鍚戝憳宸� {} 鍙戦�佸悎鍚屽埌鏈熸彁閱掑け璐ワ細{}", staff.getStaffName(), e.getMessage());
+            }
+        }
+    }
+}

--
Gitblit v1.9.3