1.打卡闭环,不能一天可以打多次卡,一天只有一次打上下班及加班卡

2.考勤出现漏打卡,次日可以正常打卡(8小时制员工)】
This commit is contained in:
andy 2025-07-01 10:54:29 +08:00
parent 9f3c939abd
commit e75014aaa7
2 changed files with 60 additions and 10 deletions

View File

@ -7,24 +7,23 @@ import com.evo.attendance.domain.RzAttendanceDetail;
import com.evo.attendance.mapper.RzAttendanceDetailMapper;
import com.evo.attendance.mapper.RzAttendanceMapper;
import com.evo.attendance.processor.PunchTheClockStrategyExchangeProcessor;
import com.evo.common.annotation.Log;
import com.evo.common.constant.Constants;
import com.evo.common.core.domain.entity.SysDept;
import com.evo.common.utils.*;
import com.evo.equipment.domain.EqButton;
import com.evo.equipment.mapper.EqButtonMapper;
import com.evo.personnelMatters.domain.EqOverStaff;
import com.evo.personnelMatters.domain.RzOverTime;
import com.evo.personnelMatters.domain.RzOverTimeDetail;
import com.evo.personnelMatters.mapper.EqOverStaffMapper;
import com.evo.personnelMatters.mapper.RzOverTimeDetailMapper;
import com.evo.personnelMatters.mapper.RzOverTimeMapper;
import com.evo.system.domain.SysStaff;
import com.evo.system.domain.SysStaffDetail;
import com.evo.system.mapper.SysDeptMapper;
import com.evo.system.mapper.SysStaffDetailMapper;
import com.evo.system.mapper.SysStaffMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@ -61,7 +60,8 @@ public class KQDeviceExchangeProcessor implements PunchTheClockStrategyExchangeP
private EqOverStaffMapper eqOverStaffMapper;
@Resource
private SysDeptMapper sysDeptMapper;
@Resource
private SysStaffDetailMapper sysStaffDetailMapper;
@Override
public boolean accept(String sn) {
@ -87,12 +87,15 @@ public class KQDeviceExchangeProcessor implements PunchTheClockStrategyExchangeP
if(!sn.equals(com.evo.equipment.constant.Constants.EQ_DEVICE_PUBLIC_CODE) && !sn.equals(sysStaff.getTimeClock())){
return initMessage(1,"未设置当前考勤机打卡权限", "000000000");
}
//检查当前打卡人是不是月薪, 如果是月薪 并且当天没有打卡, 则显示上班卡, 否则显示下班卡
SysStaffDetail sysStaffDetail = sysStaffDetailMapper.selectSysStaffDetailByStaffId(sysStaff.getUserId());
//查询员工的最后一次打卡 按钮切换
RzAttendanceDetail rzAttendanceDetail = rzAttendanceDetailMapper.selectLastRzAttendanceDetail(sysStaff.getUserId());
//判断最后一次打卡为下班卡或没有打卡记录则打卡为上班卡
if(StringUtils.isNull(rzAttendanceDetail) || "下班卡".equals(rzAttendanceDetail.getButtonType()) || "撤销".equals(rzAttendanceDetail.getButtonType())){
//如果最后一条数据的卡类型为下班卡则返回上班卡和加班卡权限 or 检查当前人员是否存在特殊加班中 或者 检查当前人员部门是否开启加班 or 当天打过加班卡
if(ObjectUtils.isNotEmpty(eqOverStaffMapper.selectEqOverStaffByUserId(sysStaff.getUserId())) || ObjectUtils.isEmpty(sysDeptMapper.selectOne(new LambdaQueryWrapper<SysDept>().eq(SysDept::getDeptId, sysStaff.getDeptId()).eq(SysDept::getIsOverTime,"1"))) || rzAttendanceDetailMapper.checkOverTimeCard(sysStaff.getUserId(), new Date()) > 0){
if(showButton(sysStaffDetail) || StringUtils.isNull(rzAttendanceDetail) || "下班卡".equals(rzAttendanceDetail.getButtonType()) || "撤销".equals(rzAttendanceDetail.getButtonType())){
//如果最后一条数据的卡类型为下班卡则返回上班卡和加班卡权限 or 检查当前人员是否存在特殊加班中 或者 检查当前人员部门是否开启加班 or 暂时取消这个判定 当天打过加班卡 ( || rzAttendanceDetailMapper.checkOverTimeCard(sysStaff.getUserId(), new Date()) > 0)
if(ObjectUtils.isNotEmpty(eqOverStaffMapper.selectEqOverStaffByUserId(sysStaff.getUserId())) || ObjectUtils.isEmpty(sysDeptMapper.selectOne(new LambdaQueryWrapper<SysDept>().eq(SysDept::getDeptId, sysStaff.getDeptId()).eq(SysDept::getIsOverTime,"1")))){
//如果存在特殊加班, 或者当前部门没有开启加班 或者当天打过加班, 则无法打加班卡
return initMessage(0,"验证通过", "111000000");
}
@ -102,10 +105,12 @@ public class KQDeviceExchangeProcessor implements PunchTheClockStrategyExchangeP
}
}
public Boolean showButton(SysStaffDetail detail){
return (detail.getBasicSalary() != null && detail.getBasicSalary().compareTo(new BigDecimal(0)) > 0) && (detail.getJobsSalary() != null && detail.getJobsSalary().compareTo(new BigDecimal(0)) > 0) && (!checkToDayCard(String.valueOf(detail.getStaffId()), "上班") && !checkToDayCard(String.valueOf(detail.getStaffId()),"加班"));
}
@Override
public String exchangeFace(String userId, String sn, Date date, String button, String rules) {
//根据ID查询员工信息 ,判断员工是否存在不存在返回失败
SysStaff sysStaff = sysStaffMapper.selectSysStaffByUserId(Long.valueOf(userId));//打卡记录信息
log.info("日常-打卡信息如下: 员工姓名:{}, 打卡时间:{}. 打卡设备:{}, 打卡情况: {}",sysStaff.getName(), sdfd.format(date), sn, rules);
@ -114,6 +119,21 @@ public class KQDeviceExchangeProcessor implements PunchTheClockStrategyExchangeP
if(StringUtils.isNotEmpty(error)){
return initMessage(1, error);
}
/***
* 应人力要求,
* 1.打卡闭环不能一天可以打多次卡一天只有一次打上下班及加班卡
* 2.考勤出现漏打卡次日可以正常打卡8小时制员工 其中8小时制员工 为单班制打卡
*/
//上班卡
if(rules.contains("上班") && checkToDayCard(userId, "上班")){
return initMessage(1, "当天已经打过上班卡");
}
//上班卡
if(rules.contains("加班") && checkToDayCard(userId, "加班")){
return initMessage(1, "当天已经打过加班卡");
}
//员工考勤记录
RzAttendance attendance = rzAttendanceMapper.queryNowDayAttendanceByStatisticalIdAndDate(Long.valueOf(userId),date);
//如果当前打卡是下班卡, 并且当前员工考勤上班时间为空, 则判定为隔天打下班卡
@ -166,6 +186,15 @@ public class KQDeviceExchangeProcessor implements PunchTheClockStrategyExchangeP
return initMessage(1, "打卡失败");
}
public Boolean checkToDayCard(String userId, String rules){
return ObjectUtils.isNotEmpty(rzAttendanceDetailMapper.selectOne(new LambdaQueryWrapper<RzAttendanceDetail>()
.like(RzAttendanceDetail::getButtonType,"%"+rules+"%")
.eq(RzAttendanceDetail::getStaffId, Long.valueOf(userId))
.eq(RzAttendanceDetail::getDelFlag, Constants.DELETE_FLAG_0)
.apply(" date_format(date_time,'%Y%m%d') = date_format({0},'%Y%m%d') ", new Date())));
}
/*** 上班卡
* @param userId
* @param date
@ -366,8 +395,24 @@ public class KQDeviceExchangeProcessor implements PunchTheClockStrategyExchangeP
public String overTimeOffDutyCard(SysStaff sysStaff , Date date, String sn){
//修改加班打卡记录 根据员工ID和时间查找 统计数据
RzOverTime rzOverTime = rzOverTimeMapper.selectRzOverTimeByNameAndMonth(sysStaff.getUserId(),date);
//如果没有加班汇总, 生成一条新的
if(ObjectUtils.isEmpty(rzOverTime)){
rzOverTime = new RzOverTime();
rzOverTime.setUserId(sysStaff.getUserId());
rzOverTime.setDeptId(sysStaff.getDeptId());
rzOverTime.setName(sysStaff.getName());
rzOverTime.setOverTimeMonth(date);
rzOverTime.setOverHours(DataUtils.DEFAULT_VALUE);
rzOverTime.setDelFlag(Constants.DELETE_FLAG_0);
rzOverTime.setCreateBy("admin");
rzOverTime.setCreateTime(new Date());
rzOverTimeMapper.insert(rzOverTime);
}
//查找加班详情 加班统计ID和加班开始时间
RzOverTimeDetail rzOverTimeDetail = rzOverTimeDetailMapper.queryRzOverTimeDetailByDateAndOverId(rzOverTime.getId(),date);
if(ObjectUtils.isEmpty(rzOverTimeDetail)){
return initMessage(1, "未找到当天的加班信息, 请补卡");
}
rzOverTimeDetail.setOverTimeEnd(date);
//计算加班时长 分钟
BigDecimal minutes = new BigDecimal((rzOverTimeDetail.getOverTimeEnd().getTime() - rzOverTimeDetail.getOverTimeStart().getTime())/1000/60);

View File

@ -41,6 +41,8 @@ public class SubsidyCalculationUtils {
public static final String xnh = "13";
public static final String yc = "11";
public static final List<String> tsbz = Collections.asList(ht,gl,xnh,yc);
public static SysStaffDetail subsidyCalculation(SysStaff staff, SysStaffDetail staffDetail, List<RzSubsidyInfo> subsidyInfoList){
//正式员工并且有补助信息
if(Constants.JOB_STATIS_1.equals(staff.getStatus())){
@ -51,24 +53,27 @@ public class SubsidyCalculationUtils {
Map<Long, RzSubsidyInfo> map = subsidyInfoList.stream().collect(Collectors.toMap(RzSubsidyInfo::getId, d->d, (k1, K2)->k1));
Boolean isAdd = true;
for(String subsidyId : staff.getSubsidys().split(",")){
isAdd = true;
isAdd = false;
RzSubsidyInfo rzSubsidyInfo = map.get(Long.valueOf(subsidyId));
String key = rzSubsidyInfo.getName();
BigDecimal value = rzSubsidyInfo.getValue();
if(ht.equals(subsidyId) && staff.getContractStart() != null && staff.getContractEnd() != null){
Integer year= DateUtils.getBetweenYear(staff.getContractStart(), staff.getContractEnd(), 1);
value = value.multiply(new BigDecimal(year));
isAdd = true;
}else if(gl.equals(subsidyId)){
Integer year= DateUtils.getBetweenYearByDays(staff.getEmploymentDate(), new Date());
//最多只允许10年的工龄补贴
if(year > 10) year=10;
value = value.multiply(new BigDecimal(year));
isAdd = true;
}else if(xnh.equals(subsidyId)){
isAdd = "新农合".equals(staff.getSocialType()) && ("".equals(staff.getSocialSubsidy()) || "享有".equals(staff.getSocialSubsidy()));
}else if(yc.equals(subsidyId)){
isAdd = "".equals(staff.getZsFlag());
}
if(isAdd) staffDetail.getExtendeds().put(key, value);
//当前补助不是特殊补助, 或者 特殊补助允许添加
if(!tsbz.contains(subsidyId) || isAdd) staffDetail.getExtendeds().put(key, value);
}
}
//计算学历补助