| | |
| | | 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.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.staff.mapper.PersonalAttendanceRecordsMapper; |
| | | 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 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; |
| | | |
| | | /** |
| | | * <p> |
| | | * 服务实现类 |
| | | * </p> |
| | | * |
| | | * @author 芯导软件(江苏)有限公司 |
| | | * @since 2026-02-09 01:20:07 |
| | | */ |
| | | @Service |
| | | public class PersonalAttendanceRecordsServiceImpl extends ServiceImpl<PersonalAttendanceRecordsMapper, PersonalAttendanceRecords> implements PersonalAttendanceRecordsService { |
| | | @Autowired |
| | | private PersonalAttendanceRecordsMapper personalAttendanceRecordsMapper; |
| | | |
| | | @Autowired |
| | | private StaffOnJobMapper staffOnJobMapper; |
| | | |
| | | @Autowired |
| | | private ISysDictDataService dictDataService; |
| | | |
| | | @Autowired |
| | | private SysDeptMapper sysDeptMapper; |
| | | |
| | | @Override |
| | | public IPage listPage(Page page, PersonalAttendanceRecords personalAttendanceRecords) { |
| | | // return personalAttendanceRecordsMapper.ListPage(page, personalAttendanceRecords); |
| | | return baseMapper.selectPage(page, new QueryWrapper<>(personalAttendanceRecords)); |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public int add(PersonalAttendanceRecords personalAttendanceRecords) { |
| | | // 当前时间 |
| | | LocalDate currentDate = LocalDate.now(); |
| | | |
| | | // 首先根据用户ID查询员工信息 |
| | | QueryWrapper<StaffOnJob> 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<PersonalAttendanceRecords> 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<PersonalAttendanceRecordsDto> listPage(Page page, PersonalAttendanceRecordsDto personalAttendanceRecordsDto) { |
| | | boolean admin = SecurityUtils.isAdmin(SecurityUtils.getUserId()); |
| | | if (!admin) { |
| | | QueryWrapper<StaffOnJob> 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<StaffOnJob> staffQueryWrapper = new QueryWrapper<>(); |
| | | staffQueryWrapper.eq("staff_no", SecurityUtils.getUsername()); |
| | | StaffOnJob staffOnJob = staffOnJobMapper.selectOne(staffQueryWrapper); |
| | | |
| | | if (staffOnJob == null) { |
| | | return null; // 当前用户没有对应的员工信息 |
| | | } |
| | | |
| | | // 根据员工ID和当前日期查询打卡记录 |
| | | QueryWrapper<PersonalAttendanceRecords> 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<StaffOnJob> 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<PersonalAttendanceRecordsDto> personalAttendanceRecords = personalAttendanceRecordsMapper.listPage(new Page<>(1, Integer.MAX_VALUE), personalAttendanceRecordsDto).getRecords(); |
| | | ExcelUtil<PersonalAttendanceRecordsDto> util = new ExcelUtil<PersonalAttendanceRecordsDto>(PersonalAttendanceRecordsDto.class); |
| | | util.exportExcel(response, personalAttendanceRecords, "考勤记录导出"); |
| | | } |
| | | } |