package cn.iocoder.yudao.module.hrm.service.attendance;
|
|
import cn.hutool.core.collection.CollUtil;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
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.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.service.employee.HrmEmployeeService;
|
import jakarta.annotation.Resource;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.validation.annotation.Validated;
|
|
import java.math.BigDecimal;
|
import java.time.LocalDate;
|
import java.time.YearMonth;
|
import java.util.List;
|
|
/**
|
* 考勤统计汇总 Service 实现类
|
*
|
* @author 芋道源码
|
*/
|
@Service
|
@Validated
|
@Slf4j
|
public class HrmAttendanceSummaryServiceImpl implements HrmAttendanceSummaryService {
|
|
@Resource
|
private HrmAttendanceSummaryMapper attendanceSummaryMapper;
|
@Resource
|
private HrmAttendanceRecordMapper attendanceRecordMapper;
|
@Resource
|
private HrmEmployeeService employeeService;
|
@Resource
|
private HrmAttendanceHolidayService attendanceHolidayService;
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void generateMonthlySummary(Long userId, String yearMonth) {
|
// 1. 解析年月,获取该月起始日期
|
YearMonth ym = YearMonth.parse(yearMonth);
|
LocalDate startDate = ym.atDay(1);
|
LocalDate endDate = ym.atEndOfMonth();
|
|
// 2. 获取员工信息
|
HrmEmployeeDO employee = employeeService.getEmployee(userId);
|
if (employee == null) {
|
log.warn("[generateMonthlySummary] 员工不存在: {}", userId);
|
return;
|
}
|
|
// 3. 查询该月所有考勤记录
|
List<HrmAttendanceRecordDO> records = attendanceRecordMapper.selectListByUserIdAndDateBetween(userId, startDate, endDate);
|
|
// 4. 计算应出勤天数(工作日数量)
|
int workDays = calculateWorkDays(startDate, endDate);
|
|
// 5. 统计各项数据
|
int lateCount = 0;
|
int earlyCount = 0;
|
int absentCount = 0;
|
BigDecimal overtimeHours = BigDecimal.ZERO;
|
int actualDays = 0;
|
|
for (HrmAttendanceRecordDO record : records) {
|
// 统计迟到
|
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());
|
}
|
// 统计实际出勤天数(有正常打卡记录)
|
if (record.getClockInTime() != null || record.getClockOutTime() != null) {
|
actualDays++;
|
}
|
}
|
|
// 6. 保存或更新汇总记录
|
HrmAttendanceSummaryDO summary = attendanceSummaryMapper.selectByUserIdAndYearMonth(userId, yearMonth);
|
if (summary == null) {
|
summary = HrmAttendanceSummaryDO.builder()
|
.userId(userId)
|
.deptId(employee.getDeptId())
|
.yearMonth(yearMonth)
|
.workDays(workDays)
|
.actualDays(actualDays)
|
.lateCount(lateCount)
|
.earlyCount(earlyCount)
|
.absentCount(absentCount)
|
.overtimeHours(overtimeHours)
|
.leaveHours(BigDecimal.ZERO)
|
.build();
|
attendanceSummaryMapper.insert(summary);
|
} else {
|
summary.setWorkDays(workDays);
|
summary.setActualDays(actualDays);
|
summary.setLateCount(lateCount);
|
summary.setEarlyCount(earlyCount);
|
summary.setAbsentCount(absentCount);
|
summary.setOvertimeHours(overtimeHours);
|
attendanceSummaryMapper.updateById(summary);
|
}
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void generateDeptMonthlySummary(Long deptId, String yearMonth) {
|
// 获取部门所有在职员工
|
List<HrmEmployeeDO> employees = employeeService.getEmployeeList(null);
|
if (CollUtil.isEmpty(employees)) {
|
return;
|
}
|
// 筛选部门员工
|
List<HrmEmployeeDO> deptEmployees = employees.stream()
|
.filter(e -> deptId.equals(e.getDeptId()))
|
.toList();
|
for (HrmEmployeeDO employee : deptEmployees) {
|
generateMonthlySummary(employee.getId(), yearMonth);
|
}
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void generateAllMonthlySummary(String yearMonth) {
|
// 获取所有在职员工
|
List<HrmEmployeeDO> employees = employeeService.getEmployeeList(null);
|
if (CollUtil.isEmpty(employees)) {
|
return;
|
}
|
for (HrmEmployeeDO employee : employees) {
|
generateMonthlySummary(employee.getId(), yearMonth);
|
}
|
}
|
|
/**
|
* 计算应出勤天数(工作日数量)
|
*/
|
private int calculateWorkDays(LocalDate startDate, LocalDate endDate) {
|
int workDays = 0;
|
LocalDate current = startDate;
|
while (!current.isAfter(endDate)) {
|
if (attendanceHolidayService.isWorkDay(current)) {
|
workDays++;
|
}
|
current = current.plusDays(1);
|
}
|
return workDays;
|
}
|
|
@Override
|
public PageResult<HrmAttendanceSummaryDO> getAttendanceSummaryPage(HrmAttendanceSummaryPageReqVO pageReqVO) {
|
return attendanceSummaryMapper.selectPage(pageReqVO, new LambdaQueryWrapperX<HrmAttendanceSummaryDO>()
|
.eqIfPresent(HrmAttendanceSummaryDO::getUserId, pageReqVO.getUserId())
|
.eqIfPresent(HrmAttendanceSummaryDO::getDeptId, pageReqVO.getDeptId())
|
.eqIfPresent(HrmAttendanceSummaryDO::getYearMonth, pageReqVO.getYearMonth())
|
.orderByDesc(HrmAttendanceSummaryDO::getYearMonth)
|
.orderByAsc(HrmAttendanceSummaryDO::getDeptId));
|
}
|
|
@Override
|
public HrmAttendanceSummaryDO getMonthlySummary(Long userId, String yearMonth) {
|
return attendanceSummaryMapper.selectByUserIdAndYearMonth(userId, yearMonth);
|
}
|
|
@Override
|
public List<HrmAttendanceSummaryDO> getDeptMonthlySummaryList(Long deptId, String yearMonth) {
|
return attendanceSummaryMapper.selectListByDeptIdAndYearMonth(deptId, yearMonth);
|
}
|
|
}
|