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.*; /** * 考勤节假日配置 Service 实现类 * * @author 超级管理员 */ @Service @Validated public class HrmAttendanceHolidayServiceImpl implements HrmAttendanceHolidayService { @Resource private HrmAttendanceHolidayMapper attendanceHolidayMapper; @Override public Long createAttendanceHoliday(HrmAttendanceHolidaySaveReqVO createReqVO) { // 校验日期是否已存在配置 validateAttendanceHolidayDateExists(createReqVO.getDate()); HrmAttendanceHolidayDO attendanceHoliday = BeanUtils.toBean(createReqVO, HrmAttendanceHolidayDO.class); // VO 侧用 Boolean 兼容前端勾选框的 true/false,落库统一为 0/1 attendanceHoliday.setIsLegal(Boolean.TRUE.equals(createReqVO.getIsLegal()) ? 1 : 0); attendanceHolidayMapper.insert(attendanceHoliday); return attendanceHoliday.getId(); } @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 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()); // 如果日期变更,校验新日期是否已存在配置 HrmAttendanceHolidayDO oldHoliday = attendanceHolidayMapper.selectById(updateReqVO.getId()); if (!oldHoliday.getDate().equals(updateReqVO.getDate())) { validateAttendanceHolidayDateExists(updateReqVO.getDate()); } HrmAttendanceHolidayDO updateObj = BeanUtils.toBean(updateReqVO, HrmAttendanceHolidayDO.class); updateObj.setIsLegal(Boolean.TRUE.equals(updateReqVO.getIsLegal()) ? 1 : 0); attendanceHolidayMapper.updateById(updateObj); } @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 ids = updateReqVO.getIds().stream().filter(Objects::nonNull).distinct().toList(); if (ids.isEmpty()) { throw exception(ATTENDANCE_HOLIDAY_RANGE_IDS_EMPTY); } List 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 ids) { List 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.physicalDeleteByIds(Collections.singletonList(id)); } private void validateAttendanceHolidayExists(Long id) { if (attendanceHolidayMapper.selectById(id) == null) { throw exception(ATTENDANCE_HOLIDAY_NOT_EXISTS); } } private void validateAttendanceHolidayDateExists(LocalDate date) { if (attendanceHolidayMapper.selectByDate(date) != null) { throw exception(ATTENDANCE_HOLIDAY_DATE_EXISTS); } } @Override public HrmAttendanceHolidayDO getAttendanceHoliday(Long id) { return attendanceHolidayMapper.selectById(id); } @Override public PageResult getAttendanceHolidayPage(HrmAttendanceHolidayPageReqVO pageReqVO) { return attendanceHolidayMapper.selectPage(pageReqVO); } @Override public PageResult getAttendanceHolidayGroupPage( HrmAttendanceHolidayPageReqVO pageReqVO) { List list = attendanceHolidayMapper.selectListByCondition(pageReqVO); if (list.isEmpty()) { return new PageResult<>(Collections.emptyList(), 0L); } // 1. 连续日期 + 名称/类型/是否法定/备注一致的记录合并为一行 List 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 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 pageGroups( List 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 getAttendanceHolidayListByDateRange(LocalDate startDate, LocalDate endDate) { return attendanceHolidayMapper.selectListByDateBetween(startDate, endDate); } @Override public boolean isWorkDay(LocalDate date) { // 1. 查询节假日配置 HrmAttendanceHolidayDO holiday = attendanceHolidayMapper.selectByDate(date); if (holiday != null) { // 有配置时,按配置判断 return HrmAttendanceHolidayTypeEnum.WORK_DAY.getType().equals(holiday.getType()); } // 2. 无配置时,按周末判断 DayOfWeek dayOfWeek = date.getDayOfWeek(); return dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY; } }