From 82155dd6f2b16fbfb718e636a54e2c9dae31246b Mon Sep 17 00:00:00 2001
From: liu <2021943741@qq.com>
Date: 星期五, 30 十月 2026 20:23:20 +0800
Subject: [PATCH] feat(hrm,bi): 移植长辉电力的考勤请假联动与 BI 大屏图表
---
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceSummaryServiceImpl.java | 128 +++++++++++++++++++++++++++++++++++++-----
1 files changed, 113 insertions(+), 15 deletions(-)
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceSummaryServiceImpl.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceSummaryServiceImpl.java
index 67eb1ef..9e05c94 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceSummaryServiceImpl.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceSummaryServiceImpl.java
@@ -5,11 +5,13 @@
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceSummaryPageReqVO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmAttendanceRecordDO;
+import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmAttendanceRuleDO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmAttendanceSummaryDO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeDO;
import cn.iocoder.yudao.module.hrm.dal.mysql.attendance.HrmAttendanceRecordMapper;
import cn.iocoder.yudao.module.hrm.dal.mysql.attendance.HrmAttendanceSummaryMapper;
import cn.iocoder.yudao.module.hrm.enums.HrmAttendanceClockTypeEnum;
+import cn.iocoder.yudao.module.hrm.enums.HrmAuditStatusEnum;
import cn.iocoder.yudao.module.hrm.service.employee.HrmEmployeeService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
@@ -19,6 +21,7 @@
import java.math.BigDecimal;
import java.time.LocalDate;
+import java.time.LocalDateTime;
import java.time.YearMonth;
import java.util.List;
@@ -40,6 +43,10 @@
private HrmEmployeeService employeeService;
@Resource
private HrmAttendanceHolidayService attendanceHolidayService;
+ @Resource
+ private cn.iocoder.yudao.module.hrm.dal.mysql.approval.HrmLeaveApplicationMapper leaveApplicationMapper;
+ @Resource
+ private HrmAttendanceRuleService attendanceRuleService;
@Override
@Transactional(rollbackFor = Exception.class)
@@ -59,6 +66,14 @@
// 3. 鏌ヨ璇ユ湀鎵�鏈夎�冨嫟璁板綍
List<HrmAttendanceRecordDO> records = attendanceRecordMapper.selectListByUserIdAndDateBetween(userId, startDate, endDate);
+ // 3.1 鏌ヨ璇ユ湀鎵�鏈夊鎵归�氳繃鐨勮鍋囧崟
+ // 鑰冨嫟璁板綍鍙湪銆屾墦鍗°�嶆椂鎵嶅垱寤猴紝璇峰亣褰撳ぉ娌℃潵涓婄彮灏变笉浼氭湁璁板綍锛�
+ // 鎵�浠ュ繀椤诲崟鐙煡璇峰亣鍗曪紝鍚﹀垯 leaveHours 浼氭紡绠椼��
+ List<cn.iocoder.yudao.module.hrm.dal.dataobject.approval.HrmLeaveApplicationDO> monthLeaves =
+ leaveApplicationMapper.selectListByUserIdAndOverlapDate(
+ userId, HrmAuditStatusEnum.APPROVE.getStatus(),
+ startDate.atStartOfDay(), endDate.plusDays(1).atStartOfDay());
+
// 4. 璁$畻搴斿嚭鍕ゅぉ鏁帮紙宸ヤ綔鏃ユ暟閲忥級
int workDays = calculateWorkDays(startDate, endDate);
@@ -67,31 +82,76 @@
int earlyCount = 0;
int absentCount = 0;
BigDecimal overtimeHours = BigDecimal.ZERO;
+ BigDecimal leaveHours = BigDecimal.ZERO;
int actualDays = 0;
for (HrmAttendanceRecordDO record : records) {
- // 缁熻杩熷埌
- if (HrmAttendanceClockTypeEnum.LATE.getType().equals(record.getClockInType())) {
- lateCount++;
+ // 璇峰亣鏍囪锛氬鎵归�氳繃鐨勮鍋囧崟钀藉簱鏃跺凡鍐欏叆 record.leaveFlag
+ boolean onLeave = Boolean.TRUE.equals(record.getLeaveFlag());
+ // 绱璇峰亣宸ユ椂
+ if (onLeave && record.getLeaveHours() != null) {
+ leaveHours = leaveHours.add(record.getLeaveHours());
}
- // 缁熻鏃╅��
- if (HrmAttendanceClockTypeEnum.EARLY.getType().equals(record.getClockOutType())) {
- earlyCount++;
- }
- // 缁熻缂哄崱
- if (HrmAttendanceClockTypeEnum.MISSING.getType().equals(record.getClockInType())) {
- absentCount++;
- }
- if (HrmAttendanceClockTypeEnum.MISSING.getType().equals(record.getClockOutType())) {
- absentCount++;
+ // 璇峰亣褰撳ぉ涓嶈杩熷埌銆佹棭閫�銆佺己鍗★紙璇峰亣绫诲瀷鏈韩鏄� LEAVE锛屼笉浼氬懡涓笅闈㈢殑鍒ゆ柇锛�
+ if (!onLeave) {
+ // 缁熻杩熷埌
+ if (HrmAttendanceClockTypeEnum.LATE.getType().equals(record.getClockInType())) {
+ lateCount++;
+ }
+ // 缁熻鏃╅��
+ if (HrmAttendanceClockTypeEnum.EARLY.getType().equals(record.getClockOutType())) {
+ earlyCount++;
+ }
+ // 缁熻缂哄崱锛氳鍋囧綋澶╂病鎵撳崱灞炴甯革紝涓嶇畻缂哄崱
+ if (HrmAttendanceClockTypeEnum.MISSING.getType().equals(record.getClockInType())) {
+ absentCount++;
+ }
+ if (HrmAttendanceClockTypeEnum.MISSING.getType().equals(record.getClockOutType())) {
+ absentCount++;
+ }
}
// 绱鍔犵彮鏃堕暱
if (record.getOvertimeHours() != null) {
overtimeHours = overtimeHours.add(record.getOvertimeHours());
}
- // 缁熻瀹為檯鍑哄嫟澶╂暟锛堟湁姝e父鎵撳崱璁板綍锛�
+ // 缁熻瀹為檯鍑哄嫟澶╂暟锛堟湁鎵撳崱璁板綍鍗崇畻锛涚函璇峰亣鏈墦鍗′笉绠楀嚭鍕わ級
if (record.getClockInTime() != null || record.getClockOutTime() != null) {
actualDays++;
+ }
+ }
+
+ // 5.1 琛ョ畻銆屾湁璇峰亣鍗曚絾娌℃湁鑰冨嫟璁板綍銆嶇殑鏃ュ瓙
+ // 杩欑被鎯呭喌鏄鍋囧綋澶╁畬鍏ㄦ病鏉ユ墦鍗★紝涓嶄細鐢熸垚 record锛�
+ // 涓婇潰寰幆瑕嗙洊涓嶅埌锛岄渶瑕佸崟鐙寜璇峰亣鍗曠疮璁″伐鏃躲��
+ if (!monthLeaves.isEmpty()) {
+ java.util.Set<LocalDate> recordDates = records.stream()
+ .map(HrmAttendanceRecordDO::getDate).collect(java.util.stream.Collectors.toSet());
+ HrmAttendanceRuleDO rule = attendanceRuleService.getAttendanceRuleByDeptId(employee.getDeptId());
+ // 鎸夊ぉ褰掔粍璇峰亣鍗�
+ java.util.Map<LocalDate, List<cn.iocoder.yudao.module.hrm.dal.dataobject.approval.HrmLeaveApplicationDO>> byDate
+ = new java.util.LinkedHashMap<>();
+ for (var leave : monthLeaves) {
+ if (leave.getStartTime() == null || leave.getEndTime() == null) {
+ continue;
+ }
+ LocalDate d = leave.getStartTime().toLocalDate();
+ LocalDate last = leave.getEndTime().toLocalDate();
+ if (last.isAfter(endDate)) {
+ last = endDate;
+ }
+ for (LocalDate cur = d; !cur.isAfter(last); cur = cur.plusDays(1)) {
+ if (cur.isBefore(startDate)) {
+ continue;
+ }
+ byDate.computeIfAbsent(cur, k -> new java.util.ArrayList<>()).add(leave);
+ }
+ }
+ for (var entry : byDate.entrySet()) {
+ // 宸叉湁鑰冨嫟璁板綍鐨勬棩鏈燂紝璇峰亣宸ユ椂宸插湪 record.leaveHours 閲岀畻杩�
+ if (recordDates.contains(entry.getKey())) {
+ continue;
+ }
+ leaveHours = leaveHours.add(calcLeaveHoursByDay(entry.getValue(), rule, entry.getKey()));
}
}
@@ -108,7 +168,7 @@
.earlyCount(earlyCount)
.absentCount(absentCount)
.overtimeHours(overtimeHours)
- .leaveHours(BigDecimal.ZERO)
+ .leaveHours(leaveHours)
.build();
attendanceSummaryMapper.insert(summary);
} else {
@@ -118,6 +178,7 @@
summary.setEarlyCount(earlyCount);
summary.setAbsentCount(absentCount);
summary.setOvertimeHours(overtimeHours);
+ summary.setLeaveHours(leaveHours); // 璇峰亣宸ユ椂涔熻鏇存柊锛屽惁鍒欓噸绠楁椂姘歌繙鍋滃湪涓婁竴娆�
attendanceSummaryMapper.updateById(summary);
}
}
@@ -155,6 +216,43 @@
/**
* 璁$畻搴斿嚭鍕ゅぉ鏁帮紙宸ヤ綔鏃ユ暟閲忥級
*/
+ /**
+ * 璁$畻鏌愪竴澶╃殑璇峰亣宸ユ椂锛堜笌鑰冨嫟璁板綍閲岀殑绠楁硶淇濇寔涓�鑷达級
+ *
+ * <p>鍙栨瘡寮犺鍋囧崟涓庤�冨嫟瑙勫垯宸ヤ綔鏃舵鐨勪氦闆嗙疮鍔狅紝绮剧‘鍒� 0.01 灏忔椂銆�</p>
+ */
+ private BigDecimal calcLeaveHoursByDay(
+ List<cn.iocoder.yudao.module.hrm.dal.dataobject.approval.HrmLeaveApplicationDO> leaves,
+ HrmAttendanceRuleDO rule, LocalDate date) {
+ LocalDateTime workStart;
+ LocalDateTime workEnd;
+ if (rule != null && rule.getWorkStartTime() != null && rule.getWorkEndTime() != null) {
+ workStart = LocalDateTime.of(date, rule.getWorkStartTime());
+ workEnd = LocalDateTime.of(date, rule.getWorkEndTime());
+ if (!workEnd.isAfter(workStart)) {
+ workEnd = workEnd.plusDays(1);
+ }
+ } else {
+ workStart = date.atStartOfDay();
+ workEnd = date.plusDays(1).atStartOfDay();
+ }
+ BigDecimal total = BigDecimal.ZERO;
+ for (var leave : leaves) {
+ if (leave.getStartTime() == null || leave.getEndTime() == null) {
+ continue;
+ }
+ LocalDateTime from = leave.getStartTime().isAfter(workStart) ? leave.getStartTime() : workStart;
+ LocalDateTime to = leave.getEndTime().isBefore(workEnd) ? leave.getEndTime() : workEnd;
+ if (!to.isAfter(from)) {
+ continue;
+ }
+ long minutes = java.time.Duration.between(from, to).toMinutes();
+ total = total.add(BigDecimal.valueOf(minutes)
+ .divide(BigDecimal.valueOf(60), 2, java.math.RoundingMode.HALF_UP));
+ }
+ return total;
+ }
+
private int calculateWorkDays(LocalDate startDate, LocalDate endDate) {
int workDays = 0;
LocalDate current = startDate;
--
Gitblit v1.9.3