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/HrmAttendanceHolidayServiceImpl.java |  148 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 146 insertions(+), 2 deletions(-)

diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceHolidayServiceImpl.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceHolidayServiceImpl.java
index 9b9a63d..d7541c5 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceHolidayServiceImpl.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceHolidayServiceImpl.java
@@ -1,19 +1,29 @@
 package cn.iocoder.yudao.module.hrm.service.attendance;
 
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceHolidayGroupRespVO;
 import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceHolidayPageReqVO;
+import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceHolidayRangeCreateReqVO;
+import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceHolidayRangeUpdateReqVO;
 import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceHolidaySaveReqVO;
 import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmAttendanceHolidayDO;
 import cn.iocoder.yudao.module.hrm.dal.mysql.attendance.HrmAttendanceHolidayMapper;
 import cn.iocoder.yudao.module.hrm.enums.HrmAttendanceHolidayTypeEnum;
 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.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
+import java.util.Objects;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.*;
@@ -42,6 +52,42 @@
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Integer createAttendanceHolidayRange(HrmAttendanceHolidayRangeCreateReqVO createReqVO) {
+        if (createReqVO.getEndDate().isBefore(createReqVO.getStartDate())) {
+            throw exception(ATTENDANCE_HOLIDAY_DATE_RANGE_INVALID);
+        }
+        return insertDays(createReqVO.getName(), createReqVO.getStartDate(), createReqVO.getEndDate(),
+                createReqVO.getType(), createReqVO.getIsLegal(), createReqVO.getRemark());
+    }
+
+    /**
+     * 鎸夊ぉ鍐欏叆鑺傚亣鏃ワ紱鍐欏叆鍓嶆暣浣撴煡閲嶏紝浠讳竴鏃ユ湡宸插瓨鍦ㄥ垯鏁翠綋涓嶅啓鍏�
+     */
+    private int insertDays(String name, LocalDate startDate, LocalDate endDate,
+                           Integer type, Boolean isLegal, String remark) {
+        // 鍏堟暣浣撴牎楠岋紝鍖洪棿鍐呭凡瀛樺湪浠讳竴鏃ユ湡鏃朵笉鍋氶儴鍒嗗啓鍏�
+        List<HrmAttendanceHolidayDO> existingList = attendanceHolidayMapper.selectListByDateBetween(startDate, endDate);
+        if (!existingList.isEmpty()) {
+            HrmAttendanceHolidayDO existing = existingList.get(0);
+            throw exception(ATTENDANCE_HOLIDAY_RANGE_DATE_EXISTS, existing.getDate(), existing.getName());
+        }
+        int count = 0;
+        for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) {
+            HrmAttendanceHolidayDO attendanceHoliday = HrmAttendanceHolidayDO.builder()
+                    .name(name)
+                    .date(date)
+                    .type(type)
+                    .isLegal(Boolean.TRUE.equals(isLegal) ? 1 : 0)
+                    .remark(remark)
+                    .build();
+            attendanceHolidayMapper.insert(attendanceHoliday);
+            count++;
+        }
+        return count;
+    }
+
+    @Override
     public void updateAttendanceHoliday(HrmAttendanceHolidaySaveReqVO updateReqVO) {
         validateAttendanceHolidayExists(updateReqVO.getId());
         // 濡傛灉鏃ユ湡鍙樻洿锛屾牎楠屾柊鏃ユ湡鏄惁宸插瓨鍦ㄩ厤缃�
@@ -55,9 +101,41 @@
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateAttendanceHolidayRange(HrmAttendanceHolidayRangeUpdateReqVO updateReqVO) {
+        // 1. 鏍¢獙鏃堕棿鑼冨洿涓庡尯闂磋褰�
+        if (updateReqVO.getEndDate().isBefore(updateReqVO.getStartDate())) {
+            throw exception(ATTENDANCE_HOLIDAY_DATE_RANGE_INVALID);
+        }
+        List<Long> ids = updateReqVO.getIds().stream().filter(Objects::nonNull).distinct().toList();
+        if (ids.isEmpty()) {
+            throw exception(ATTENDANCE_HOLIDAY_RANGE_IDS_EMPTY);
+        }
+        List<HrmAttendanceHolidayDO> oldList = attendanceHolidayMapper.selectBatchIds(ids);
+        if (oldList.size() != ids.size()) {
+            throw exception(ATTENDANCE_HOLIDAY_RANGE_NOT_EXISTS);
+        }
+        // 2. 鍒犻櫎鍖洪棿鍐呭師璁板綍锛屽啀鎸夋柊鐨勮捣姝㈡棩鏈熸暣浣撻噸鍐欙紙鍚屼竴浜嬪姟锛屽紓甯镐細鍥炴粴锛�
+        attendanceHolidayMapper.physicalDeleteByIds(ids);
+        insertDays(updateReqVO.getName(), updateReqVO.getStartDate(), updateReqVO.getEndDate(),
+                updateReqVO.getType(), updateReqVO.getIsLegal(), updateReqVO.getRemark());
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void deleteAttendanceHolidayRange(List<Long> ids) {
+        List<Long> cleanIds = ids == null ? Collections.emptyList()
+                : ids.stream().filter(Objects::nonNull).distinct().toList();
+        if (cleanIds.isEmpty()) {
+            throw exception(ATTENDANCE_HOLIDAY_RANGE_IDS_EMPTY);
+        }
+        attendanceHolidayMapper.physicalDeleteByIds(cleanIds);
+    }
+
+    @Override
     public void deleteAttendanceHoliday(Long id) {
         validateAttendanceHolidayExists(id);
-        attendanceHolidayMapper.deleteById(id);
+        attendanceHolidayMapper.physicalDeleteByIds(Collections.singletonList(id));
     }
 
     private void validateAttendanceHolidayExists(Long id) {
@@ -83,6 +161,72 @@
     }
 
     @Override
+    public PageResult<HrmAttendanceHolidayGroupRespVO> getAttendanceHolidayGroupPage(
+            HrmAttendanceHolidayPageReqVO pageReqVO) {
+        List<HrmAttendanceHolidayDO> list = attendanceHolidayMapper.selectListByCondition(pageReqVO);
+        if (list.isEmpty()) {
+            return new PageResult<>(Collections.emptyList(), 0L);
+        }
+        // 1. 杩炵画鏃ユ湡 + 鍚嶇О/绫诲瀷/鏄惁娉曞畾/澶囨敞涓�鑷寸殑璁板綍鍚堝苟涓轰竴琛�
+        List<HrmAttendanceHolidayGroupRespVO> groups = new ArrayList<>();
+        HrmAttendanceHolidayDO previous = null;
+        for (HrmAttendanceHolidayDO current : list) {
+            HrmAttendanceHolidayGroupRespVO last = groups.isEmpty() ? null : groups.get(groups.size() - 1);
+            boolean sameGroup = last != null && previous != null
+                    && previous.getDate().plusDays(1).equals(current.getDate())
+                    && sameHoliday(previous, current);
+            if (sameGroup) {
+                last.setEndDate(current.getDate());
+                last.getIds().add(current.getId());
+                last.setDayCount(last.getDayCount() + 1);
+            } else {
+                HrmAttendanceHolidayGroupRespVO group = new HrmAttendanceHolidayGroupRespVO();
+                group.setId(current.getId());
+                List<Long> ids = new ArrayList<>();
+                ids.add(current.getId());
+                group.setIds(ids);
+                group.setName(current.getName());
+                group.setDate(current.getDate());
+                group.setEndDate(current.getDate());
+                group.setDayCount(1);
+                group.setType(current.getType());
+                group.setIsLegal(current.getIsLegal());
+                group.setRemark(current.getRemark());
+                group.setCreateTime(current.getCreateTime());
+                groups.add(group);
+            }
+            previous = current;
+        }
+        // 2. 鎸夋棩鏈熷�掑簭锛屼笌閫愭棩鍒楄〃淇濇寔涓�鑷�
+        groups.sort(Comparator.comparing(HrmAttendanceHolidayGroupRespVO::getDate).reversed());
+        // 3. 鍒嗙粍鍚庡唴瀛樺垎椤�
+        return pageGroups(groups, pageReqVO);
+    }
+
+    private boolean sameHoliday(HrmAttendanceHolidayDO a, HrmAttendanceHolidayDO b) {
+        return StrUtil.equals(a.getName(), b.getName())
+                && Objects.equals(a.getType(), b.getType())
+                && Objects.equals(a.getIsLegal(), b.getIsLegal())
+                && StrUtil.equals(a.getRemark(), b.getRemark());
+    }
+
+    private PageResult<HrmAttendanceHolidayGroupRespVO> pageGroups(
+            List<HrmAttendanceHolidayGroupRespVO> groups, HrmAttendanceHolidayPageReqVO pageReqVO) {
+        long total = groups.size();
+        if (Objects.equals(PageParam.PAGE_SIZE_NONE, pageReqVO.getPageSize())) {
+            return new PageResult<>(groups, total);
+        }
+        int pageNo = pageReqVO.getPageNo() == null ? 1 : pageReqVO.getPageNo();
+        int pageSize = pageReqVO.getPageSize() == null ? 10 : pageReqVO.getPageSize();
+        int from = Math.max(0, (pageNo - 1) * pageSize);
+        if (from >= groups.size()) {
+            return new PageResult<>(Collections.emptyList(), total);
+        }
+        int to = Math.min(groups.size(), from + pageSize);
+        return new PageResult<>(new ArrayList<>(groups.subList(from, to)), total);
+    }
+
+    @Override
     public List<HrmAttendanceHolidayDO> getAttendanceHolidayListByDateRange(LocalDate startDate, LocalDate endDate) {
         return attendanceHolidayMapper.selectListByDateBetween(startDate, endDate);
     }
@@ -100,4 +244,4 @@
         return dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY;
     }
 
-}
\ No newline at end of file
+}

--
Gitblit v1.9.3