gaoluyang
昨天 ef3cbc1c6882786e8218b08be3e52ea44587f03b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
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<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());
        // 如果日期变更,校验新日期是否已存在配置
        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<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.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<HrmAttendanceHolidayDO> getAttendanceHolidayPage(HrmAttendanceHolidayPageReqVO pageReqVO) {
        return attendanceHolidayMapper.selectPage(pageReqVO);
    }
 
    @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);
    }
 
    @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;
    }
 
}