From 70aea4d9e226e32fcd96c39b9b55ae59fbbcb4a4 Mon Sep 17 00:00:00 2001
From: huminmin <mac@MacBook-Pro.local>
Date: 星期二, 10 二月 2026 15:48:08 +0800
Subject: [PATCH] 生成缺勤记录

---
 src/main/java/com/ruoyi/staff/pojo/PersonalAttendanceRecords.java                    |    6 +-
 src/main/java/com/ruoyi/staff/mapper/PersonalAttendanceRecordsMapper.java            |    5 +
 src/main/java/com/ruoyi/staff/service/impl/PersonalAttendanceRecordsServiceImpl.java |   75 +++++++++++++++++-------
 src/main/java/com/ruoyi/staff/task/PersonalAttendanceRecordsTask.java                |   76 +++++++++++++++++++++++++
 src/main/resources/mapper/staff/PersonalAttendanceRecordsMapper.xml                  |   13 ++++
 5 files changed, 149 insertions(+), 26 deletions(-)

diff --git a/src/main/java/com/ruoyi/staff/mapper/PersonalAttendanceRecordsMapper.java b/src/main/java/com/ruoyi/staff/mapper/PersonalAttendanceRecordsMapper.java
index 77ce68c..8abb915 100644
--- a/src/main/java/com/ruoyi/staff/mapper/PersonalAttendanceRecordsMapper.java
+++ b/src/main/java/com/ruoyi/staff/mapper/PersonalAttendanceRecordsMapper.java
@@ -10,6 +10,9 @@
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
+import java.time.LocalDate;
+import java.util.List;
+
 /**
  * <p>
  *  Mapper 鎺ュ彛
@@ -21,4 +24,6 @@
 @Mapper
 public interface PersonalAttendanceRecordsMapper extends BaseMapper<PersonalAttendanceRecords> {
     IPage<PersonalAttendanceRecordsDto> listPage(Page page, @Param("params") PersonalAttendanceRecordsDto personalAttendanceRecordsDto);
+
+    List<StaffOnJob> selectStaffWithoutAttendanceRecord(@Param("date") LocalDate date);
 }
diff --git a/src/main/java/com/ruoyi/staff/pojo/PersonalAttendanceRecords.java b/src/main/java/com/ruoyi/staff/pojo/PersonalAttendanceRecords.java
index b0dfb5b..853cd5c 100644
--- a/src/main/java/com/ruoyi/staff/pojo/PersonalAttendanceRecords.java
+++ b/src/main/java/com/ruoyi/staff/pojo/PersonalAttendanceRecords.java
@@ -62,9 +62,9 @@
     @Excel(name = "宸ユ椂(灏忔椂)", sort = 7)
     private BigDecimal workHours;
 
-    @ApiModelProperty("鐘舵�� 0姝e父 1杩熷埌 2鏃╅��")
-    @Excel(name = "鐘舵��", sort = 8,readConverterExp = "0=姝e父,1=杩熷埌,2=鏃╅��")
-    private Byte status;
+    @ApiModelProperty("鐘舵�� 0姝e父 1杩熷埌 2鏃╅�� 3杩熷埌鏃╅�� 4缂哄嫟")
+    @Excel(name = "鐘舵��", sort = 8,readConverterExp = "0=姝e父,1=杩熷埌,2=鏃╅��,3=杩熷埌鏃╅��,4=缂哄嫟")
+    private Integer status;
 
     @ApiModelProperty("澶囨敞")
     @Excel(name = "澶囨敞", sort = 9)
diff --git a/src/main/java/com/ruoyi/staff/service/impl/PersonalAttendanceRecordsServiceImpl.java b/src/main/java/com/ruoyi/staff/service/impl/PersonalAttendanceRecordsServiceImpl.java
index 5e0b5df..bcc3d5d 100644
--- a/src/main/java/com/ruoyi/staff/service/impl/PersonalAttendanceRecordsServiceImpl.java
+++ b/src/main/java/com/ruoyi/staff/service/impl/PersonalAttendanceRecordsServiceImpl.java
@@ -67,26 +67,43 @@
             throw new BaseException("褰撳墠鐢ㄦ埛娌℃湁瀵瑰簲鐨勫憳宸ヤ俊鎭�");
         }
 
+        // 褰撳墠鏃堕棿
+        LocalDateTime currentDateTime = LocalDateTime.now();
+
+        // 濡傛灉鎵撳崱鏃堕棿瓒呰繃鑰冨嫟涓嬬彮鏃堕棿涓嶈兘鎵撳崱
+        // 鑾峰彇鑰冨嫟涓嬬彮鏃堕棿鐐�
+        String[] timeConfigs = getAttendanceTimeConfig();
+        String timeConfig = timeConfigs[1];
+        String[] timeParts = timeConfig.split(":");
+        int standardHour = Integer.parseInt(timeParts[0]);
+        int standardMinute = Integer.parseInt(timeParts[1]);
+        // 褰撳墠鏃堕棿
+        int actualHour = currentDateTime.getHour();
+        int actualMinute = currentDateTime.getMinute();
+        // 鍒ゆ柇鎵撳崱鏃堕棿鏄惁鏅氫簬褰撳墠鏃堕棿
+        if (actualHour > standardHour || (actualHour == standardHour && actualMinute > standardMinute)) {
+            throw new BaseException("鎵撳崱鏃堕棿涓嶈兘鏅氫簬涓嬬彮鏃堕棿");
+        }
+
         // 鏍规嵁鍛樺伐ID鍜屽綋鍓嶆棩鏈熸煡璇㈡墦鍗¤褰�
         QueryWrapper<PersonalAttendanceRecords> attendanceQueryWrapper = new QueryWrapper<>();
         attendanceQueryWrapper.eq("staff_on_job_id", staffOnJob.getId())
                 .eq("date", currentDate);
         PersonalAttendanceRecords attendanceRecord = personalAttendanceRecordsMapper.selectOne(attendanceQueryWrapper);
-        DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm");
         // 鏍规嵁瀛楀吀璁剧疆鐨勮�冨嫟鏃堕棿鍒ゆ柇杩熷埌鏃╅��
         if (attendanceRecord == null) {
             // 涓嶅瓨鍦ㄦ墦鍗¤褰曪紝鍒涘缓鏂拌褰�
             personalAttendanceRecords.setStaffOnJobId(staffOnJob.getId());
             personalAttendanceRecords.setDate(currentDate);
-            personalAttendanceRecords.setWorkStartAt(LocalDateTime.now());
-            personalAttendanceRecords.setStatus(determineAttendanceStatus(personalAttendanceRecords.getWorkStartAt(), true));
+            personalAttendanceRecords.setWorkStartAt(currentDateTime);
+            personalAttendanceRecords.setStatus(determineAttendanceStatus(personalAttendanceRecords, true));
             personalAttendanceRecords.setRemark(personalAttendanceRecords.getRemark());
             personalAttendanceRecords.setTenantId(staffOnJob.getTenantId());
             return personalAttendanceRecordsMapper.insert(personalAttendanceRecords);
         } else {
             if (attendanceRecord.getWorkEndAt() == null) {
                 // 鏇存柊宸ヤ綔缁撴潫鏃堕棿鍜屽伐浣滄椂闀�
-                attendanceRecord.setWorkEndAt(LocalDateTime.now());
+                attendanceRecord.setWorkEndAt(currentDateTime);
                 // 璁$畻宸ヤ綔鏃堕暱锛堢簿纭埌鍒嗛挓锛屼繚鐣�2浣嶅皬鏁帮級
                 LocalDateTime startTime = attendanceRecord.getWorkStartAt();
                 LocalDateTime endTime = attendanceRecord.getWorkEndAt();
@@ -96,7 +113,7 @@
                         .divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP);
                 attendanceRecord.setWorkHours(workHours);
                 // 鏇存柊鑰冨嫟鐘舵��
-                attendanceRecord.setStatus(determineAttendanceStatus(attendanceRecord.getWorkEndAt(), false));
+                attendanceRecord.setStatus(determineAttendanceStatus(attendanceRecord, false));
                 return personalAttendanceRecordsMapper.updateById(attendanceRecord);
             } else {
                 throw new BaseException("鎮ㄥ凡缁忔墦杩囧崱浜�,鏃犻渶閲嶅鎵撳崱!!!");
@@ -104,27 +121,36 @@
         }
     }
 
+    // 鑾峰彇鑰冨嫟鏃堕棿閰嶇疆
+    private String[] getAttendanceTimeConfig() {
+        String[] timeConfigs = new String[2];
+        try {
+            String dictType = "sys_work_time";
+
+            // 鑾峰彇涓婄彮鏃堕棿閰嶇疆锛岄粯璁や负09:00
+            String startTimeConfig = dictDataService.selectDictLabel(dictType, "start_at");
+            timeConfigs[0] = (startTimeConfig == null || startTimeConfig.trim().isEmpty()) ? "09:00" : startTimeConfig;
+
+            // 鑾峰彇涓嬬彮鏃堕棿閰嶇疆锛岄粯璁や负18:00
+            String endTimeConfig = dictDataService.selectDictLabel(dictType, "end_at");
+            timeConfigs[1] = (endTimeConfig == null || endTimeConfig.trim().isEmpty()) ? "18:00" : endTimeConfig;
+
+            return timeConfigs;
+        } catch (Exception e) {
+            timeConfigs[0] = "09:00"; // 榛樿涓婄彮鏃堕棿
+            timeConfigs[1] = "18:00"; // 榛樿涓嬬彮鏃堕棿
+            return timeConfigs;
+        }
+    }
+
     // 鏍规嵁瀹為檯鏃堕棿鍜屾槸鍚︿笂鐝椂闂村垽鏂�冨嫟鐘舵��
-    // 0 姝e父 1 杩熷埌 2 鏃╅��
-    private byte determineAttendanceStatus(LocalDateTime actualTime, boolean isStart) {
+    // 0 姝e父 1 杩熷埌 2 鏃╅�� 3 杩熷埌鏃╅�� 4 缂哄嫟
+    private Integer determineAttendanceStatus(PersonalAttendanceRecords attendanceRecord, boolean isStart) {
+        LocalDateTime actualTime = isStart ? attendanceRecord.getWorkStartAt() : attendanceRecord.getWorkEndAt();
         try {
             // 鑾峰彇鑰冨嫟鏃堕棿閰嶇疆
-            String dictType = "sys_work_time"; // 鑰冨嫟鏃堕棿瀛楀吀绫诲瀷
-            String timeConfig;
-
-            if (isStart) {
-                // 涓婄彮鏃堕棿閰嶇疆锛岄粯璁や负09:00
-                timeConfig = dictDataService.selectDictLabel(dictType, "work_start_time");
-                if (timeConfig == null || timeConfig.trim().isEmpty()) {
-                    timeConfig = "09:00";
-                }
-            } else {
-                // 涓嬬彮鏃堕棿閰嶇疆锛岄粯璁や负18:00
-                timeConfig = dictDataService.selectDictLabel(dictType, "work_end_time");
-                if (timeConfig == null || timeConfig.trim().isEmpty()) {
-                    timeConfig = "18:00";
-                }
-            }
+            String[] timeConfigs = getAttendanceTimeConfig();
+            String timeConfig = isStart ? timeConfigs[0] : timeConfigs[1];
 
             // 瑙f瀽鏍囧噯鏃堕棿
             String[] timeParts = timeConfig.split(":");
@@ -144,6 +170,9 @@
             } else {
                 // 涓嬬彮鎵撳崱锛氭棭浜庢爣鍑嗘椂闂寸畻鏃╅��
                 if (actualHour < standardHour || (actualHour == standardHour && actualMinute < standardMinute)) {
+                    if (attendanceRecord.getStatus() == 1) {
+                        return 3; // 杩熷埌鏃╅��
+                    }
                     return 2; // 鏃╅��
                 }
             }
diff --git a/src/main/java/com/ruoyi/staff/task/PersonalAttendanceRecordsTask.java b/src/main/java/com/ruoyi/staff/task/PersonalAttendanceRecordsTask.java
new file mode 100644
index 0000000..b44b659
--- /dev/null
+++ b/src/main/java/com/ruoyi/staff/task/PersonalAttendanceRecordsTask.java
@@ -0,0 +1,76 @@
+package com.ruoyi.staff.task;
+
+import com.ruoyi.staff.pojo.PersonalAttendanceRecords;
+import com.ruoyi.staff.pojo.StaffOnJob;
+import com.ruoyi.staff.service.PersonalAttendanceRecordsService;
+import com.ruoyi.staff.mapper.PersonalAttendanceRecordsMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 涓汉鑰冨嫟璁板綍瀹氭椂浠诲姟
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-02-09
+ */
+@Slf4j
+@Component
+public class PersonalAttendanceRecordsTask {
+
+    @Autowired
+    private PersonalAttendanceRecordsMapper personalAttendanceRecordsMapper;
+
+    @Autowired
+    private PersonalAttendanceRecordsService personalAttendanceRecordsService;
+
+    /**
+     * 姣忓ぉ鍑屾櫒鐢熸垚鏄ㄦ棩鐨勭己鍕よ褰�
+     * 瀹氭椂浠诲姟锛氭瘡澶╁噷鏅�1鐐规墽琛�
+     * 鎺掗櫎浠婂ぉ鍒氬叆鑱岀殑鍛樺伐
+     */
+//    @Scheduled(cron = "0 0 1 * * ?")
+    @Scheduled(cron = "0/30 * * * * ?")
+    public void generateAbsenceRecords() {
+
+        try {
+            // 鑾峰彇鏄ㄦ棩鏃ユ湡
+            LocalDate yesterday = LocalDate.now().minusDays(1);
+            log.info("鐢熸垚鏃ユ湡锛歿} 鐨勭己鍕よ褰�", yesterday);
+
+            // 鐩存帴鏌ヨ鏄ㄥぉ娌℃湁鑰冨嫟璁板綍鐨勫湪鑱屽憳宸ワ紙鎺掗櫎浠婂ぉ鍒氬叆鑱岀殑锛�
+            List<StaffOnJob> staffWithoutAttendance = personalAttendanceRecordsMapper.selectStaffWithoutAttendanceRecord(yesterday);
+
+            // 閬嶅巻娌℃湁鑰冨嫟璁板綍鐨勫憳宸ワ紝鐢熸垚缂哄嫟璁板綍
+            for (StaffOnJob staff : staffWithoutAttendance) {
+                try {
+                    PersonalAttendanceRecords absenceRecord = new PersonalAttendanceRecords();
+                    absenceRecord.setStaffOnJobId(staff.getId());
+                    absenceRecord.setDate(yesterday);
+                    absenceRecord.setWorkStartAt(null);
+                    absenceRecord.setWorkEndAt(null);
+                    absenceRecord.setWorkHours(BigDecimal.ZERO);
+                    absenceRecord.setStatus(4); // 璁剧疆鐘舵�佷负缂哄嫟
+                    absenceRecord.setRemark("绯荤粺鑷姩鐢熸垚-缂哄嫟");
+                    absenceRecord.setTenantId(staff.getTenantId());
+                    absenceRecord.setCreateTime(LocalDateTime.now());
+                    absenceRecord.setUpdateTime(LocalDateTime.now());
+                    personalAttendanceRecordsService.save(absenceRecord);
+
+                } catch (Exception e) {
+                    log.error("涓哄憳宸}鐢熸垚缂哄嫟璁板綍澶辫触锛歿}", staff.getStaffName(), e.getMessage(), e);
+                }
+            }
+
+            log.info("鏄ㄦ棩缂哄嫟璁板綍鐢熸垚瀹屾垚");
+        } catch (Exception e) {
+            log.error("鐢熸垚鏄ㄦ棩缂哄嫟璁板綍浠诲姟鎵ц澶辫触锛歿}", e.getMessage(), e);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/resources/mapper/staff/PersonalAttendanceRecordsMapper.xml b/src/main/resources/mapper/staff/PersonalAttendanceRecordsMapper.xml
index 0e3c2c6..5f423e8 100644
--- a/src/main/resources/mapper/staff/PersonalAttendanceRecordsMapper.xml
+++ b/src/main/resources/mapper/staff/PersonalAttendanceRecordsMapper.xml
@@ -40,4 +40,17 @@
             and personal_attendance_records.date &lt; DATE_ADD(DATE(#{params.date}), INTERVAL 1 DAY)
         </if>
     </select>
+
+    <select id="selectStaffWithoutAttendanceRecord" resultType="com.ruoyi.staff.pojo.StaffOnJob">
+        SELECT soj.*
+        FROM staff_on_job soj
+        WHERE soj.status = 1
+        AND DATE(soj.create_time) &lt; #{date}
+        AND NOT EXISTS (
+        SELECT 1
+        FROM personal_attendance_records par
+        WHERE par.staff_on_job_id = soj.id
+        AND par.date = #{date}
+        )
+    </select>
 </mapper>

--
Gitblit v1.9.3