package com.ruoyi.staff.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.exception.base.BaseException; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.project.system.domain.SysDept; import com.ruoyi.project.system.mapper.SysDeptMapper; import com.ruoyi.project.system.service.ISysDictDataService; import com.ruoyi.staff.dto.PersonalAttendanceRecordsDto; import com.ruoyi.staff.dto.StaffOnJobDto; import com.ruoyi.staff.mapper.StaffOnJobMapper; import com.ruoyi.staff.pojo.PersonalAttendanceRecords; import com.ruoyi.staff.mapper.PersonalAttendanceRecordsMapper; import com.ruoyi.staff.pojo.StaffOnJob; import com.ruoyi.staff.service.PersonalAttendanceRecordsService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.staff.task.PersonalAttendanceRecordsTask; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.servlet.http.HttpServletResponse; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.List; /** *

* 服务实现类 *

* * @author 芯导软件(江苏)有限公司 * @since 2026-02-09 01:20:07 */ @Service public class PersonalAttendanceRecordsServiceImpl extends ServiceImpl implements PersonalAttendanceRecordsService { @Autowired private PersonalAttendanceRecordsMapper personalAttendanceRecordsMapper; @Autowired private StaffOnJobMapper staffOnJobMapper; @Autowired private PersonalAttendanceRecordsTask personalAttendanceRecordsTask; @Autowired private ISysDictDataService dictDataService; @Autowired private SysDeptMapper sysDeptMapper; @Override @Transactional(rollbackFor = Exception.class) public int add(PersonalAttendanceRecords personalAttendanceRecords) { // 当前时间 LocalDate currentDate = LocalDate.now(); // 首先根据用户ID查询员工信息 QueryWrapper staffQueryWrapper = new QueryWrapper<>(); staffQueryWrapper.eq("staff_no", SecurityUtils.getUsername()); StaffOnJob staffOnJob = staffOnJobMapper.selectOne(staffQueryWrapper); if (staffOnJob == null) { throw new BaseException("当前用户没有对应的员工信息"); } // 当前时间 LocalDateTime currentDateTime = LocalDateTime.now(); // 如果打卡时间超过考勤下班时间不能打卡 // 获取考勤下班时间点 String[] timeConfigs = getAttendanceTimeConfig(); String timeConfig = timeConfigs[1]; String[] timeParts = timeConfig.split(":"); int standardHour = Integer.parseInt(timeParts[0]); int standardMinute = Integer.parseInt(timeParts[1]); // 当前时间 int actualHour = currentDateTime.getHour(); int actualMinute = currentDateTime.getMinute(); // 判断打卡时间是否晚于当前时间 if (actualHour > standardHour || (actualHour == standardHour && actualMinute > standardMinute)) { throw new BaseException("打卡时间不能晚于下班时间"); } // 根据员工ID和当前日期查询打卡记录 QueryWrapper attendanceQueryWrapper = new QueryWrapper<>(); attendanceQueryWrapper.eq("staff_on_job_id", staffOnJob.getId()) .eq("date", currentDate); PersonalAttendanceRecords attendanceRecord = personalAttendanceRecordsMapper.selectOne(attendanceQueryWrapper); // 根据字典设置的考勤时间判断迟到早退 if (attendanceRecord == null) { // 不存在打卡记录,创建新记录 personalAttendanceRecords.setStaffOnJobId(staffOnJob.getId()); personalAttendanceRecords.setDate(currentDate); personalAttendanceRecords.setWorkStartAt(currentDateTime); personalAttendanceRecords.setStatus(determineAttendanceStatus(personalAttendanceRecords, true)); personalAttendanceRecords.setRemark(personalAttendanceRecords.getRemark()); personalAttendanceRecords.setTenantId(staffOnJob.getTenantId()); return personalAttendanceRecordsMapper.insert(personalAttendanceRecords); } else { if (attendanceRecord.getWorkEndAt() == null) { // 更新工作结束时间和工作时长 attendanceRecord.setWorkEndAt(currentDateTime); // 计算工作时长(精确到分钟,保留2位小数) LocalDateTime startTime = attendanceRecord.getWorkStartAt(); LocalDateTime endTime = attendanceRecord.getWorkEndAt(); // 计算两个时间之间的分钟数 long totalMinutes = java.time.Duration.between(startTime, endTime).toMinutes(); BigDecimal workHours = BigDecimal.valueOf(totalMinutes) .divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP); attendanceRecord.setWorkHours(workHours); // 更新考勤状态 attendanceRecord.setStatus(determineAttendanceStatus(attendanceRecord, false)); return personalAttendanceRecordsMapper.updateById(attendanceRecord); } else { throw new BaseException("您已经打过卡了,无需重复打卡!!!"); } } } // 获取考勤时间配置 private String[] getAttendanceTimeConfig() { String[] timeConfigs = new String[2]; try { String dictType = "sys_work_time"; // 获取上班时间配置,默认为09:00 String startTimeConfig = dictDataService.selectDictLabel(dictType, "start_at"); timeConfigs[0] = (startTimeConfig == null || startTimeConfig.trim().isEmpty()) ? "09:00" : startTimeConfig; // 获取下班时间配置,默认为18:00 String endTimeConfig = dictDataService.selectDictLabel(dictType, "end_at"); timeConfigs[1] = (endTimeConfig == null || endTimeConfig.trim().isEmpty()) ? "18:00" : endTimeConfig; return timeConfigs; } catch (Exception e) { timeConfigs[0] = "09:00"; // 默认上班时间 timeConfigs[1] = "18:00"; // 默认下班时间 return timeConfigs; } } // 根据实际时间和是否上班时间判断考勤状态 // 0 正常 1 迟到 2 早退 3 迟到早退 4 缺勤 private Integer determineAttendanceStatus(PersonalAttendanceRecords attendanceRecord, boolean isStart) { LocalDateTime actualTime = isStart ? attendanceRecord.getWorkStartAt() : attendanceRecord.getWorkEndAt(); try { // 获取考勤时间配置 String[] timeConfigs = getAttendanceTimeConfig(); String timeConfig = isStart ? timeConfigs[0] : timeConfigs[1]; // 解析标准时间 String[] timeParts = timeConfig.split(":"); int standardHour = Integer.parseInt(timeParts[0]); int standardMinute = Integer.parseInt(timeParts[1]); // 获取实际时间的时分 int actualHour = actualTime.getHour(); int actualMinute = actualTime.getMinute(); // 判断状态 if (isStart) { // 上班打卡:超过标准时间算迟到 if (actualHour > standardHour || (actualHour == standardHour && actualMinute > standardMinute)) { return 1; // 迟到 } } else { // 下班打卡:早于标准时间算早退 if (actualHour < standardHour || (actualHour == standardHour && actualMinute < standardMinute)) { if (attendanceRecord.getStatus() == 1) { return 3; // 迟到早退 } return 2; // 早退 } } return 0; // 正常 } catch (Exception e) { // 如果获取配置失败,默认返回正常状态 log.warn("获取考勤时间配置失败,使用默认状态:" + e.getMessage()); return 0; } } @Override public IPage listPage(Page page, PersonalAttendanceRecordsDto personalAttendanceRecordsDto) { boolean admin = SecurityUtils.isAdmin(SecurityUtils.getUserId()); if (!admin) { QueryWrapper staffQueryWrapper = new QueryWrapper<>(); staffQueryWrapper.eq("staff_no", SecurityUtils.getUsername()); StaffOnJob staffOnJob = staffOnJobMapper.selectOne(staffQueryWrapper); if (staffOnJob == null) { return new Page<>(page.getCurrent(), page.getSize(), 0); } personalAttendanceRecordsDto.setStaffOnJobId(staffOnJob.getId()); } return personalAttendanceRecordsMapper.listPage(page,personalAttendanceRecordsDto); } @Override public PersonalAttendanceRecordsDto todayInfo(PersonalAttendanceRecordsDto personalAttendanceRecordsDto) { // 获取当前日期 LocalDate currentDate = LocalDate.now(); // 首先根据用户ID查询员工信息 QueryWrapper staffQueryWrapper = new QueryWrapper<>(); staffQueryWrapper.eq("staff_no", SecurityUtils.getUsername()); StaffOnJob staffOnJob = staffOnJobMapper.selectOne(staffQueryWrapper); if (staffOnJob == null) { return null; // 当前用户没有对应的员工信息 } // 根据员工ID和当前日期查询打卡记录 QueryWrapper attendanceQueryWrapper = new QueryWrapper<>(); attendanceQueryWrapper.eq("staff_on_job_id", staffOnJob.getId()) .eq("date", currentDate); PersonalAttendanceRecords attendanceRecord = personalAttendanceRecordsMapper.selectOne(attendanceQueryWrapper); // 返回参数 PersonalAttendanceRecordsDto resultDto = new PersonalAttendanceRecordsDto(); if (attendanceRecord != null) { // 如果有打卡记录,复制打卡记录信息 BeanUtils.copyProperties(attendanceRecord, resultDto); } // 员工相关信息 resultDto.setStaffName(staffOnJob.getStaffName()); resultDto.setStaffNo(staffOnJob.getStaffNo()); resultDto.setDeptId(staffOnJob.getSysDeptId() != null ? staffOnJob.getSysDeptId() : null); SysDept dept = sysDeptMapper.selectDeptById(staffOnJob.getSysDeptId()); resultDto.setDeptName(dept != null ? dept.getDeptName() : null); return resultDto; } @Override public void export(HttpServletResponse response, PersonalAttendanceRecordsDto personalAttendanceRecordsDto) { boolean admin = SecurityUtils.isAdmin(SecurityUtils.getUserId()); if (!admin) { QueryWrapper staffQueryWrapper = new QueryWrapper<>(); staffQueryWrapper.eq("staff_no", SecurityUtils.getUsername()); StaffOnJob staffOnJob = staffOnJobMapper.selectOne(staffQueryWrapper); if (staffOnJob == null) { throw new ServiceException("没有员工信息,无法导出考勤记录"); } personalAttendanceRecordsDto.setStaffOnJobId(staffOnJob.getId()); } List personalAttendanceRecords = personalAttendanceRecordsMapper.listPage(new Page<>(1, Integer.MAX_VALUE), personalAttendanceRecordsDto).getRecords(); ExcelUtil util = new ExcelUtil(PersonalAttendanceRecordsDto.class); util.exportExcel(response, personalAttendanceRecords, "考勤记录导出"); } }