From ef3cbc1c6882786e8218b08be3e52ea44587f03b Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期日, 08 十一月 2026 09:45:42 +0800
Subject: [PATCH] feat(hrm): 请假批量导入、加班代申请人、批量排班与节假日区间联动
---
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmEmployeeScheduleServiceImpl.java | 134 ++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 133 insertions(+), 1 deletions(-)
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmEmployeeScheduleServiceImpl.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmEmployeeScheduleServiceImpl.java
index 36eb169..2c597c6 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmEmployeeScheduleServiceImpl.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmEmployeeScheduleServiceImpl.java
@@ -1,21 +1,30 @@
package cn.iocoder.yudao.module.hrm.service.attendance;
import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmEmployeeScheduleBatchSaveReqVO;
+import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmEmployeeScheduleBatchSaveRespVO;
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmEmployeeScheduleListReqVO;
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmEmployeeScheduleRespVO;
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmEmployeeScheduleSaveReqVO;
+import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmAttendanceHolidayDO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmEmployeeScheduleDO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmShiftDO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeDO;
+import cn.iocoder.yudao.module.hrm.enums.HrmAttendanceHolidayTypeEnum;
import cn.iocoder.yudao.module.hrm.dal.mysql.attendance.HrmEmployeeScheduleMapper;
import cn.iocoder.yudao.module.hrm.dal.mysql.attendance.HrmShiftMapper;
import cn.iocoder.yudao.module.hrm.dal.mysql.employee.HrmEmployeeMapper;
+import cn.iocoder.yudao.module.system.api.dept.DeptApi;
+import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
+import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.YearMonth;
import java.util.*;
@@ -40,6 +49,10 @@
private HrmEmployeeMapper employeeMapper;
@Resource
private HrmShiftMapper shiftMapper;
+ @Resource
+ private DeptApi deptApi;
+ @Resource
+ private HrmAttendanceHolidayService attendanceHolidayService;
@Override
public List<HrmEmployeeScheduleRespVO> getScheduleList(HrmEmployeeScheduleListReqVO reqVO) {
@@ -52,7 +65,9 @@
if (reqVO.getEmployeeId() != null) {
list = employeeScheduleMapper.selectListByEmployeeIdAndPeriod(reqVO.getEmployeeId(), startDate, endDate);
} else if (reqVO.getDeptId() != null) {
- List<HrmEmployeeDO> employees = employeeMapper.selectByDeptId(reqVO.getDeptId());
+ // 涓庡憳宸ュ垎椤典繚鎸佷竴鑷达細閫変腑閮ㄩ棬鏃惰繛甯︽煡璇㈡墍鏈変笅绾ч儴闂ㄧ殑鍛樺伐
+ Set<Long> deptIds = getSelfAndChildDeptIds(reqVO.getDeptId());
+ List<HrmEmployeeDO> employees = employeeMapper.selectListByDeptIds(deptIds);
Set<Long> employeeIds = employees.stream().map(HrmEmployeeDO::getId).collect(Collectors.toSet());
list = CollUtil.isEmpty(employeeIds) ? Collections.emptyList()
: employeeScheduleMapper.selectListByEmployeeIdsAndPeriod(employeeIds, startDate, endDate);
@@ -140,4 +155,121 @@
}
}
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public HrmEmployeeScheduleBatchSaveRespVO batchSaveSchedule(HrmEmployeeScheduleBatchSaveReqVO reqVO) {
+ // 1. 鏍¢獙鏃ユ湡鑼冨洿鍜岀彮娆�
+ if (reqVO.getEndDate().isBefore(reqVO.getStartDate())) {
+ throw exception(SCHEDULE_BATCH_DATE_RANGE_INVALID);
+ }
+ if (reqVO.getShiftId() != null && shiftMapper.selectById(reqVO.getShiftId()) == null) {
+ throw exception(SCHEDULE_SHIFT_NOT_EXISTS);
+ }
+ // 2. 鎸夐儴闂紙鍚笅绾э級+ 宀椾綅 + 鍛樺伐鐘舵�佺瓫閫夌洰鏍囧憳宸�
+ Set<Long> deptIds = getSelfAndChildDeptIds(reqVO.getDeptId());
+ List<HrmEmployeeDO> employees = employeeMapper.selectListByDeptIds(deptIds).stream()
+ .filter(employee -> matchEmployeeStatus(employee, reqVO.getEmployeeStatus()))
+ .filter(employee -> matchPost(employee, reqVO.getPostId()))
+ .collect(Collectors.toList());
+ if (CollUtil.isEmpty(employees)) {
+ DeptRespDTO dept = deptApi.getDept(reqVO.getDeptId());
+ throw exception(SCHEDULE_BATCH_EMPLOYEE_EMPTY, dept == null ? String.valueOf(reqVO.getDeptId()) : dept.getName());
+ }
+
+ // 3. 棰勫姞杞借寖鍥村唴宸叉湁鎺掔彮锛岄伩鍏嶉�愭潯鏌ヨ
+ Set<Long> employeeIds = employees.stream().map(HrmEmployeeDO::getId).collect(Collectors.toSet());
+ Map<String, HrmEmployeeScheduleDO> existingMap = employeeScheduleMapper
+ .selectListByEmployeeIdsAndPeriod(employeeIds, reqVO.getStartDate(), reqVO.getEndDate())
+ .stream()
+ .collect(Collectors.toMap(item -> scheduleKey(item.getEmployeeId(), item.getDate()),
+ Function.identity(), (a, b) -> a));
+
+ // 4. 棰勫姞杞藉尯闂村唴鑺傚亣鏃ラ厤缃細閰嶇疆涓轰紤鎭棩鐨勫綋澶╁己鍒朵紤鎭紝閰嶇疆涓哄伐浣滄棩鐨勫綋澶╁己鍒舵帓鐝紙鍙鐩栧懆鏈級
+ Map<LocalDate, Integer> holidayTypeMap = attendanceHolidayService
+ .getAttendanceHolidayListByDateRange(reqVO.getStartDate(), reqVO.getEndDate())
+ .stream()
+ .collect(Collectors.toMap(HrmAttendanceHolidayDO::getDate, HrmAttendanceHolidayDO::getType,
+ (a, b) -> a));
+
+ // 5. 鎸夋棩鏈熼�愬ぉ鐢熸垚鎺掔彮锛氫紤鎭棩鍒犻櫎鍘熺彮娆★紝宸ヤ綔鏃� upsert
+ int itemCount = 0;
+ for (LocalDate date = reqVO.getStartDate(); !date.isAfter(reqVO.getEndDate()); date = date.plusDays(1)) {
+ boolean rest = isRestDay(date, reqVO.getRestMode(), holidayTypeMap.get(date))
+ || reqVO.getShiftId() == null;
+ for (HrmEmployeeDO employee : employees) {
+ HrmEmployeeScheduleDO existing = existingMap.get(scheduleKey(employee.getId(), date));
+ if (rest) {
+ if (existing != null) {
+ employeeScheduleMapper.deleteById(existing.getId());
+ itemCount++;
+ }
+ continue;
+ }
+ if (existing == null) {
+ HrmEmployeeScheduleDO schedule = new HrmEmployeeScheduleDO();
+ schedule.setEmployeeId(employee.getId());
+ schedule.setDate(date);
+ schedule.setShiftId(reqVO.getShiftId());
+ employeeScheduleMapper.insert(schedule);
+ itemCount++;
+ } else if (!Objects.equals(existing.getShiftId(), reqVO.getShiftId())) {
+ existing.setShiftId(reqVO.getShiftId());
+ employeeScheduleMapper.updateById(existing);
+ itemCount++;
+ }
+ }
+ }
+
+ HrmEmployeeScheduleBatchSaveRespVO respVO = new HrmEmployeeScheduleBatchSaveRespVO();
+ respVO.setEmployeeCount(employees.size());
+ respVO.setItemCount(itemCount);
+ return respVO;
+ }
+
+ private Set<Long> getSelfAndChildDeptIds(Long deptId) {
+ Set<Long> deptIds = new LinkedHashSet<>();
+ deptIds.add(deptId);
+ List<DeptRespDTO> childDepts = deptApi.getChildDeptList(deptId);
+ if (CollUtil.isNotEmpty(childDepts)) {
+ childDepts.forEach(dept -> deptIds.add(dept.getId()));
+ }
+ return deptIds;
+ }
+
+ /** employeeStatus 涓虹┖鏃跺寘鍚湪鑱屽拰璇曠敤锛屾帓闄ょ鑱� */
+ private boolean matchEmployeeStatus(HrmEmployeeDO employee, Integer employeeStatus) {
+ if (employeeStatus != null) {
+ return Objects.equals(employee.getEmployeeStatus(), employeeStatus);
+ }
+ return !Objects.equals(employee.getEmployeeStatus(), 3);
+ }
+
+ private boolean matchPost(HrmEmployeeDO employee, Long postId) {
+ if (postId == null) {
+ return true;
+ }
+ if (StrUtil.isBlank(employee.getPostIds())) {
+ return false;
+ }
+ return JSONUtil.toList(employee.getPostIds(), Long.class).contains(postId);
+ }
+
+ private boolean isRestDay(LocalDate date, String restMode, Integer holidayType) {
+ // 鑺傚亣鏃ラ厤缃紭鍏堬細浼戞伅鏃ュ己鍒朵紤鎭紝宸ヤ綔鏃ュ己鍒朵笂鐝紙鍚皟浼戜笂鐝殑鍛ㄦ湯锛�
+ if (holidayType != null) {
+ return HrmAttendanceHolidayTypeEnum.REST_DAY.getType().equals(holidayType);
+ }
+ if ("double".equals(restMode)) {
+ return date.getDayOfWeek() == DayOfWeek.SATURDAY || date.getDayOfWeek() == DayOfWeek.SUNDAY;
+ }
+ if ("single".equals(restMode)) {
+ return date.getDayOfWeek() == DayOfWeek.SUNDAY;
+ }
+ return false;
+ }
+
+ private String scheduleKey(Long employeeId, LocalDate date) {
+ return employeeId + "_" + date;
+ }
+
}
--
Gitblit v1.9.3