From 7e6bc1e8b46469f589423277cf25ad9cd826cf66 Mon Sep 17 00:00:00 2001 From: Joshi <3040996759@qq.com> Date: Mon, 1 Jun 2026 14:38:56 +0800 Subject: [PATCH] =?UTF-8?q?fix(attendance):=20=E4=BC=98=E5=8C=96=E8=80=83?= =?UTF-8?q?=E5=8B=A4=E7=8A=B6=E6=80=81=E8=AF=86=E5=88=AB=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E5=B9=B6=E8=A1=A5=E5=85=85=E6=BC=8F=E6=89=93=E5=8D=A1=E5=A4=84?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 在考勤状态筛选、汇总卡片和图例中将“迟到/早退”标签更新为“迟到/早退/缺卡”,以包含漏打卡情况 2. 后端服务中,在考勤状态常量列表新增“missed_start”、“missed_end”、“missed”三种漏打卡状态 3. 重构上下班打卡时间判断逻辑:当打卡时间晚于理论上班时间或早于理论下班时间超过30分钟时,标记为漏打卡(上班漏打卡或下班漏打卡) 4. 调整状态覆盖逻辑:上班漏打卡或下班漏打卡状态会覆盖原有的迟到/早退状态;若上下班均漏打卡则标记为全天漏打卡(missed) 5. 更新考勤记录处理:当标记为漏打卡时,对应的首次或末次打卡时间字段设为null 6. 优化旷工判定逻辑:单独处理上午段(P1)或下午段(P2)漏打卡的情况,分别标记为半天旷工(absent_half) 7. 前端新增漏打卡状态对应的日历样式(状态色块为#e6a23c)和状态描述文本(“上班漏打卡”、“下班漏打卡”) --- .../wms/hrm/attendance/attendanceCheck.vue | 16 ++- .../impl/WmsAttendanceCheckServiceImpl.java | 110 +++++++++++------- 2 files changed, 82 insertions(+), 44 deletions(-) diff --git a/klp-ui/src/views/wms/hrm/attendance/attendanceCheck.vue b/klp-ui/src/views/wms/hrm/attendance/attendanceCheck.vue index 4bb16ad9..1716ea73 100644 --- a/klp-ui/src/views/wms/hrm/attendance/attendanceCheck.vue +++ b/klp-ui/src/views/wms/hrm/attendance/attendanceCheck.vue @@ -171,7 +171,7 @@ - + @@ -396,7 +396,7 @@ {{ currentPersonData.employeeName }} {{ personSummary.normal }}天 - {{ personSummary.abnormal }}天 + {{ personSummary.abnormal }}天 {{ personSummary.absentHalf }}天 {{ personSummary.absentFull }}天 {{ personSummary.noRecord }}天 @@ -456,7 +456,7 @@
正常 - 迟到/早退 + 迟到/早退/缺卡 半天旷工 全天旷工 无记录 @@ -957,6 +957,8 @@ export default { case 'absent_half': return 'status-absent-half' case 'absent_full': return 'status-absent-full' case 'abnormal': return 'status-abnormal' + case 'missed_start': return 'status-missed' + case 'missed_end': return 'status-missed' default: return 'status-default' } }, @@ -972,8 +974,10 @@ export default { case 'early_two': return '早退II' case 'absent_half': return '半天旷工' case 'absent_full': return '全天旷工' - case 'abnormal': return '迟到/早退' + case 'abnormal': return '迟到/早退/缺卡' case 'missed': return '未打卡' + case 'missed_start': return '上班漏打卡' + case 'missed_end': return '下班漏打卡' default: return '无' } }, @@ -1325,6 +1329,10 @@ export default { background-color: #c0c4cc; } +.status-missed { + background-color: #e6a23c; +} + .shift-name { font-size: 12px; color: #606266; diff --git a/klp-wms/src/main/java/com/klp/service/impl/WmsAttendanceCheckServiceImpl.java b/klp-wms/src/main/java/com/klp/service/impl/WmsAttendanceCheckServiceImpl.java index dfd4f3df..501dc961 100644 --- a/klp-wms/src/main/java/com/klp/service/impl/WmsAttendanceCheckServiceImpl.java +++ b/klp-wms/src/main/java/com/klp/service/impl/WmsAttendanceCheckServiceImpl.java @@ -49,7 +49,9 @@ import java.util.stream.Collectors; public class WmsAttendanceCheckServiceImpl implements IWmsAttendanceCheckService { private static final List STATUS_SEVERITY = java.util.Arrays.asList("normal", "late_warn", "early_warn", - "late_one", "early_one", "late_two", "early_two", "absent_half"); + "late_one", "early_one", "late_two", "early_two", "absent_half", "missed_start", "missed_end", "missed"); + + private static final long LATE_EARLY_MAX_SECONDS = 30 * 60L; private static final int BATCH_SIZE = 500; @@ -530,6 +532,7 @@ public class WmsAttendanceCheckServiceImpl implements IWmsAttendanceCheckService private void checkPeriod(WmsAttendanceCheck check, WmsAttendanceRule rule, int period, List periodRecords, Date expectedStart, Date expectedEnd) { + // 旷工:0条打卡记录 if (periodRecords.isEmpty()) { if (period == 1) { check.setP1Status("missed"); @@ -550,53 +553,72 @@ public class WmsAttendanceCheckServiceImpl implements IWmsAttendanceCheckService int earlyMinutes = 0; BigDecimal deduct = BigDecimal.ZERO; String status = "normal"; + boolean startMissed = false; + boolean endMissed = false; - if (expStart != null && firstCheck.isAfter(expStart)) { - lateMinutes = (int) Duration.between(expStart, firstCheck).toMinutes(); - if (lateMinutes > rule.getAbsentHalfDay()) { - status = "absent_half"; - } else if (lateMinutes > rule.getLateOne()) { - status = "late_two"; - deduct = deduct.add(rule.getDeductTwo()); - } else if (lateMinutes > rule.getLateWarn()) { - status = "late_one"; - deduct = deduct.add(rule.getDeductOne()); - } else { - status = "late_warn"; + // 上班检测:晚于理论上班时间30分钟以上 → 漏打卡 + if (expStart != null) { + long lateSecs = Duration.between(expStart, firstCheck).getSeconds(); + if (lateSecs > LATE_EARLY_MAX_SECONDS) { + startMissed = true; + } else if (lateSecs > 0) { + lateMinutes = (int) (lateSecs / 60); + if (lateMinutes > rule.getAbsentHalfDay()) { + status = "absent_half"; + } else if (lateMinutes > rule.getLateOne()) { + status = "late_two"; + deduct = deduct.add(rule.getDeductTwo()); + } else if (lateMinutes > rule.getLateWarn()) { + status = "late_one"; + deduct = deduct.add(rule.getDeductOne()); + } else { + status = "late_warn"; + } } } + // 下班检测:早于理论下班时间30分钟以上 → 漏打卡 if (expEnd != null && lastCheck.isBefore(expEnd)) { - int min = (int) Duration.between(lastCheck, expEnd).toMinutes(); - if (min > rule.getAbsentHalfDay()) { - status = maxSeverity(status, "absent_half"); - earlyMinutes = min; - } else if (min > rule.getLateOne()) { - status = maxSeverity(status, "early_two"); - deduct = deduct.add(rule.getDeductTwo()); - earlyMinutes = min; - } else if (min > rule.getLateWarn()) { - status = maxSeverity(status, "early_one"); - deduct = deduct.add(rule.getDeductOne()); - earlyMinutes = min; - } else { - if ("normal".equals(status)) { - status = "early_warn"; + long earlySecs = Duration.between(lastCheck, expEnd).getSeconds(); + if (earlySecs > LATE_EARLY_MAX_SECONDS) { + endMissed = true; + } else if (earlySecs > 0) { + earlyMinutes = (int) (earlySecs / 60); + if (earlyMinutes > rule.getAbsentHalfDay()) { + status = maxSeverity(status, "absent_half"); + } else if (earlyMinutes > rule.getLateOne()) { + status = maxSeverity(status, "early_two"); + deduct = deduct.add(rule.getDeductTwo()); + } else if (earlyMinutes > rule.getLateWarn()) { + status = maxSeverity(status, "early_one"); + deduct = deduct.add(rule.getDeductOne()); + } else { + if ("normal".equals(status)) { + status = "early_warn"; + } } - earlyMinutes = min; } } + // 上班漏打卡或下班漏打卡会覆盖原有状态 + if (startMissed && endMissed) { + status = "missed"; + } else if (startMissed) { + status = maxSeverity(status, "missed_start"); + } else if (endMissed) { + status = maxSeverity(status, "missed_end"); + } + if (period == 1) { - check.setP1FirstCheck(firstRec.getChecktime()); - check.setP1LastCheck(lastRec.getChecktime()); + check.setP1FirstCheck(startMissed ? null : firstRec.getChecktime()); + check.setP1LastCheck(endMissed ? null : lastRec.getChecktime()); check.setP1LateMinutes(lateMinutes); check.setP1EarlyMinutes(earlyMinutes); check.setP1Status(status); check.setP1Deduct(deduct); } else { - check.setP2FirstCheck(firstRec.getChecktime()); - check.setP2LastCheck(lastRec.getChecktime()); + check.setP2FirstCheck(startMissed ? null : firstRec.getChecktime()); + check.setP2LastCheck(endMissed ? null : lastRec.getChecktime()); check.setP2LateMinutes(lateMinutes); check.setP2EarlyMinutes(earlyMinutes); check.setP2Status(status); @@ -622,19 +644,27 @@ public class WmsAttendanceCheckServiceImpl implements IWmsAttendanceCheckService if ("absent_half".equals(check.getP1Status()) || "absent_half".equals(check.getP2Status())) { hasAbsentHalf = true; } - if ("missed".equals(check.getP1Status())) { - if (check.getP2StartTime() != null) { - if ("missed".equals(check.getP2Status())) { - check.setAbsentType("full_day"); - check.setOverallStatus("absent_full"); - return; - } + + boolean p1Missed = "missed".equals(check.getP1Status()); + boolean p2Missed = check.getP2Status() != null && "missed".equals(check.getP2Status()); + boolean hasP2 = check.getP2StartTime() != null; + + if (p1Missed) { + if (hasP2 && !p2Missed) { + check.setAbsentType("half_day"); + check.setOverallStatus("absent_half"); + return; } else { check.setAbsentType("full_day"); check.setOverallStatus("absent_full"); return; } } + if (p2Missed) { + check.setAbsentType("half_day"); + check.setOverallStatus("absent_half"); + return; + } if (check.getP1StartTime() == null && check.getP2StartTime() == null) { check.setAbsentType("full_day"); check.setOverallStatus("absent_full");