考勤逻辑重构代码

This commit is contained in:
fl 2025-02-17 18:21:08 +08:00
parent 908dd61ff7
commit c0332b2548
3 changed files with 973 additions and 0 deletions

View File

@ -0,0 +1,50 @@
package com.bonus.system.att.service;
import com.bonus.system.api.domain.MapVo;
import com.bonus.system.att.entity.OrgChangeBean;
import java.util.List;
/**
* 考勤计算
* @author fly
*/
public interface AttCalService {
void getAttendanceData(String pushDate, int pushType);
/**
* 插入模板数据
* @param pushDate
* @param pushType
*/
void insertAttTempData(String pushDate, int pushType);
/**
* 待考勤人员列表
* @param pushDate
*/
void insertAttDateHistory(String pushDate);
/**
* 考勤数据应用步骤一考勤数据更新
* 建议一小时一次
* @param pushDate 时间
* @param pushType 1:自动推送当天 2:手动历史
*/
void updateAttData(String pushDate, int pushType);
/**
* 考勤数据应用步骤二旷工状态更新
* 每天12点将当天未打卡考勤人员上班置为旷工
* 每天晚上22点将下班未打卡置为旷工
* @param pushDate 时间
* @param pushType 1:自动推送当天 2:手动历史
*/
void updateAbsenteeismData(String pushDate, int pushType);
/**
* 请假数据应用步骤一法假数据更新
* 法定节假日不会随时间变化一天执行一次就可以了
* @param pushDate 时间
*/
void updateLegalHolidayData(String pushDate);
}

View File

@ -0,0 +1,798 @@
package com.bonus.system.att.service;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson2.JSONObject;
import com.bonus.common.core.utils.DateTimeHelper;
import com.bonus.common.core.utils.DateUtils;
import com.bonus.common.core.utils.StringUtils;
import com.bonus.common.security.utils.SecurityUtils;
import com.bonus.system.api.domain.MapVo;
import com.bonus.system.api.domain.SysUser;
import com.bonus.system.att.dao.AttGroupDao;
import com.bonus.system.att.dao.AttSourceDataDao;
import com.bonus.system.att.dao.OrgChangeDao;
import com.bonus.system.att.entity.*;
import com.bonus.system.att.utils.AddressCoordinateFormatUtil;
import com.bonus.system.att.utils.AttTimeUtil;
import com.bonus.system.att.utils.WorkdayCalculator;
import com.bonus.system.dept.dao.ProDeptRoleDao;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
/**
* 组织架构-业务层
*
* @author zys
*/
@Slf4j
@Service("AttCalService")
public class AttCalServiceImpl implements AttCalService {
@Resource(name = "attSourceDataDao")
private AttSourceDataDao attSourceDataDao;
@Resource(name = "attGroupDao")
private AttGroupDao attGroupDao;
@Resource(name = "sqlSessionTemplate")
private SqlSessionTemplate sqlSessionTemplate;
/**
* 待考勤人员列表
* 在模版数据之前生成
*
* @param pushDate 时间(可为空为空则推历史60天内没生成的数据不为空则推指定日期)
*/
@Override
public void insertAttDateHistory(String pushDate) {
List<String> dateList = new ArrayList<>();
if (StringUtils.isEmpty(pushDate)) {
String startDate = DateUtil.today();
// 解析 startDate 字符串到 LocalDate 对象
LocalDate date = LocalDate.parse(startDate, DateTimeFormatter.ISO_LOCAL_DATE);
// 向前推n天
int n = 60;
LocalDate newDate = date.minusDays(n);
// 如果需要将结果格式化为特定格式的字符串可以使用以下代码
String formattedNewDate = newDate.format(DateTimeFormatter.ISO_LOCAL_DATE);
//向前推1个月的时间检索缺少哪一天推送哪一天
List<String> dateListOne = attSourceDataDao.getAttSettingDate(startDate, n);
dateList = AttTimeUtil.getStrDateListBetween(formattedNewDate, startDate);
// dateList 中移除所有在 dateListOne 中存在的元素
dateListOne.removeIf(startDate::contains);
dateList.removeIf(dateListOne::contains);
} else {
dateList.add(pushDate);
}
for (String currentDay : dateList) {
List<AttGroupBean> attList = attSourceDataDao.getAttSettingHistoryDate(currentDay);
if (!attList.isEmpty()) {
attSourceDataDao.insertAttSettingHistoryData(attList);
}
}
}
/**
* 考勤数据应用步骤一考勤数据更新
* 建议一小时一次
*
* @param pushDate 时间
* @param pushType 1:自动推送当天 2:手动历史
*/
@Override
public void updateAttData(String pushDate, int pushType) {
//查出人员考勤记录
AtomicReference<List<AttSourceDataBean>> sourceDataList = new AtomicReference<>(attSourceDataDao.getSourceAttData(pushDate, pushType));
//查出考勤组
List<AttGroupBean> groupList = getGroupData(pushDate);
//
groupList.forEach(c -> {
List<AttSourceDataBean> list = Collections.emptyList();
//考勤规则里面的attType 1 固定打卡 2 自由打卡
if ("1".equals(c.getAttType())) {
list = groupFixedData(sourceDataList.get().stream()
.filter(a -> "1".equals(a.getGroupType()) &&
a.getGroupId() == c.getGroupId())
.collect(Collectors.toList()), c);
} else if ("2".equals(c.getAttType())) {
list = groupFreeData(sourceDataList.get().stream()
.filter(a -> "2".equals(a.getGroupType()) &&
a.getGroupId() == c.getGroupId())
.collect(Collectors.toList()), c);
}
//修改考勤记录 att_data
if (!list.isEmpty()) {
if ("1".equals(c.getAttType())) {
//固定打卡
// 定义时间格式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
// 移除移除 attType 2 attCurrentTime 在上午 5 点到 12 点之间的元素
list.removeIf(record -> {
String attType = record.getAttType();
String attCurrentTime = record.getAttCurrentTime();
if (!"".equals(attCurrentTime)) {
if (attCurrentTime.contains(" ")) {
attCurrentTime = attCurrentTime.split(" ")[0];
}
LocalDateTime time = LocalDateTime.parse(attCurrentTime, formatter);
// 提取时间部分
LocalTime localTime = time.toLocalTime();
boolean condition1 = "2".equals(attType) && localTime.isAfter(LocalTime.of(5, 0)) && localTime.isBefore(LocalTime.of(12, 0));
boolean condition2 = "1".equals(attType) && localTime.isAfter(LocalTime.of(12, 0)) && localTime.isBefore(LocalTime.of(23, 59));
return condition1 || condition2;
} else {
return false;
}
});
// 插入剩余的元素
attSourceDataDao.updateAttStatusData(list);
} else if ("2".equals(c.getAttType())) {
//自由打卡
attSourceDataDao.updateAttStatusData(list);
}
}
});
}
/**
* 考勤数据应用步骤二旷工状态更新
* 每天12点将当天未打卡考勤人员上班置为旷工
* 每天晚上22点将下班未打卡置为旷工
* @param pushDate 时间
* @param pushType 1:自动推送当天 2:手动历史
*/
@Override
public void updateAbsenteeismData(String pushDate, int pushType) {
List<AttDataBean> list = new ArrayList<>();
List<AttDataBean> listUpdate = new ArrayList<>();
if(pushType == 1){
//判断是否大于12点大于则更新所有attType = 1的未打卡状态为旷工
//判断是否大于22点大于则更新所有未打卡状态为旷工
boolean pm = AttTimeUtil.timeCheck(21, 59);
if(pm){
list = attSourceDataDao.getAttDataByStatus(pushDate,"");
listUpdate = attSourceDataDao.getAttDataUpdateByStatus(pushDate,"");
}else{
boolean am = AttTimeUtil.timeCheck(11, 59);
if(am){
list = attSourceDataDao.getAttDataByStatus(pushDate,"1");
listUpdate = attSourceDataDao.getAttDataUpdateByStatus(pushDate,"1");
}
}
}else if(pushType == 2){
list = attSourceDataDao.getAttDataByStatus(pushDate,"");
listUpdate = attSourceDataDao.getAttDataUpdateByStatus(pushDate,"");
}
if(list != null && !list.isEmpty()){
list.forEach(c -> c.setAttStatus("3"));
attSourceDataDao.updateAttDataList(list);
}
if(listUpdate != null && !listUpdate.isEmpty()){
listUpdate.forEach(c -> c.setAttStatus("3"));
attSourceDataDao.updateAttDataUpdateList(listUpdate);
}
}
@Override
public void updateLegalHolidayData(String pushDate) {
//查询当日是否是法假
List<Holiday> holidays = attSourceDataDao.selectHolidayByDay(pushDate);
if(!holidays.isEmpty()){
//当日为节假日
List<AttGroupBean> groupList = Optional.ofNullable(attGroupDao.selectAttGroupList(new AttGroupBean())).
orElseGet(ArrayList::new);
List<AttGroupBean> attList = attSourceDataDao.getAttSettingHistoryDate(pushDate);
groupList.forEach(c -> {
if(c.getIsHaveHoliday() == 1){
List<AttGroupBean> collect = attList.stream().filter(a -> Objects.equals(a.getGroupId(), c.getGroupId())).collect(Collectors.toList());
if(!collect.isEmpty()){
// 创建一个新的 List<AttDataBean>
List<AttDataBean> listUpdate = new ArrayList<>();
collect.forEach(v -> {
AttDataBean dataBean = new AttDataBean();
dataBean.setUserId(v.getUserId());
dataBean.setAttCurrentDay(v.getCurrentDay());
dataBean.setAttStatus("11");
listUpdate.add(dataBean);
});
attSourceDataDao.updateAttDataList(listUpdate);
attSourceDataDao.updateAttDataUpdateList(listUpdate);
}
}
});
}
}
/**
* 插入模板数据
*
* @param pushDate
* @param pushType 1定时 2手动
*/
@Override
public void insertAttTempData(String pushDate, int pushType) {
//查询当天应考勤人员列表
List<AttDataBean> listPerson = attSourceDataDao.getAttPerson(pushDate);
if (pushType == 1) {
//只会在凌晨执行一次
attSourceDataDao.insertAttDataList(listPerson);
} else if (pushType == 2) {
//1.查询出历史当天已插入模版
//2.查询出历史当天考勤列表
//3.对比增量更新
List<Long> personId = attSourceDataDao.getAttDataPerson(pushDate);
listPerson.removeIf(c -> personId.contains(c.getUserId()));
attSourceDataDao.insertAttDataList(listPerson);
}
}
/**
* 获取考勤数据数据拉取
*
* @param pushDate 日期
* @param pushType 1:自动推送当天 2:手动历史
*/
@Override
public void getAttendanceData(String pushDate, int pushType) {
List<AttSourceDataBean> qsyAttData = getQsyAttendanceData(pushDate, pushType);
List<AttSourceDataBean> machineAttSourceList = getMachineAttendanceData(pushDate, pushType);
//新增考勤到考勤来源表(数据过多时分批次插入)
machineAttSourceList.addAll(qsyAttData);
insertAttSourceData(machineAttSourceList);
}
/**
* 获取黔送云的考勤数据
*/
private List<AttSourceDataBean> getQsyAttendanceData(String pushDate, int pushType) {
List<AttSourceDataBean> attSourceList = Optional.ofNullable(attSourceDataDao.getQsyAttendances(pushDate, pushType)).
orElseGet(ArrayList::new);
if (!attSourceList.isEmpty()) {
attSourceList.forEach(c -> {
String address;
String province;
try {
JSONObject result = AddressCoordinateFormatUtil.
coordinateToAddress2(c.getAttLon(), c.getAttLat());
address = result.getString("formatted_address");
province = result.getJSONObject("addressComponent").getString("province");
} catch (Exception e) {
address = "地址未转化成功";
province = "地址未转化成功";
}
c.setAttAddress(address);
c.setProvince(province);
}
);
}
return attSourceList;
}
/**
* 获取考勤机的考勤数据
*/
private List<AttSourceDataBean> getMachineAttendanceData(String pushDate, int pushType) {
return Optional.ofNullable(attSourceDataDao.getMachineAttendances(pushDate, pushType)).
orElseGet(ArrayList::new);
}
/**
* 新增考勤到数据库(考勤来源表)
* 处理过后的数据新增到考勤来源表
*
* @param attSourceList 考勤数据
*/
public void insertAttSourceData(List<AttSourceDataBean> attSourceList) {
if (attSourceList == null || attSourceList.isEmpty()) {
return;
}
attSourceList.forEach(c -> {
if ("0".equals(c.getAttType())) {
//判断打卡类型
int i = AttTimeUtil.getAttTypeByTime(c.getAttCurrentTime());
c.setAttType(String.valueOf(i));
}
//判断打卡时间归属于当天还是昨天
AttTimeUtil.changeAttCurrentDay(c);
});
int batchSize = 3000;
int totalBatches = (int) Math.ceil((double) attSourceList.size() / batchSize);
try {
for (int i = 0; i < totalBatches; i++) {
int fromIndex = i * batchSize;
int toIndex = Math.min(fromIndex + batchSize, attSourceList.size());
List<AttSourceDataBean> batch = attSourceList.subList(fromIndex, toIndex);
attSourceDataDao.insertAttSourceData(batch);
if ((i + 1) % 100 == 0 || i + 1 == totalBatches) {
sqlSessionTemplate.flushStatements(); // 提交事务
sqlSessionTemplate.clearCache(); // 清理缓存
}
}
} catch (Exception e) {
throw new RuntimeException("考勤数据抓取到数据库出错啦", e);
}
}
/**
* 查出每一个考勤组的应出勤天数以及考勤组的规则
*
* @return 考勤组集合
*/
public List<AttGroupBean> getGroupData(String pushDate) {
//查出考勤组的数据
List<AttGroupBean> groupList = Optional.ofNullable(attGroupDao.selectAttGroupList(new AttGroupBean())).
orElseGet(ArrayList::new);
if (groupList.isEmpty()) {
return new ArrayList<>();
}
//查询当月否有节假日或补班
List<Holiday> holidays = attSourceDataDao.selectHolidayByMonth(pushDate);
groupList.forEach(c -> {
//应考勤天
List<String> attDayList = WorkdayCalculator.getWorkDay(c.getAttDay(),
Integer.parseInt(c.getAttType()), holidays, pushDate);
c.setAttWorkDayBean(new PersonAttWorkDayBean(c.getGroupId(), attDayList.size(), attDayList));
});
return groupList;
}
/**
* 固定考勤数据计算
*
* @param list 考勤来源数据
* @param attGroupBean 考勤组数据
* @return 考勤数据
*/
private List<AttSourceDataBean> groupFixedData(List<AttSourceDataBean> list,
AttGroupBean attGroupBean) {
List<AttSourceDataBean> newList = new ArrayList<>();
//分组排序(将每个人每天在自己考勤组的数据分组)
Map<String, List<AttSourceDataBean>> groupedItems = list.stream()
.collect(Collectors.groupingBy(
c -> c.getUserId() + "|" + c.getAttCurrentDay(),
Collectors.collectingAndThen(
Collectors.toList(),
v -> {
v.sort(Comparator.comparing(AttSourceDataBean::getAttCurrentTime));
return v;
}
)
));
//固定打卡以最靠近上班打卡时间前的出闸机的时间为基准去除掉之前的所有打卡数据
AttTimeUtil.processGroupedItems(groupedItems, attGroupBean);
groupedItems.forEach((c, v) -> {
//第一次上班时间
AttSourceDataBean frontToWorkBean = v.stream()
.filter(a -> a.getAttType().equals("1"))
.collect(Collectors.toList())
.stream().min(Comparator.comparing(AttSourceDataBean::getAttCurrentTime)).orElse(null);
//最新一次下班时间
AttSourceDataBean backOffWorkBean = v.stream()
.filter(a -> a.getAttType().equals("2"))
.collect(Collectors.toList())
.stream().max(Comparator.comparing(AttSourceDataBean::getAttCurrentTime)).orElse(null);
if (backOffWorkBean != null) {
//最新一次下班时间插入list
newList.add(backOffWorkBean);
}
//没有下班卡则添加第一次打卡数据
newList.add(frontToWorkBean);
//获取工作时间外出记录并存储支线不影响
addWorkTimeOutRecord(v, attGroupBean);
});
Iterator<AttSourceDataBean> iterator = newList.iterator();
while (iterator.hasNext()) {
if (iterator.next() == null) {
iterator.remove();
}
}
processAttendanceStatus(newList, attGroupBean.getToWorkTime(), attGroupBean.getOffWorkTime(), attGroupBean.getLateMinute(),
attGroupBean.getAbsenteeismLateMinute(), attGroupBean.getLeaveMinute(), attGroupBean.getAbsenteeismLeaveMinute());
return newList;
}
/**
* 自由考勤数据计算
*
* @param list 考勤来源数据
* @param attGroupBean 考勤组数据
* @return 考勤数据
*/
private List<AttSourceDataBean> groupFreeData(List<AttSourceDataBean> list, AttGroupBean attGroupBean) {
List<AttSourceDataBean> newList = new ArrayList<>();
//分组排序(将每个人每天在自己考勤组的数据分组)
Map<String, List<AttSourceDataBean>> groupedItems = list.stream()
.collect(Collectors.groupingBy(
c -> c.getUserId() + "|" + c.getAttCurrentDay(),
Collectors.collectingAndThen(
Collectors.toList(),
v -> {
v.sort(Comparator.comparing(AttSourceDataBean::getAttCurrentTime));
return v;
}
)
));
groupedItems.forEach((c, v) -> {
//第一次上班时间
AttSourceDataBean frontToWorkBean = v.stream()
.filter(a -> a.getAttType().equals("1"))
.collect(Collectors.toList())
.stream().min(Comparator.comparing(AttSourceDataBean::getAttCurrentTime)).orElse(null);
//最新一次下班时间
AttSourceDataBean backOffWorkBean = v.stream()
.filter(a -> a.getAttType().equals("2"))
.collect(Collectors.toList())
.stream().max(Comparator.comparing(AttSourceDataBean::getAttCurrentTime)).orElse(null);
//自由打卡不需要去除数据
getFreeAttData(newList, frontToWorkBean, backOffWorkBean, attGroupBean);
});
return newList;
}
/**
* 上下班考勤计算
*
* @param list 考勤来源数据
* @param attGroupBean 考勤组数据
* @param tf 固定|自由
* @return 考勤数据
*/
private List<AttSourceDataBean> getDataList(List<AttSourceDataBean> list,
AttGroupBean attGroupBean,
Boolean tf) {
List<AttSourceDataBean> newList = new ArrayList<>();
//分组排序(将每个人每天在自己考勤组的数据分组)
Map<String, List<AttSourceDataBean>> groupedItems = list.stream()
.collect(Collectors.groupingBy(
c -> c.getUserId() + "|" + c.getAttCurrentDay(),
Collectors.collectingAndThen(
Collectors.toList(),
v -> {
v.sort(Comparator.comparing(AttSourceDataBean::getAttCurrentTime));
return v;
}
)
));
if (!tf) {
//固定打卡以最靠近上班打卡时间前的出闸机的时间为基准去除掉之前的所有打卡数据
AttTimeUtil.processGroupedItems(groupedItems, attGroupBean);
}
groupedItems.forEach((c, v) -> {
//第一次上班时间
AttSourceDataBean frontToWorkBean = v.stream()
.filter(a -> a.getAttType().equals("1"))
.collect(Collectors.toList())
.stream().min(Comparator.comparing(AttSourceDataBean::getAttCurrentTime)).orElse(null);
//最新一次下班时间
AttSourceDataBean backOffWorkBean = v.stream()
.filter(a -> a.getAttType().equals("2"))
.collect(Collectors.toList())
.stream().max(Comparator.comparing(AttSourceDataBean::getAttCurrentTime)).orElse(null);
if (tf) {
// if(!v.isEmpty() && v.get(0).getName().equals("任荣辉")){
// System.out.println("11111111111");
// }
//自由打卡不需要去除数据
getFreeAttData(newList, frontToWorkBean, backOffWorkBean, attGroupBean);
} else {
if (backOffWorkBean != null) {
//最新一次下班时间插入list
newList.add(backOffWorkBean);
}
//没有下班卡则添加第一次打卡数据
newList.add(frontToWorkBean);
//获取工作异常
LocalTime lastOutTime = null;
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
try {
//存储工作异常的list
List<AttSourceDataBean> longBreakRecords = new ArrayList<>();
for (int i = 0; i < v.size(); i++) {
AttSourceDataBean record = v.get(i);
// 如果是的记录保存时间
if ("2".equals(record.getAttType())) {
lastOutTime = LocalDateTime.parse(record.getAttCurrentTime(), dateTimeFormatter).toLocalTime();
if (lastOutTime.isAfter(LocalTime.parse(attGroupBean.getOffWorkTime(), timeFormatter))) {
//进在下班之后不考虑
break;
}
}
// 如果是的记录并且之前有的记录
else if ("1".equals(record.getAttType()) && lastOutTime != null) {
LocalTime inTime = LocalDateTime.parse(record.getAttCurrentTime(), dateTimeFormatter).toLocalTime();
// 计算实际工作时间不包括午休时间
// if(record.getName().equals("何波")){
// System.out.println("11111111111");
// }
// - 的时间在上班时间前去除
if (inTime.isBefore(LocalTime.parse(attGroupBean.getToWorkTime(), timeFormatter))) {
//进在上班之后不考虑
break;
}
Duration workDuration = AttTimeUtil.calculateWorkDuration(lastOutTime, inTime, attGroupBean);
// 如果工作时间外出超过了规定时间记录下这对
if (workDuration != null && !workDuration.isNegative() && workDuration.toMinutes() > attGroupBean.getWorkAbnormalMinute()) {
AttSourceDataBean longBreakRecord = new AttSourceDataBean();
longBreakRecord.setUserId(record.getUserId());
longBreakRecord.setName(record.getName());
longBreakRecord.setOrgId(attGroupBean.getOrgId());
longBreakRecord.setAttCurrentDay(record.getAttCurrentDay());
longBreakRecord.setAttCurrentTime(v.get(i - 1).getAttCurrentTime() + " " + record.getAttCurrentTime());
longBreakRecord.setAttAddress(v.get(i - 1).getAttAddress() + " " + record.getAttAddress());
longBreakRecords.add(longBreakRecord);
}
// 更新lastOutTime为null因为已经处理了这一对
lastOutTime = null;
}
}
//判断有没有临时外出请假等等
if (!longBreakRecords.isEmpty()) {
int x = attSourceDataDao.getLeaveDataByUserId(longBreakRecords.get(0));
if (x == 0) {
attSourceDataDao.insertWorkAbnormal(longBreakRecords);
}
}
} catch (Exception e) {
e.printStackTrace();
log.error("工作异常处理报错");
}
}
});
return newList;
}
/**
* 处理打卡状态
*
* @param newList 考勤数据
* @param toWorkTime 上班时间
* @param offWorkTime 下班时间
* @param lateMinute 迟到时间
* @param absenteeismLateMinute 旷工迟到分钟
* @param leaveMinute 早退分钟
* @param absenteeismLeaveMinute 旷工早退分钟
*/
private void processAttendanceStatus(List<AttSourceDataBean> newList, String toWorkTime,
String offWorkTime, long lateMinute,
long absenteeismLateMinute, long leaveMinute,
long absenteeismLeaveMinute) {
newList.forEach(c -> {
// if(c.getName().equals("肖阳")){
// System.out.println("c.getAttCurrentTime() = " + c.getAttCurrentTime());
// }
System.out.println(c.getName());
int attStatus = calculateStatus(
c.getAttType(),
c.getAttCurrentTime(),
"1".equals(c.getAttType()) ? toWorkTime : offWorkTime,
"1".equals(c.getAttType()) ? lateMinute : leaveMinute,
"1".equals(c.getAttType()) ? absenteeismLateMinute : absenteeismLeaveMinute
);
System.out.println(1111);
// 如打卡状态为正常检查是否有异常出入时间
if (attStatus == 1 && c.getAttStatus() != null && c.getAttStatus() == 8) {
attStatus = 8; // 异常
boolean b = DateTimeHelper.compareTime2(c.getAttCurrentTime(), c.getAbnormalAttTime());
String time = "";
String address = "";
if (b) {
time = c.getAttCurrentTime() + " " + c.getAbnormalAttTime();
address = c.getAttAddress() + " " + c.getAbnormalAttAddress();
} else {
time = c.getAbnormalAttTime() + " " + c.getAttCurrentTime();
address = c.getAbnormalAttAddress() + " " + c.getAttAddress();
}
c.setAttCurrentTime(time);
c.setAttAddress(address);
}
System.out.println(2222);
c.setAttStatus(attStatus);
});
}
/**
* 获取自由考勤数据
*
* @param newList 考勤数据
* @param frontToWorkBean 第一次上班考勤数据
* @param backOffWorkBean 最后一次下班考勤数据
* @param attGroupBean 考勤组数据
*/
private void getFreeAttData(List<AttSourceDataBean> newList,
AttSourceDataBean frontToWorkBean,
AttSourceDataBean backOffWorkBean,
AttGroupBean attGroupBean) {
// todayClockNum 自由打卡每天打卡次数
if (attGroupBean.getTodayClockNum() == 2) {
if (frontToWorkBean != null) {
frontToWorkBean.setAttStatus(1);
newList.add(frontToWorkBean);
}
if (backOffWorkBean != null) {
if (frontToWorkBean != null) {
long minutesDiff = DateUtil.between(
DateUtils.parseDate(frontToWorkBean.getAttCurrentTime()),
DateUtils.parseDate(backOffWorkBean.getAttCurrentTime()),
DateUnit.MINUTE
);
if (minutesDiff >= attGroupBean.getAttendanceDuration()) {
backOffWorkBean.setAttStatus(1);
} else {
if (minutesDiff > attGroupBean.getAbsenteeismLateMinute()) {
backOffWorkBean.setAttStatus(1);
} else if (minutesDiff > attGroupBean.getAbsenteeismLeaveMinute()) {
backOffWorkBean.setAttStatus(4);
} else {
backOffWorkBean.setAttStatus(3);
}
}
newList.add(backOffWorkBean);
} else {
backOffWorkBean.setAttStatus(1);
newList.add(backOffWorkBean);
}
}
} else {
if (frontToWorkBean != null) {
// if(frontToWorkBean.getName().equals("位兆虎")){
// System.out.println(1);
// }
frontToWorkBean.setAttStatus(1);
AttSourceDataBean bean;
try {
bean = (AttSourceDataBean) BeanUtils.cloneBean(frontToWorkBean);
} catch (Exception e) {
throw new RuntimeException(e);
}
bean.setAttCurrentTime("");
bean.setAttType("2");
newList.add(bean);
newList.add(frontToWorkBean);
} else if (backOffWorkBean != null) {
backOffWorkBean.setAttStatus(1);
AttSourceDataBean bean;
try {
bean = (AttSourceDataBean) BeanUtils.cloneBean(backOffWorkBean);
} catch (Exception e) {
throw new RuntimeException(e);
}
bean.setAttCurrentTime("");
bean.setAttType("1");
newList.add(bean);
newList.add(backOffWorkBean);
}
}
}
/**
* 计算时间差并返回状态
*
* @param attType 考勤类型
* @param attTime 考勤时间
* @param standardTime 下班时间
* @param lateThreshold 迟到早退时间
* @param absenteeismThreshold 旷工时间
* @return 考勤状态
*/
private int calculateStatus(String attType, String attTime, String standardTime,
long lateThreshold, long absenteeismThreshold) {
int status = 1;
// 创建两个时间点
Date date1 = DateUtils.parseDate(DateUtils.getYyyyMmDd(attTime) + " " + standardTime);
Date date2 = DateUtils.parseDate(attTime);
if ("2".equals(attType)) {
String startTime = DateUtils.getYyyyMmDd(attTime) + " 00:00:00";
String endTime = DateUtils.getYyyyMmDd(attTime) + " 04:59:59";
if (DateUtils.getTimeIsRange(attTime, startTime, endTime)) {
date1 = DateUtils.parseDate(DateUtils.getAfterDate(DateUtils.getYyyyMmDd(attTime)) + " " + standardTime);
}
}
if ("1".equals(attType)) { // 上班打卡
// 计算时间差
// 1 原因30上班30:59不算迟到类似于默认可以迟到一分钟
double difference = (date2.getTime() - date1.getTime()) / 60000.0 - 1;
if (difference > 0) {
if (difference > absenteeismThreshold) {
status = 3; // 旷工
} else if (difference > lateThreshold) {
status = 2; // 迟到
}
}
} else if ("2".equals(attType)) { // 下班打卡
// 计算时间差
double difference = (date2.getTime() - date1.getTime()) / 60000.0;
if (difference < 0) {
if (Math.abs(difference) > lateThreshold) {
status = 4; // 早退
}
if (Math.abs(difference) > absenteeismThreshold) {
status = 3; // 旷工
}
}
}
return status; // 正常
}
private void addWorkTimeOutRecord(List<AttSourceDataBean> v, AttGroupBean attGroupBean) {
LocalTime lastOutTime = null;
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
try {
//存储工作异常的list
List<AttSourceDataBean> longBreakRecords = new ArrayList<>();
for (int i = 0; i < v.size(); i++) {
AttSourceDataBean record = v.get(i);
// 如果是的记录保存时间
if ("2".equals(record.getAttType())) {
lastOutTime = LocalDateTime.parse(record.getAttCurrentTime(), dateTimeFormatter).toLocalTime();
if (lastOutTime.isAfter(LocalTime.parse(attGroupBean.getOffWorkTime(), timeFormatter))) {
//进在下班之后不考虑
break;
}
}
// 如果是的记录并且之前有的记录
else if ("1".equals(record.getAttType()) && lastOutTime != null) {
LocalTime inTime = LocalDateTime.parse(record.getAttCurrentTime(), dateTimeFormatter).toLocalTime();
// 计算实际工作时间不包括午休时间
// if(record.getName().equals("何波")){
// System.out.println("11111111111");
// }
// - 的时间在上班时间前去除
if (inTime.isBefore(LocalTime.parse(attGroupBean.getToWorkTime(), timeFormatter))) {
//进在上班之后不考虑
break;
}
Duration workDuration = AttTimeUtil.calculateWorkDuration(lastOutTime, inTime, attGroupBean);
// 如果工作时间外出超过了规定时间记录下这对
if (workDuration != null && !workDuration.isNegative() && workDuration.toMinutes() > attGroupBean.getWorkAbnormalMinute()) {
AttSourceDataBean longBreakRecord = new AttSourceDataBean();
longBreakRecord.setUserId(record.getUserId());
longBreakRecord.setName(record.getName());
longBreakRecord.setOrgId(attGroupBean.getOrgId());
longBreakRecord.setAttCurrentDay(record.getAttCurrentDay());
longBreakRecord.setAttCurrentTime(v.get(i - 1).getAttCurrentTime() + " " + record.getAttCurrentTime());
longBreakRecord.setAttAddress(v.get(i - 1).getAttAddress() + " " + record.getAttAddress());
longBreakRecords.add(longBreakRecord);
}
// 更新lastOutTime为null因为已经处理了这一对
lastOutTime = null;
}
}
//判断有没有临时外出请假等等
if (!longBreakRecords.isEmpty()) {
int x = attSourceDataDao.getLeaveDataByUserId(longBreakRecords.get(0));
if (x == 0) {
attSourceDataDao.insertWorkAbnormal(longBreakRecords);
}
}
} catch (Exception e) {
e.printStackTrace();
log.error("工作异常处理报错");
}
}
}

View File

@ -0,0 +1,125 @@
package com.bonus.system.att.tasks;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.bonus.common.core.utils.DateTimeHelper;
import com.bonus.common.core.utils.DateUtils;
import com.bonus.system.att.dao.AttGroupDao;
import com.bonus.system.att.dao.AttSourceDataDao;
import com.bonus.system.att.entity.*;
import com.bonus.system.att.service.AttCalService;
import com.bonus.system.att.utils.AddressCoordinateFormatUtil;
import com.bonus.system.att.utils.AttTimeUtil;
import com.bonus.system.att.utils.WorkdayCalculator;
import com.bonus.system.holiday.dao.HolidayDao;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import javax.annotation.Resource;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
/**
* @author zys
* 考勤定时器
*/
@Configuration
@EnableScheduling
@Slf4j
@EnableAsync
public class NewAttTask {
@Resource(name = "attSourceDataDao")
private AttSourceDataDao attSourceDataDao;
@Resource(name = "sqlSessionTemplate")
private SqlSessionTemplate sqlSessionTemplate;
@Resource(name = "AttCalService")
private AttCalService attCalService;
/**
* 待考勤人员列表
* 在人员考勤模版数据之前生成
* @param pushDate 时间(可为空为空则推历史60天内没生成的数据不为空则推指定日期)
*/
private void insertAttDateHistory(String pushDate) {
//待考勤人员列表生成
attCalService.insertAttDateHistory(pushDate);
}
/**
* 人员考勤模版数据默认是未打卡
* 每天早上0点30执行一次
* @param pushDate 时间
* @param pushType 1:自动推送当天 2:手动历史
*/
private void insertAttTempData(String pushDate, int pushType) {
//获取考勤数据
attCalService.insertAttTempData(pushDate, pushType);
}
/**
* 获取其他系统考勤数据
* 建议一小时一次
* @param pushDate 时间
* @param pushType 1:自动推送当天 2:手动历史
*/
private void getAttendanceData(String pushDate, int pushType) {
//获取考勤数据
attCalService.getAttendanceData(pushDate, pushType);
}
/**
* 考勤数据应用步骤一考勤数据更新
* 建议一小时一次
* @param pushDate 时间
* @param pushType 1:自动推送当天 2:手动历史
*/
private void updateAttData(String pushDate, int pushType) {
//获取考勤数据
attCalService.updateAttData(pushDate, pushType);
}
/**
* 考勤数据应用步骤二旷工状态更新
* 每天12点将当天未打卡考勤人员上班置为旷工
* 每天晚上22点将下班未打卡置为旷工
* @param pushDate 时间
* @param pushType 1:自动推送当天 2:手动历史
*/
private void updateAbsenteeismData(String pushDate, int pushType) {
//旷工状态更新
attCalService.updateAbsenteeismData(pushDate, pushType);
}
/**
* 请假数据应用步骤一法假数据更新
* 法定节假日不会随时间变化一天执行一次就可以了
* @param pushDate 时间
*/
private void updateLegalHolidayData(String pushDate) {
//旷工状态更新
attCalService.updateLegalHolidayData(pushDate);
}
}