请假考勤调整

This commit is contained in:
aikai 2024-07-23 17:14:00 +08:00
parent 24d3508ea8
commit 2ae403942e
21 changed files with 538 additions and 32 deletions

View File

@ -0,0 +1,27 @@
package cn.iocoder.yudao.framework.common.pojo;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class BpmOALeaveDTO {
/**
* 请假表单主键
*/
private Long id;
/**
* 申请人的用户编号
*
* 关联 AdminUserDO id 属性
*/
private Long userId;
/**
* 开始时间
*/
private LocalDateTime startTime;
/**
* 结束时间
*/
private LocalDateTime endTime;
}

View File

@ -8,6 +8,7 @@ import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray; import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject; import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.Constants;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.*; import java.time.*;
@ -257,6 +258,25 @@ public class DateUtils {
return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now()); return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now());
} }
/**
* 获取两个时间区间 - 获取两个时间区间的所有日期
*
* @param beginTime
* @param endTime
* @return
*/
public static List<String> betweenDayList(LocalDateTime beginTime, LocalDateTime endTime) {
List<String> list = new ArrayList<>();
long num = LocalDateTimeUtil.between(beginTime, endTime, ChronoUnit.DAYS);
for (int i = 0; i <= num; i++) {
LocalDateTime dateTime = LocalDateTimeUtil.offset(beginTime, i, ChronoUnit.DAYS);
list.add(dateTime.format(Constants.REPO_DATE_FORMAT));
}
return list;
}
/** /**
* 获取两个时间区间 - 获取两个时间区间的所有日期 * 获取两个时间区间 - 获取两个时间区间的所有日期
* *
@ -357,6 +377,7 @@ public class DateUtils {
/** /**
* 将1-7的数字转换为对应的中文星期表示 * 将1-7的数字转换为对应的中文星期表示
*
* @param dayOfWeek 数字形式的星期几1-7分别代表星期一到星期日 * @param dayOfWeek 数字形式的星期几1-7分别代表星期一到星期日
* @return 中文表示的星期几 * @return 中文表示的星期几
*/ */

View File

@ -20,6 +20,7 @@ public interface ErrorCodeConstants {
ErrorCode OA_DEPART_BM_POST_NOT_EXISTS = new ErrorCode(1_009_001_005, "部门的部门经理不存在"); ErrorCode OA_DEPART_BM_POST_NOT_EXISTS = new ErrorCode(1_009_001_005, "部门的部门经理不存在");
ErrorCode OA_HR_POST_NOT_EXISTS = new ErrorCode(1_009_001_006, "HR岗位未设置"); ErrorCode OA_HR_POST_NOT_EXISTS = new ErrorCode(1_009_001_006, "HR岗位未设置");
ErrorCode OA_DAY_LEAVE_ERROR = new ErrorCode(1_009_001_007, "请假天数必须>=1"); ErrorCode OA_DAY_LEAVE_ERROR = new ErrorCode(1_009_001_007, "请假天数必须>=1");
ErrorCode FAILED_TO_APPLY_FOR_LEAVE = new ErrorCode(1_009_001_008, "请假失败");
ErrorCode OA_REIMBURSEMENT_NOT_EXISTS = new ErrorCode(1_009_001_100, "报销申请不存在"); ErrorCode OA_REIMBURSEMENT_NOT_EXISTS = new ErrorCode(1_009_001_100, "报销申请不存在");
ErrorCode OA_EVECTION_NOT_EXISTS = new ErrorCode(1_009_001_101, "出差申请不存在"); ErrorCode OA_EVECTION_NOT_EXISTS = new ErrorCode(1_009_001_101, "出差申请不存在");

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.bpm.framework.rpc.config; package cn.iocoder.yudao.module.bpm.framework.rpc.config;
import cn.iocoder.yudao.module.infra.api.file.FileApi; import cn.iocoder.yudao.module.infra.api.file.FileApi;
import cn.iocoder.yudao.module.system.api.attendance.AttendanceApi;
import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.PostApi; import cn.iocoder.yudao.module.system.api.dept.PostApi;
import cn.iocoder.yudao.module.system.api.dict.DictDataApi; import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
@ -15,8 +16,8 @@ import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
@EnableFeignClients(clients = {FileApi.class,RoleApi.class, DeptApi.class, PostApi.class, AdminUserApi.class, SmsSendApi.class, DictDataApi.class, NotifyMessageSendApi.class, @EnableFeignClients(clients = {FileApi.class, RoleApi.class, DeptApi.class, PostApi.class, AdminUserApi.class, SmsSendApi.class, DictDataApi.class, NotifyMessageSendApi.class,
SubscribeMessageSendApi.class, SocialClientApi.class, UsersExtApi.class SubscribeMessageSendApi.class, SocialClientApi.class, UsersExtApi.class, AttendanceApi.class
}) })
public class RpcConfiguration { public class RpcConfiguration {
} }

View File

@ -1,6 +1,10 @@
package cn.iocoder.yudao.module.bpm.service.oa; package cn.iocoder.yudao.module.bpm.service.oa;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.pojo.BpmOALeaveDTO;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.bpm.api.oa.vo.BpmOALeaveRpcVO; import cn.iocoder.yudao.module.bpm.api.oa.vo.BpmOALeaveRpcVO;
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi; import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
@ -12,17 +16,21 @@ import cn.iocoder.yudao.module.bpm.convert.oa.BpmOALeaveConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOALeaveDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOALeaveDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOALeaveMapper; import cn.iocoder.yudao.module.bpm.dal.mysql.oa.BpmOALeaveMapper;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
import cn.iocoder.yudao.module.system.api.attendance.AttendanceApi;
import cn.iocoder.yudao.module.system.api.attendance.dto.AttendancePunchRecordDTO;
import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.FAILED_TO_APPLY_FOR_LEAVE;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.OA_LEAVE_NOT_EXISTS; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.OA_LEAVE_NOT_EXISTS;
/** /**
@ -46,6 +54,8 @@ public class BpmOALeaveServiceImpl extends BpmOABaseService implements BpmOALeav
private BpmProcessInstanceApi processInstanceApi; private BpmProcessInstanceApi processInstanceApi;
@Resource @Resource
private StringRedisTemplate stringRedisTemplate; private StringRedisTemplate stringRedisTemplate;
@Resource
private AttendanceApi attendanceApi;
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@ -77,6 +87,7 @@ public class BpmOALeaveServiceImpl extends BpmOABaseService implements BpmOALeav
@Override @Override
@Transactional(rollbackFor = Exception.class)
public void updateLeaveResult(Long id, Integer result) { public void updateLeaveResult(Long id, Integer result) {
BpmOALeaveDO bpmOALeaveDO = validateLeaveExists(id); BpmOALeaveDO bpmOALeaveDO = validateLeaveExists(id);
leaveMapper.updateById(new BpmOALeaveDO().setId(id).setResult(result)); leaveMapper.updateById(new BpmOALeaveDO().setId(id).setResult(result));
@ -84,17 +95,28 @@ public class BpmOALeaveServiceImpl extends BpmOABaseService implements BpmOALeav
// -- 如果是的话 先插入到redis中 - (事前请假) // -- 如果是的话 先插入到redis中 - (事前请假)
// -- 如果不是 则查询相应的考勤 修改考勤状态 事后请假 - 还需要判断结束时间是否 // -- 如果不是 则查询相应的考勤 修改考勤状态 事后请假 - 还需要判断结束时间是否
// //
// if (result.equals(BpmProcessInstanceResultEnum.APPROVE.getResult())) { LocalDateTime now = LocalDateTimeUtil.now();
// if (LocalDateTimeUtil.between(bpmOALeaveDO.getStartTime(), LocalDateTimeUtil.now()).toDays() > 0) { if (result.equals(BpmProcessInstanceResultEnum.APPROVE.getResult())) {
// // -- 插入到redis中 // 事后请假修改考勤 = 考勤的预设已经生成过了 - 并且已经在表里面存在了 - 所以要找到表中的数据 - 修改考勤状态
// LeaveVO leaveVO = new LeaveVO(); CommonResult commonResult = attendanceApi.askingForLeaveAfterwardsToModifyAttendance(new AttendancePunchRecordDTO()
// BeanUtil.copyProperties(bpmOALeaveDO, leaveVO); .setUserId(bpmOALeaveDO.getUserId())
// stringRedisTemplate.opsForHash().put("leave", bpmOALeaveDO.getUserId().toString(), JSONUtil.toJsonStr(leaveVO)); .setStartTime(bpmOALeaveDO.getStartTime())
// } else { .setEndTime(bpmOALeaveDO.getEndTime())
// // -- 查询相应的考勤 修改考勤状态 .setLeaveId(id)
// );
// } if (!commonResult.isSuccess()) {
// } throw exception(FAILED_TO_APPLY_FOR_LEAVE);
}
if (now.isBefore(bpmOALeaveDO.getEndTime())) {
// 事前请假 = 考勤预设可能还没有生成 - 因为可能请假好几天 - 所以这里处理就比较麻烦点 - 先看下考勤表里面有没有在这个区间的考勤 - 如果有的话先修改考勤状态 -
// 然后将数据先存入redis - 在设置考勤预设的时候 就去redis 中查询是否有请假 - 有的话预设的时候就预设进去
BpmOALeaveDTO dto = new BpmOALeaveDTO();
BeanUtil.copyProperties(bpmOALeaveDO, dto);
String key = "leave" + "_" + bpmOALeaveDO.getUserId().toString();
stringRedisTemplate.opsForHash().put(key, id.toString(), JSONUtil.toJsonStr(dto));
// -- 将请假put到redis的map中 -
}
}
} }
private BpmOALeaveDO validateLeaveExists(Long id) { private BpmOALeaveDO validateLeaveExists(Long id) {

View File

@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.system.api.attendance;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.system.api.attendance.dto.AttendancePunchRecordDTO;
import cn.iocoder.yudao.module.system.api.attendance.vo.AttendancePunchRecordVO;
import cn.iocoder.yudao.module.system.enums.ApiConstants;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
@Tag(name = "RPC 服务 - 考勤")
public interface AttendanceApi {
String PREFIX = ApiConstants.PREFIX + "/attendance";
@PostMapping(PREFIX + "/askingForLeaveAfterwardsToModifyAttendance")
@Operation(summary = "获取考勤记录")
CommonResult askingForLeaveAfterwardsToModifyAttendance(@RequestBody AttendancePunchRecordDTO attendancePunchRecordDTO);
}

View File

@ -0,0 +1,32 @@
package cn.iocoder.yudao.module.system.api.attendance.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 用户打卡记录 DTO
*
* @author 艾楷
*/
@Data
@Accessors(chain = true)
public class AttendancePunchRecordDTO {
/**
* 用户id
*/
private Long userId;
/**
* 开始时间
*/
private LocalDateTime startTime;
/**
* 结束时间
*/
private LocalDateTime endTime;
/**
* 请假id 对应bpm_oa_leave表id
*/
private Long leaveId;
}

View File

@ -0,0 +1,138 @@
package cn.iocoder.yudao.module.system.api.attendance.vo;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 用户打卡记录 DO
*
* @author 艾楷
*/
@Data
public class AttendancePunchRecordVO {
/**
* 编号
*/
private Long id;
/**
* 考勤组管理员id
*/
private Long userId;
/**
* 部门id
*/
private Long deptId;
/**
* 考勤组id
*/
private Long attendanceGroupId;
/**
* 考勤组名称
*/
private String attendanceGroupName;
/**
* 班次id
*/
private Long attendanceGroupShiftId;
/**
* 班次名称
*/
private String attendanceGroupShiftName;
/**
* 班次子表id
*/
private Long attendanceGroupShiftItemId;
/**
* 考勤类型 1固定班制 2排班制
*/
private Integer type;
/**
* 打卡类型 1考勤机 2小程序范围打卡
*/
private Integer punchType;
/**
* 上下班类型 0上班 1下班
*/
private Integer workType;
/**
* 级别 1到
*/
private Integer level;
/**
* 打卡状态 0正常 1迟到 2早退 3缺卡 4未打卡(还没到打卡时间) 5补卡 6请假
*/
private Integer status;
/**
* 明天的打卡 0否 1是 (业务需要 先把明天的打卡统计出来)
*/
private Integer nextDayFlag;
/**
* 是否外勤 0否 1是
*/
private Integer fieldServiceFlag;
/**
* 日期yyyy-MM-dd格式 (归属于哪一天)
*/
private String dayTime;
/**
* 日期yyyy-MM-dd格式 (实际是哪一天)
*/
private String actualDayTime;
/**
* 打卡时间
*/
private LocalDateTime punchTime;
/**
* 应打卡时间
*/
private LocalDateTime shouldPunchTime;
/**
* 最晚打卡时间(超过即为缺卡)
*/
private LocalDateTime latestPunchTime;
/**
* 打卡备注
*/
private String remark;
/**
* 图片
*/
private String image;
/**
* 用户打卡地点
*/
private String punchAddress;
/**
* 迟到时长时间戳
*/
private Long lateTime;
/**
* 早退时长时间戳
*/
private Long leaveEarlyTime;
/**
* 加班时长时间戳
*/
private Long workOvertimeTime;
/**
* 是否已提醒 0否 1是
*/
private Integer remindFlag;
/**
* 请假json对象
*/
private String leaveJson;
}

View File

@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.system.api.attendance;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.system.api.attendance.dto.AttendancePunchRecordDTO;
import cn.iocoder.yudao.module.system.service.attendance.punchrecord.AttendancePunchRecordService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController // 提供 RESTful API 接口 Feign 调用
@Validated
public class AttendanceApiImpl implements AttendanceApi {
@Resource
private AttendancePunchRecordService attendancePunchRecordService;
@Override
public CommonResult askingForLeaveAfterwardsToModifyAttendance(AttendancePunchRecordDTO attendancePunchRecordDTO) {
attendancePunchRecordService.askingForLeaveAfterwardsToModifyAttendance(attendancePunchRecordDTO);
return CommonResult.success("ok");
}
}

View File

@ -25,6 +25,8 @@ public class AttendanceStatisticsVO {
private List<AttendancePunchStatisticsVO> fieldServiceNumber; private List<AttendancePunchStatisticsVO> fieldServiceNumber;
@Schema(description = "休息日") @Schema(description = "休息日")
private List<AttendancePunchStatisticsVO> restDays; private List<AttendancePunchStatisticsVO> restDays;
@Schema(description = "请假次数")
private List<AttendancePunchStatisticsVO> leaveNumber;
@Schema(description = "头部次数") @Schema(description = "头部次数")
private CalculateNum calculateNum; private CalculateNum calculateNum;
} }

View File

@ -9,7 +9,7 @@ import java.util.List;
@Data @Data
@Accessors(chain = true) @Accessors(chain = true)
public class AttendanceStatusByDayVO { public class AttendanceStatusByDayVO {
@Schema(description = "状态 0正常 1异常(迟到/早退/缺卡)") @Schema(description = "状态 0正常 1异常(迟到/早退/缺卡/请假/补卡)")
private Integer status; private Integer status;
@Schema(description = "是否有提交过 请假/加班/出差/外出/补卡 申请 0否 1是") @Schema(description = "是否有提交过 请假/加班/出差/外出/补卡 申请 0否 1是")
private Integer submitStatus = 0; private Integer submitStatus = 0;

View File

@ -27,6 +27,8 @@ public class CalculateNum {
String totalEarlyDeparturesTimeStr; String totalEarlyDeparturesTimeStr;
@Schema(description = "缺卡总次数") @Schema(description = "缺卡总次数")
int totalMissingCardsNumber = 0; int totalMissingCardsNumber = 0;
@Schema(description = "请假总次数")
int totalLeaveNumber = 0;
@Schema(description = "上班缺卡总次数") @Schema(description = "上班缺卡总次数")
int totalUpMissingCardsNumber = 0; int totalUpMissingCardsNumber = 0;
@Schema(description = "下班缺卡总次数") @Schema(description = "下班缺卡总次数")

View File

@ -30,6 +30,9 @@ public class TeamAttendanceStatisticsByCycleVO {
@Schema(description = "外勤") @Schema(description = "外勤")
private List<TeamAttendancePunchStatisticsVO> fieldServiceList; private List<TeamAttendancePunchStatisticsVO> fieldServiceList;
@Schema(description = "请假")
private List<TeamAttendancePunchStatisticsVO> leaveList;
@Schema(description = "顶部") @Schema(description = "顶部")
private TeamAttendanceStatisticsNumVO teamAttendanceStatisticsNumVO; private TeamAttendanceStatisticsNumVO teamAttendanceStatisticsNumVO;
@ -52,6 +55,9 @@ public class TeamAttendanceStatisticsByCycleVO {
@Schema(description = "缺卡次数") @Schema(description = "缺卡次数")
private Integer missingCardNum; private Integer missingCardNum;
@Schema(description = "请假次数")
private Integer leaveNum;
@Schema(description = "旷工次数") @Schema(description = "旷工次数")
private Integer absenteeismNum; private Integer absenteeismNum;

View File

@ -19,4 +19,6 @@ public class TeamAttendanceStatisticsByDayVO {
private Integer leaveEarlyNum; private Integer leaveEarlyNum;
@Schema(description = "外勤数量") @Schema(description = "外勤数量")
private Integer fieldworkNum; private Integer fieldworkNum;
@Schema(description = "请假数量")
private Integer leaveNum;
} }

View File

@ -77,7 +77,7 @@ public class AttendancePunchRecordDO extends BaseDO {
private Integer level; private Integer level;
/** /**
* 打卡状态 0正常 1迟到 2早退 3缺卡 4未打卡(还没到打卡时间) 5补卡 * 打卡状态 0正常 1迟到 2早退 3缺卡 4未打卡(还没到打卡时间) 5补卡 6请假
*/ */
private Integer status; private Integer status;
@ -91,9 +91,14 @@ public class AttendancePunchRecordDO extends BaseDO {
*/ */
private Integer fieldServiceFlag; private Integer fieldServiceFlag;
/** /**
* 日期yyyy-MM-dd格式 * 日期yyyy-MM-dd格式 (归属于哪一天)
*/ */
private String dayTime; private String dayTime;
/**
* 日期yyyy-MM-dd格式 (实际是哪一天)
*/
private String actualDayTime;
/** /**
* 打卡时间 * 打卡时间
*/ */
@ -140,6 +145,11 @@ public class AttendancePunchRecordDO extends BaseDO {
*/ */
private Integer remindFlag; private Integer remindFlag;
/**
* 请假单id
*/
private Long leaveId;
@TableField(exist = false) @TableField(exist = false)
private String deptName; private String deptName;
} }

View File

@ -3,9 +3,10 @@ package cn.iocoder.yudao.module.system.dal.mysql.attendance.punchrecord;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.system.api.attendance.dto.AttendancePunchRecordDTO;
import cn.iocoder.yudao.module.system.api.attendance.vo.AttendancePunchRecordVO;
import cn.iocoder.yudao.module.system.controller.admin.punchrecord.vo.AttendancePunchRecordPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.punchrecord.vo.AttendancePunchRecordPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.punchrecord.AttendancePunchRecordDO; import cn.iocoder.yudao.module.system.dal.dataobject.attendance.punchrecord.AttendancePunchRecordDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.service.attendance.punch.dto.AttendanceOnTheDayDTO; import cn.iocoder.yudao.module.system.service.attendance.punch.dto.AttendanceOnTheDayDTO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;

View File

@ -446,7 +446,12 @@ public class AttendanceServiceImpl implements AttendanceService {
Map<String, List<AttendancePunchRecordDO>> collect = list.stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getDayTime)); Map<String, List<AttendancePunchRecordDO>> collect = list.stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getDayTime));
List<Integer> statusList = Arrays.asList(AttendanceOnTheDayDTO.PUNCH_STATUS_LATE, AttendanceOnTheDayDTO.PUNCH_STATUS_LEAVE_EARLY, AttendanceOnTheDayDTO.PUNCH_STATUS_MISS); List<Integer> statusList = Arrays.asList(AttendanceOnTheDayDTO.PUNCH_STATUS_LATE,
AttendanceOnTheDayDTO.PUNCH_STATUS_LEAVE_EARLY,
AttendanceOnTheDayDTO.PUNCH_STATUS_MISS,
AttendanceOnTheDayDTO.REPLACEMENT_CARD,
AttendanceOnTheDayDTO.ASK_FOR_LEAVE
);
for (Map.Entry<String, List<AttendancePunchRecordDO>> entry : collect.entrySet()) { for (Map.Entry<String, List<AttendancePunchRecordDO>> entry : collect.entrySet()) {
AttendanceStatusByDayVO attendanceStatusByDayVO = new AttendanceStatusByDayVO(); AttendanceStatusByDayVO attendanceStatusByDayVO = new AttendanceStatusByDayVO();
int status = 0; int status = 0;
@ -504,6 +509,7 @@ public class AttendanceServiceImpl implements AttendanceService {
List<AttendancePunchStatisticsVO> beLateNumber = new ArrayList<>(); List<AttendancePunchStatisticsVO> beLateNumber = new ArrayList<>();
List<AttendancePunchStatisticsVO> earlyDeparturesNumber = new ArrayList<>(); List<AttendancePunchStatisticsVO> earlyDeparturesNumber = new ArrayList<>();
List<AttendancePunchStatisticsVO> missingCardsNumber = new ArrayList<>(); List<AttendancePunchStatisticsVO> missingCardsNumber = new ArrayList<>();
List<AttendancePunchStatisticsVO> leaveNumber = new ArrayList<>();
List<AttendancePunchStatisticsVO> minerDays = new ArrayList<>(); List<AttendancePunchStatisticsVO> minerDays = new ArrayList<>();
List<AttendancePunchStatisticsVO> fieldServiceNumber = new ArrayList<>(); List<AttendancePunchStatisticsVO> fieldServiceNumber = new ArrayList<>();
CalculateNum calculateNum = new CalculateNum(); CalculateNum calculateNum = new CalculateNum();
@ -533,6 +539,10 @@ public class AttendanceServiceImpl implements AttendanceService {
List<AttendancePunchStatisticsVO> missingCardsList = this.calculateMissingCardsList(day, weekChinese, workMap, calculateNum); List<AttendancePunchStatisticsVO> missingCardsList = this.calculateMissingCardsList(day, weekChinese, workMap, calculateNum);
missingCardsNumber.addAll(missingCardsList); missingCardsNumber.addAll(missingCardsList);
// -- 请假计算
List<AttendancePunchStatisticsVO> leaveList = this.calculateLeaveList(day, weekChinese, workMap, calculateNum);
leaveNumber.addAll(leaveList);
// -- 旷工计算 // -- 旷工计算
AttendancePunchStatisticsVO miner = this.calculateMiner(day, weekChinese, entry.getValue(), calculateNum); AttendancePunchStatisticsVO miner = this.calculateMiner(day, weekChinese, entry.getValue(), calculateNum);
if (miner != null) { if (miner != null) {
@ -553,6 +563,7 @@ public class AttendanceServiceImpl implements AttendanceService {
vo.setBeLateNumber(beLateNumber); vo.setBeLateNumber(beLateNumber);
vo.setEarlyDeparturesNumber(earlyDeparturesNumber); vo.setEarlyDeparturesNumber(earlyDeparturesNumber);
vo.setMissingCardsNumber(missingCardsNumber); vo.setMissingCardsNumber(missingCardsNumber);
vo.setLeaveNumber(leaveNumber);
vo.setMinerDays(minerDays); vo.setMinerDays(minerDays);
vo.setFieldServiceNumber(fieldServiceNumber); vo.setFieldServiceNumber(fieldServiceNumber);
vo.setRestDays(restDays); vo.setRestDays(restDays);
@ -560,6 +571,80 @@ public class AttendanceServiceImpl implements AttendanceService {
return vo; return vo;
} }
/**
* 请假计算
*
* @param workMap
* @return
*/
private String calculateLeaveList(Map<Long, List<AttendancePunchRecordDO>> workMap) {
int todayLeaveNumber = 0;
for (Map.Entry<Long, List<AttendancePunchRecordDO>> workEntry : workMap.entrySet()) {
if (CollectionUtil.isNotEmpty(workEntry.getValue())) {
// -- 请假计算
List<AttendancePunchRecordDO> leaveList = workEntry.getValue().stream().filter(a -> AttendanceOnTheDayDTO.ASK_FOR_LEAVE.equals(a.getStatus())).collect(Collectors.toList());
if (CollectionUtil.isNotEmpty(leaveList)) {
todayLeaveNumber += leaveList.size();
}
}
}
return String.valueOf(todayLeaveNumber);
}
/**
* 请假计算
*
* @param workMap
* @param calculateNum
* @return
*/
private void calculateLeaveList(Map<Long, List<AttendancePunchRecordDO>> workMap, CalculateNum calculateNum) {
int todayLeaveNumber = 0;
for (Map.Entry<Long, List<AttendancePunchRecordDO>> workEntry : workMap.entrySet()) {
if (CollectionUtil.isNotEmpty(workEntry.getValue())) {
// -- 请假计算
List<AttendancePunchRecordDO> leaveList = workEntry.getValue().stream().filter(a -> AttendanceOnTheDayDTO.ASK_FOR_LEAVE.equals(a.getStatus())).collect(Collectors.toList());
if (CollectionUtil.isNotEmpty(leaveList)) {
todayLeaveNumber += leaveList.size();
}
}
}
calculateNum.setTotalLeaveNumber(calculateNum.getTotalLeaveNumber() + todayLeaveNumber);
}
/**
* 请假计算
*
* @param day
* @param weekChinese
* @param workMap
* @param calculateNum
* @return
*/
private List<AttendancePunchStatisticsVO> calculateLeaveList(String day, String weekChinese, Map<Long, List<AttendancePunchRecordDO>> workMap, CalculateNum calculateNum) {
List<AttendancePunchStatisticsVO> list = new ArrayList<>();
int totalLeaveNumber = 0;
for (Map.Entry<Long, List<AttendancePunchRecordDO>> workEntry : workMap.entrySet()) {
if (CollectionUtil.isNotEmpty(workEntry.getValue())) {
// -- 缺卡计算
List<AttendancePunchRecordDO> missingCardsList = workEntry.getValue().stream().filter(a -> AttendanceOnTheDayDTO.ASK_FOR_LEAVE.equals(a.getStatus())).collect(Collectors.toList());
if (CollectionUtil.isNotEmpty(missingCardsList)) {
totalLeaveNumber += missingCardsList.size();
for (AttendancePunchRecordDO attendancePunchRecordDO : missingCardsList) {
AttendancePunchStatisticsVO earlyDepartures = new AttendancePunchStatisticsVO();
earlyDepartures.setDay(day);
earlyDepartures.setWeek(weekChinese);
earlyDepartures.setShouldPunchTime(attendancePunchRecordDO.getShouldPunchTime().format(DateTimeFormatter.ofPattern("HH:mm")));
list.add(earlyDepartures);
}
}
}
}
calculateNum.setTotalLeaveNumber(calculateNum.getTotalLeaveNumber() + totalLeaveNumber);
return list;
}
@Override @Override
public Map<String, TeamAttendanceStatisticsByDayVO> teamStatisticsByDay(TeamAttendanceStatisticsByDayDTO dto) { public Map<String, TeamAttendanceStatisticsByDayVO> teamStatisticsByDay(TeamAttendanceStatisticsByDayDTO dto) {
Map<String, TeamAttendanceStatisticsByDayVO> map = new HashMap<>(); Map<String, TeamAttendanceStatisticsByDayVO> map = new HashMap<>();
@ -587,6 +672,7 @@ public class AttendanceServiceImpl implements AttendanceService {
int lateNum = 0; int lateNum = 0;
int leaveEarlyNum = 0; int leaveEarlyNum = 0;
int fieldworkNum = 0; int fieldworkNum = 0;
int leaveNum = 0;
for (AttendancePunchRecordDO attendancePunchRecordDO : entry.getValue()) { for (AttendancePunchRecordDO attendancePunchRecordDO : entry.getValue()) {
if (AttendanceOnTheDayDTO.PUNCH_STATUS_MISS.equals(attendancePunchRecordDO.getStatus())) { if (AttendanceOnTheDayDTO.PUNCH_STATUS_MISS.equals(attendancePunchRecordDO.getStatus())) {
punchStatusMissNum++; punchStatusMissNum++;
@ -600,6 +686,9 @@ public class AttendanceServiceImpl implements AttendanceService {
if (Constants.TRUE.equals(attendancePunchRecordDO.getFieldServiceFlag())) { if (Constants.TRUE.equals(attendancePunchRecordDO.getFieldServiceFlag())) {
fieldworkNum++; fieldworkNum++;
} }
if (AttendanceOnTheDayDTO.ASK_FOR_LEAVE.equals(attendancePunchRecordDO.getStatus())) {
leaveNum++;
}
} }
TeamAttendanceStatisticsByDayVO vo = new TeamAttendanceStatisticsByDayVO(); TeamAttendanceStatisticsByDayVO vo = new TeamAttendanceStatisticsByDayVO();
vo.setAnswerNum(userMap.keySet().size()); vo.setAnswerNum(userMap.keySet().size());
@ -608,6 +697,7 @@ public class AttendanceServiceImpl implements AttendanceService {
vo.setLateNum(lateNum); vo.setLateNum(lateNum);
vo.setLeaveEarlyNum(leaveEarlyNum); vo.setLeaveEarlyNum(leaveEarlyNum);
vo.setFieldworkNum(fieldworkNum); vo.setFieldworkNum(fieldworkNum);
vo.setLeaveNum(leaveNum);
map.put(entry.getKey(), vo); map.put(entry.getKey(), vo);
} }
return map; return map;
@ -661,6 +751,8 @@ public class AttendanceServiceImpl implements AttendanceService {
List<TeamAttendancePunchStatisticsVO> absenteeismList = new ArrayList<>(); List<TeamAttendancePunchStatisticsVO> absenteeismList = new ArrayList<>();
//外勤 //外勤
List<TeamAttendancePunchStatisticsVO> fieldServiceList = new ArrayList<>(); List<TeamAttendancePunchStatisticsVO> fieldServiceList = new ArrayList<>();
//请假
List<TeamAttendancePunchStatisticsVO> leaveList = new ArrayList<>();
Map<Long, List<AttendancePunchRecordDO>> userPunchMap = list.stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getUserId)); Map<Long, List<AttendancePunchRecordDO>> userPunchMap = list.stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getUserId));
// -- 根据用户分组 // -- 根据用户分组
@ -690,6 +782,8 @@ public class AttendanceServiceImpl implements AttendanceService {
this.calculateEarlyDepartures(workMap, calculateNum); this.calculateEarlyDepartures(workMap, calculateNum);
// -- 缺卡 // -- 缺卡
this.calculateMissingCardsList(workMap, calculateNum); this.calculateMissingCardsList(workMap, calculateNum);
// -- 请假
this.calculateLeaveList(workMap, calculateNum);
// -- 旷工 // -- 旷工
this.calculateMiner(dayEntry.getValue(), calculateNum); this.calculateMiner(dayEntry.getValue(), calculateNum);
// -- 外勤 // -- 外勤
@ -733,6 +827,14 @@ public class AttendanceServiceImpl implements AttendanceService {
BeanUtil.copyProperties(averageWorkingHourVO, item); BeanUtil.copyProperties(averageWorkingHourVO, item);
fieldServiceList.add(item.setTop(calculateNum.getTotalFieldServiceNumber() + "").setDown("")); fieldServiceList.add(item.setTop(calculateNum.getTotalFieldServiceNumber() + "").setDown(""));
} }
// --- 请假漏卡
if (calculateNum.getTotalLeaveNumber() > 0) {
TeamAttendancePunchStatisticsVO item = new TeamAttendancePunchStatisticsVO();
BeanUtil.copyProperties(averageWorkingHourVO, item);
leaveList.add(item.setTop(calculateNum.getTotalLeaveNumber() + "次请假漏卡").setDown(""));
}
} }
vo.setAverageWorkingHoursList(averageWorkingHours); vo.setAverageWorkingHoursList(averageWorkingHours);
@ -741,6 +843,8 @@ public class AttendanceServiceImpl implements AttendanceService {
vo.setMissingCardList(missingCardList); vo.setMissingCardList(missingCardList);
vo.setAbsenteeismList(absenteeismList); vo.setAbsenteeismList(absenteeismList);
vo.setFieldServiceList(fieldServiceList); vo.setFieldServiceList(fieldServiceList);
vo.setLeaveList(leaveList);
int sum = averageWorkingHours.stream().mapToInt(a -> a.getCalculateNum().getTotalAttendanceDays()).sum(); int sum = averageWorkingHours.stream().mapToInt(a -> a.getCalculateNum().getTotalAttendanceDays()).sum();
TeamAttendanceStatisticsByCycleVO.TeamAttendanceStatisticsNumVO teamAttendanceStatisticsByCycleVO = new TeamAttendanceStatisticsByCycleVO.TeamAttendanceStatisticsNumVO(); TeamAttendanceStatisticsByCycleVO.TeamAttendanceStatisticsNumVO teamAttendanceStatisticsByCycleVO = new TeamAttendanceStatisticsByCycleVO.TeamAttendanceStatisticsNumVO();
teamAttendanceStatisticsByCycleVO.setAverageWorkingHours( teamAttendanceStatisticsByCycleVO.setAverageWorkingHours(
@ -750,6 +854,7 @@ public class AttendanceServiceImpl implements AttendanceService {
.setBeLateNum(beLateList.stream().mapToInt(a -> a.getCalculateNum().getTotalLateArrivalsNumber()).sum()) .setBeLateNum(beLateList.stream().mapToInt(a -> a.getCalculateNum().getTotalLateArrivalsNumber()).sum())
.setLeaveEarlyNum(leaveEarlyList.stream().mapToInt(a -> a.getCalculateNum().getTotalEarlyDeparturesNumber()).sum()) .setLeaveEarlyNum(leaveEarlyList.stream().mapToInt(a -> a.getCalculateNum().getTotalEarlyDeparturesNumber()).sum())
.setMissingCardNum(missingCardList.stream().mapToInt(a -> a.getCalculateNum().getTotalMissingCardsNumber()).sum()) .setMissingCardNum(missingCardList.stream().mapToInt(a -> a.getCalculateNum().getTotalMissingCardsNumber()).sum())
.setLeaveNum(leaveList.stream().mapToInt(a -> a.getCalculateNum().getTotalLeaveNumber()).sum())
.setAbsenteeismNum(absenteeismList.stream().mapToInt(a -> a.getCalculateNum().getTotalMinerDays()).sum()) .setAbsenteeismNum(absenteeismList.stream().mapToInt(a -> a.getCalculateNum().getTotalMinerDays()).sum())
.setFieldServiceNum(fieldServiceList.stream().mapToInt(a -> a.getCalculateNum().getTotalFieldServiceNumber()).sum()) .setFieldServiceNum(fieldServiceList.stream().mapToInt(a -> a.getCalculateNum().getTotalFieldServiceNumber()).sum())
); );
@ -913,6 +1018,10 @@ public class AttendanceServiceImpl implements AttendanceService {
//早退时长 //早退时长
String leaveEarly = this.calculateEarlyDepartures(workMap); String leaveEarly = this.calculateEarlyDepartures(workMap);
row.add(leaveEarly); row.add(leaveEarly);
//请假漏卡次数
String leaveList = this.calculateLeaveList(workMap);
row.add(leaveList);
Map<Integer, Integer> missingCardsMap = this.calculateCommuteMissingCardsList(workMap); Map<Integer, Integer> missingCardsMap = this.calculateCommuteMissingCardsList(workMap);
row.add(missingCardsMap.get(Constants.ZERO).toString()); row.add(missingCardsMap.get(Constants.ZERO).toString());
row.add(missingCardsMap.get(Constants.ONE).toString()); row.add(missingCardsMap.get(Constants.ONE).toString());
@ -926,7 +1035,8 @@ public class AttendanceServiceImpl implements AttendanceService {
// 这里 需要指定写用哪个class去写然后写到第一个sheet名字为模板 然后文件流会自动关闭 // 这里 需要指定写用哪个class去写然后写到第一个sheet名字为模板 然后文件流会自动关闭
try { try {
EasyExcel.write(response.getOutputStream()) // EasyExcel.write(response.getOutputStream())
EasyExcel.write("/Users/aikai/Downloads/" + System.currentTimeMillis() + "考勤统计按日导出.xls")
.head(generateDailyHead(headTitle, detailedHead, maxSize)) .head(generateDailyHead(headTitle, detailedHead, maxSize))
.autoCloseStream(false) .autoCloseStream(false)
.excelType(ExcelTypeEnum.XLS) .excelType(ExcelTypeEnum.XLS)
@ -934,7 +1044,7 @@ public class AttendanceServiceImpl implements AttendanceService {
.sheet("考勤统计按日导出") .sheet("考勤统计按日导出")
.doWrite(data); .doWrite(data);
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("考勤统计", StandardCharsets.UTF_8.name())); response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("考勤统计", StandardCharsets.UTF_8.name()));
response.setContentType("application/vnd.ms-excel;charset=UTF-8"); // response.setContentType("application/vnd.ms-excel;charset=UTF-8");
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
@ -980,6 +1090,8 @@ public class AttendanceServiceImpl implements AttendanceService {
return "未打卡"; return "未打卡";
} else if (status == 5) { } else if (status == 5) {
return "补卡"; return "补卡";
} else if (status == 6) {
return "请假";
} }
return ""; return "";
} }
@ -1035,6 +1147,8 @@ public class AttendanceServiceImpl implements AttendanceService {
this.calculateCommuteMissingCardsList(workMap, calculateNum); this.calculateCommuteMissingCardsList(workMap, calculateNum);
// -- 旷工 // -- 旷工
this.calculateMiner(dayEntry.getValue(), calculateNum); this.calculateMiner(dayEntry.getValue(), calculateNum);
// -- 请假
this.calculateLeaveList(workMap, calculateNum);
} }
//休息天数 //休息天数
this.calculateRestDay(dateList, dayMap.keySet().stream().distinct().collect(Collectors.toList()), calculateNum); this.calculateRestDay(dateList, dayMap.keySet().stream().distinct().collect(Collectors.toList()), calculateNum);
@ -1058,6 +1172,7 @@ public class AttendanceServiceImpl implements AttendanceService {
row.add(String.valueOf(calculateNum.getTotalEarlyDeparturesNumber())); row.add(String.valueOf(calculateNum.getTotalEarlyDeparturesNumber()));
row.add(String.valueOf(calculateNum.getTotalEarlyDeparturesTimeStr())); row.add(String.valueOf(calculateNum.getTotalEarlyDeparturesTimeStr()));
row.add(String.valueOf(calculateNum.getTotalLeaveNumber()));
row.add(String.valueOf(calculateNum.getTotalUpMissingCardsNumber())); row.add(String.valueOf(calculateNum.getTotalUpMissingCardsNumber()));
row.add(String.valueOf(calculateNum.getTotalDownMissingCardsNumber())); row.add(String.valueOf(calculateNum.getTotalDownMissingCardsNumber()));
row.add(String.valueOf(calculateNum.getTotalMinerDays())); row.add(String.valueOf(calculateNum.getTotalMinerDays()));
@ -1067,7 +1182,8 @@ public class AttendanceServiceImpl implements AttendanceService {
} }
try { try {
EasyExcel.write(response.getOutputStream()) // EasyExcel.write(response.getOutputStream())
EasyExcel.write("/Users/aikai/Downloads/" + System.currentTimeMillis() + "考勤统计按日导出.xls")
.head(generateHead(headTitle, detailedHead)) .head(generateHead(headTitle, detailedHead))
.autoCloseStream(false) .autoCloseStream(false)
.excelType(ExcelTypeEnum.XLS) .excelType(ExcelTypeEnum.XLS)
@ -1075,7 +1191,7 @@ public class AttendanceServiceImpl implements AttendanceService {
.sheet("考勤统计按月导出") .sheet("考勤统计按月导出")
.doWrite(data); .doWrite(data);
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("考勤统计", StandardCharsets.UTF_8.name())); response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("考勤统计", StandardCharsets.UTF_8.name()));
response.setContentType("application/vnd.ms-excel;charset=UTF-8"); // response.setContentType("application/vnd.ms-excel;charset=UTF-8");
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
@ -1096,6 +1212,7 @@ public class AttendanceServiceImpl implements AttendanceService {
head.add(Arrays.asList(headTitle, detailedHead, "迟到时长", "迟到时长")); head.add(Arrays.asList(headTitle, detailedHead, "迟到时长", "迟到时长"));
head.add(Arrays.asList(headTitle, detailedHead, "早退次数", "早退次数")); head.add(Arrays.asList(headTitle, detailedHead, "早退次数", "早退次数"));
head.add(Arrays.asList(headTitle, detailedHead, "早退时长", "早退时长")); head.add(Arrays.asList(headTitle, detailedHead, "早退时长", "早退时长"));
head.add(Arrays.asList(headTitle, detailedHead, "请假漏卡次数", "请假漏卡次数"));
head.add(Arrays.asList(headTitle, detailedHead, "上班缺卡次数", "上班缺卡次数")); head.add(Arrays.asList(headTitle, detailedHead, "上班缺卡次数", "上班缺卡次数"));
head.add(Arrays.asList(headTitle, detailedHead, "下班缺卡次数", "下班缺卡次数")); head.add(Arrays.asList(headTitle, detailedHead, "下班缺卡次数", "下班缺卡次数"));
head.add(Arrays.asList(headTitle, detailedHead, "旷工天数", "旷工天数")); head.add(Arrays.asList(headTitle, detailedHead, "旷工天数", "旷工天数"));
@ -1140,6 +1257,7 @@ public class AttendanceServiceImpl implements AttendanceService {
head.add(Arrays.asList(headTitle, detailedHead, "迟到时长", "迟到时长")); head.add(Arrays.asList(headTitle, detailedHead, "迟到时长", "迟到时长"));
// head.add(Arrays.asList(headTitle, detailedHead, "早退次数", "早退次数")); // head.add(Arrays.asList(headTitle, detailedHead, "早退次数", "早退次数"));
head.add(Arrays.asList(headTitle, detailedHead, "早退时长", "早退时长")); head.add(Arrays.asList(headTitle, detailedHead, "早退时长", "早退时长"));
head.add(Arrays.asList(headTitle, detailedHead, "请假漏卡次数", "请假漏卡次数"));
head.add(Arrays.asList(headTitle, detailedHead, "上班缺卡次数", "上班缺卡次数")); head.add(Arrays.asList(headTitle, detailedHead, "上班缺卡次数", "上班缺卡次数"));
head.add(Arrays.asList(headTitle, detailedHead, "下班缺卡次数", "下班缺卡次数")); head.add(Arrays.asList(headTitle, detailedHead, "下班缺卡次数", "下班缺卡次数"));
// head.add(Arrays.asList(headTitle, detailedHead, "旷工天数", "旷工天数")); // head.add(Arrays.asList(headTitle, detailedHead, "旷工天数", "旷工天数"));
@ -1319,7 +1437,7 @@ public class AttendanceServiceImpl implements AttendanceService {
} }
/** /**
* 迟到次数 * 缺卡次数 - 上下班区分开
* *
* @param workMap * @param workMap
*/ */

View File

@ -9,13 +9,15 @@ import java.time.LocalDateTime;
public class AttendanceOnTheDayDTO { public class AttendanceOnTheDayDTO {
/** /**
* 打卡状态 0正常 1迟到 2早退 3缺卡 4未打卡(还没到打卡时间) * 打卡状态 0正常 1迟到 2早退 3缺卡 4未打卡(还没到打卡时间) 5补卡 6请假
*/ */
public static final Integer PUNCH_STATUS_NORMAL = 0; public static final Integer PUNCH_STATUS_NORMAL = 0;
public static final Integer PUNCH_STATUS_LATE = 1; public static final Integer PUNCH_STATUS_LATE = 1;
public static final Integer PUNCH_STATUS_LEAVE_EARLY = 2; public static final Integer PUNCH_STATUS_LEAVE_EARLY = 2;
public static final Integer PUNCH_STATUS_MISS = 3; public static final Integer PUNCH_STATUS_MISS = 3;
public static final Integer PUNCH_STATUS_UN_PUNCH = 4; public static final Integer PUNCH_STATUS_UN_PUNCH = 4;
public static final Integer REPLACEMENT_CARD = 5;
public static final Integer ASK_FOR_LEAVE = 6;
@Schema(description = "子表id") @Schema(description = "子表id")
private Long id; private Long id;
@ -44,7 +46,7 @@ public class AttendanceOnTheDayDTO {
@Schema(description = "后打卡时间(分钟) 默认 120分钟") @Schema(description = "后打卡时间(分钟) 默认 120分钟")
private Integer afterPunchTime; private Integer afterPunchTime;
@Schema(description = "打卡状态 0正常 1迟到 2早退 3缺卡 4未打卡(还没到打卡时间)") @Schema(description = "打卡状态 0正常 1迟到 2早退 3缺卡 4未打卡(还没到打卡时间) 5补卡 6请假")
private Integer punchStatus; private Integer punchStatus;
@Schema(description = "是否外勤 0否 1是", example = "1") @Schema(description = "是否外勤 0否 1是", example = "1")

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.system.service.attendance.punchrecord; package cn.iocoder.yudao.module.system.service.attendance.punchrecord;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.api.attendance.dto.AttendancePunchRecordDTO;
import cn.iocoder.yudao.module.system.controller.admin.punchrecord.vo.AttendancePunchRecordPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.punchrecord.vo.AttendancePunchRecordPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.punchrecord.vo.AttendancePunchRecordSaveReqVO; import cn.iocoder.yudao.module.system.controller.admin.punchrecord.vo.AttendancePunchRecordSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.group.AttendanceGroupDO; import cn.iocoder.yudao.module.system.dal.dataobject.attendance.group.AttendanceGroupDO;
@ -112,4 +113,11 @@ public interface AttendancePunchRecordService {
* @param editList * @param editList
*/ */
void batchUpdate(List<AttendancePunchRecordDO> editList); void batchUpdate(List<AttendancePunchRecordDO> editList);
/**
* 请假修改状态
*
* @param dto
*/
void askingForLeaveAfterwardsToModifyAttendance(AttendancePunchRecordDTO dto);
} }

View File

@ -6,9 +6,11 @@ import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.Constants; import cn.iocoder.yudao.framework.common.Constants;
import cn.iocoder.yudao.framework.common.pojo.BpmOALeaveDTO;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.system.api.attendance.dto.AttendancePunchRecordDTO;
import cn.iocoder.yudao.module.system.controller.admin.punchrecord.vo.AttendancePunchRecordPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.punchrecord.vo.AttendancePunchRecordPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.punchrecord.vo.AttendancePunchRecordSaveReqVO; import cn.iocoder.yudao.module.system.controller.admin.punchrecord.vo.AttendancePunchRecordSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.group.AttendanceGroupDO; import cn.iocoder.yudao.module.system.dal.dataobject.attendance.group.AttendanceGroupDO;
@ -26,7 +28,9 @@ import cn.iocoder.yudao.module.system.service.attendance.punch.dto.AttendanceOnT
import cn.iocoder.yudao.module.system.service.attendance.scheduling.AttendanceSchedulingService; import cn.iocoder.yudao.module.system.service.attendance.scheduling.AttendanceSchedulingService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService; import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -206,7 +210,7 @@ public class AttendancePunchRecordServiceImpl implements AttendancePunchRecordSe
// //
// } // }
// } // }
Map<String, String[]> delLeaveMap = new HashMap<>();
for (Map.Entry<Long, Long> entry : map.entrySet()) { for (Map.Entry<Long, Long> entry : map.entrySet()) {
String key = Constants.ATTENDANCE + Constants.UNDERLINE + entry.getKey() + Constants.UNDERLINE; // + 时间 String key = Constants.ATTENDANCE + Constants.UNDERLINE + entry.getKey() + Constants.UNDERLINE; // + 时间
AttendanceGroupDO attendanceGroupDO = groupMap.get(entry.getKey()); AttendanceGroupDO attendanceGroupDO = groupMap.get(entry.getKey());
@ -221,7 +225,9 @@ public class AttendancePunchRecordServiceImpl implements AttendancePunchRecordSe
List<AttendanceOnTheDayDTO> attendanceOnTheDayDTOS = attendanceService.buildAttendanceOnTheDay(attendanceGroupShiftItemDOS); List<AttendanceOnTheDayDTO> attendanceOnTheDayDTOS = attendanceService.buildAttendanceOnTheDay(attendanceGroupShiftItemDOS);
for (Long userId : userIds) { for (Long userId : userIds) {
AdminUserDO adminUserDO = userMap.get(userId); AdminUserDO adminUserDO = userMap.get(userId);
Map<Object, Object> leaveRedisMap = this.getAttendanceLeaveRedisMap(userId);
Long deptId = adminUserDO == null ? null : adminUserDO.getDeptId(); Long deptId = adminUserDO == null ? null : adminUserDO.getDeptId();
List<String> leaveIds = new ArrayList<>();
for (AttendanceOnTheDayDTO attendanceOnTheDayDTO : attendanceOnTheDayDTOS) { for (AttendanceOnTheDayDTO attendanceOnTheDayDTO : attendanceOnTheDayDTOS) {
AttendancePunchRecordDO attendancePunchRecordDO = new AttendancePunchRecordDO(); AttendancePunchRecordDO attendancePunchRecordDO = new AttendancePunchRecordDO();
attendancePunchRecordDO.setUserId(userId); attendancePunchRecordDO.setUserId(userId);
@ -239,21 +245,67 @@ public class AttendancePunchRecordServiceImpl implements AttendancePunchRecordSe
attendancePunchRecordDO.setFieldServiceFlag(Constants.FALSE); attendancePunchRecordDO.setFieldServiceFlag(Constants.FALSE);
attendancePunchRecordDO.setNextDayFlag(Constants.TRUE); attendancePunchRecordDO.setNextDayFlag(Constants.TRUE);
attendancePunchRecordDO.setDayTime(time); attendancePunchRecordDO.setDayTime(time);
LocalDateTime shouldPunchTime = LocalDateTime.ofInstant(DateUtils.buildHHmmTime(attendanceOnTheDayDTO.getTime(), LocalDateTime shouldPunchTime = LocalDateTime.ofInstant(DateUtils.buildHHmmTime(attendanceOnTheDayDTO.getTime(),
(attendanceOnTheDayDTO.getNextDayFlag() == 0 ? localDateTime : nextDayLocalDateTime)).toInstant(), ZoneId.systemDefault()); (attendanceOnTheDayDTO.getNextDayFlag() == 0 ? localDateTime : nextDayLocalDateTime)).toInstant(), ZoneId.systemDefault());
// -- 请假插入预设
for (Map.Entry<Object, Object> leaveEntry : leaveRedisMap.entrySet()) {
BpmOALeaveDTO dto = JSONUtil.toBean(leaveEntry.getValue().toString(), BpmOALeaveDTO.class);
// - 如果在这个区间之内 - 那么就是需要设为请假的
if ((dto.getStartTime().isBefore(shouldPunchTime) || dto.getStartTime().equals(shouldPunchTime)) &&
(dto.getEndTime().isAfter(shouldPunchTime) || dto.getEndTime().equals(shouldPunchTime))) {
attendancePunchRecordDO.setStatus(AttendanceOnTheDayDTO.ASK_FOR_LEAVE);
attendancePunchRecordDO.setLeaveId(dto.getId());
} else if (dto.getEndTime().isBefore(shouldPunchTime)) {
leaveIds.add(dto.getId().toString());
}
}
String actualDayTime = shouldPunchTime.format(Constants.REPO_DATE_FORMAT);
attendancePunchRecordDO.setActualDayTime(actualDayTime);
attendancePunchRecordDO.setShouldPunchTime(shouldPunchTime); attendancePunchRecordDO.setShouldPunchTime(shouldPunchTime);
attendancePunchRecordDO.setLatestPunchTime(shouldPunchTime.plusMinutes(attendanceOnTheDayDTO.getAfterPunchTime())); attendancePunchRecordDO.setLatestPunchTime(shouldPunchTime.plusMinutes(attendanceOnTheDayDTO.getAfterPunchTime()));
attendancePunchRecordDOList.add(attendancePunchRecordDO); attendancePunchRecordDOList.add(attendancePunchRecordDO);
} }
stringRedisTemplate.opsForHash().put(key + time, userId.toString(), JSONUtil.toJsonStr(attendanceOnTheDayDTOS)); stringRedisTemplate.opsForHash().put(key + time, userId.toString(), JSONUtil.toJsonStr(attendanceOnTheDayDTOS));
if (!leaveIds.isEmpty()) {
delLeaveMap.put("leave" + "_" + userId, leaveIds.toArray(new String[0]));
}
} }
//设置缓存 2天 //设置缓存 2天
stringRedisTemplate.expire(key + time, 2, TimeUnit.DAYS); stringRedisTemplate.expire(key + time, 2, TimeUnit.DAYS);
} }
// -- 删除redis中的请假数据
this.delLeave(delLeaveMap);
// -- 批量 // -- 批量
this.saveBatch(attendancePunchRecordDOList); this.saveBatch(attendancePunchRecordDOList);
} }
/**
* 删除redis中的请假数据
*
* @param delLeaveMap
*/
private void delLeave(Map<String, String[]> delLeaveMap) {
if (MapUtil.isNotEmpty(delLeaveMap)) {
for (Map.Entry<String, String[]> entry : delLeaveMap.entrySet()) {
stringRedisTemplate.opsForHash().delete(entry.getKey(), entry.getValue());
}
}
}
/**
* 获取用户请假redisMap
*
* @param userId
* @return
*/
private Map<Object, Object> getAttendanceLeaveRedisMap(Long userId) {
HashOperations<String, Object, Object> hashOps = stringRedisTemplate.opsForHash();
String key = "leave" + "_" + userId.toString();
return hashOps.entries(key);
}
@Override @Override
public List<AttendancePunchRecordDO> getNotReminded(LocalDateTime localDateTime) { public List<AttendancePunchRecordDO> getNotReminded(LocalDateTime localDateTime) {
String targetDayStr = localDateTime.format(Constants.REPO_DATE_FORMAT); String targetDayStr = localDateTime.format(Constants.REPO_DATE_FORMAT);
@ -268,6 +320,17 @@ public class AttendancePunchRecordServiceImpl implements AttendancePunchRecordSe
punchRecordMapper.updateBatch(editList); punchRecordMapper.updateBatch(editList);
} }
@Override
public void askingForLeaveAfterwardsToModifyAttendance(AttendancePunchRecordDTO dto) {
punchRecordMapper.update(new AttendancePunchRecordDO()
.setStatus(AttendanceOnTheDayDTO.ASK_FOR_LEAVE)
.setLeaveId(dto.getLeaveId()),
new LambdaUpdateWrapper<AttendancePunchRecordDO>().eq(AttendancePunchRecordDO::getUserId, dto.getUserId())
.ge(AttendancePunchRecordDO::getShouldPunchTime, dto.getStartTime())
.le(AttendancePunchRecordDO::getShouldPunchTime, dto.getEndTime()));
}
private Map<Long, Long> getAttendanceGroupShiftIdGroupByGroup(List<AttendanceGroupDO> attendanceGroupDOS, LocalDateTime localDateTime) { private Map<Long, Long> getAttendanceGroupShiftIdGroupByGroup(List<AttendanceGroupDO> attendanceGroupDOS, LocalDateTime localDateTime) {
Map<Long, Long> map = new HashMap<>(); Map<Long, Long> map = new HashMap<>();
List<AttendanceGroupDO> fixedList = attendanceGroupDOS.stream().filter(a -> a.getType().equals(1)).collect(Collectors.toList()); List<AttendanceGroupDO> fixedList = attendanceGroupDOS.stream().filter(a -> a.getType().equals(1)).collect(Collectors.toList());