import com.engine.attendance.attendanceanalysis.cmd.GetClockInPointCmd; import com.engine.attendance.attendanceanalysis.service.UtilService; import com.engine.attendance.attendanceanalysis.service.impl.UtilServiceImpl; import com.engine.attendance.attendanceanalysis.wrapper.AttendanceAnalysisWrapper; import com.engine.attendance.enums.AccountingUnitEnum; import com.engine.attendance.enums.CheckBoxEnum; import com.engine.attendance.enums.ClassSegmentTypeEnum; import com.engine.attendance.enums.ClockPointEnum; import com.engine.common.util.DateUtil; import com.engine.common.util.ServiceUtil; import com.engine.common.util.Utils; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import weaver.general.Util; import java.time.ZoneOffset; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class TestGetClockInPoint { public static void main(String[] args) { List> schedulingList = Lists.newArrayList(); schedulingList.add(new HashMap(){{ put("edsc","9.50"); put("bdlx","0"); put("jsdk","0"); put("tqdkfzs","120"); put("sfkt","1"); put("dtjssj","12:00"); put("zddxfz","30"); put("bcxx","25"); put("dxhs","5"); put("bcsdxx","09:00-13:00|13:00-18:30"); put("sfdx","0"); put("dtkssj","09:00"); put("thdkfzs","120"); put("ksdk","1"); put("rqlx","11"); }}); schedulingList.add(new HashMap(){{ put("edsc","9.50"); put("bdlx","1"); put("jsdk","0"); put("tqdkfzs","120"); put("sfkt","1"); put("dtjssj","13:00"); put("zddxfz","30"); put("bcxx","25"); put("dxhs","5"); put("bcsdxx","09:00-13:00|13:00-18:30"); put("sfdx","1"); put("dtkssj","12:00"); put("thdkfzs","120"); put("ksdk","0"); put("rqlx","11"); }}); schedulingList.add(new HashMap(){{ put("edsc","9.50"); put("bdlx","0"); put("jsdk","1"); put("tqdkfzs","120"); put("sfkt","1"); put("dtjssj","18:00"); put("zddxfz","30"); put("bcxx","25"); put("dxhs","5"); put("bcsdxx","09:00-13:00|13:00-18:30"); put("sfdx","1"); put("dtkssj","13:00"); put("thdkfzs","120"); put("ksdk","0"); put("rqlx","11"); }}); List> dataList = Lists.newArrayList(); dataList.add(new HashMap(){{ put("signtime","08:38:00"); put("id","48"); put("userid","53"); put("signdate","2023-11-12"); }}); dataList.add(new HashMap(){{ put("signtime","10:29:00"); put("id","48"); put("userid","53"); put("signdate","2023-11-12"); }}); Map param = Maps.newHashMap(); List> askForLeaveAndEvctionSchedule = Lists.newArrayList(); askForLeaveAndEvctionSchedule.add(new HashMap(){{ put("edsc","9.50"); put("bdlx",ClassSegmentTypeEnum.ASK_FOR_LEAVE.getKey()); put("jsdk","0"); put("tqdkfzs","60"); put("sfkt","1"); put("dtjssj","18:00"); put("zddxfz","30"); put("bcxx","25"); put("dxhs","5"); put("bcsdxx","09:00-13:00|13:00-18:30"); put("sfdx","1"); put("dtkssj","11:00"); put("thdkfzs","60"); put("ksdk","0"); put("rqlx","11"); }}); param.put("analysisDate","2023-11-12"); param.put("scheduleResult",schedulingList); param.put("clockInTimeList",dataList); param.put("askForLeaveAndEvctionSchedule",askForLeaveAndEvctionSchedule); // List>> collect = getClockInPoint("2023-11-12",schedulingList,dataList); List>> collect = (List>>)new GetClockInPointCmd(param).execute(null).get("clcokInTimeData"); // Map clock = getNeedRecordClockInTime(collect); // System.out.println(clock); } public static List>> getClockInPoint(String analysisDate, List> needClockInSchedule, List> clockInTimeList){ List>> clcokInTimeData = Lists.newArrayList(); for (Map needClockIn :needClockInSchedule){ if (CheckBoxEnum.CHECKED.getKey().equals(needClockIn.get("ksdk"))){ String dtkssj = analysisDate+" "+needClockIn.get("dtkssj"); String dtjssj = analysisDate+" "+needClockIn.get("dtjssj"); if (DateUtil.getTime(dtkssj).compareTo(DateUtil.getTime(dtjssj)) > 0){ dtjssj = DateUtil.AfterDay(analysisDate,1) +" "+needClockIn.get("dtjssj"); } int tqdkfzs = Integer.valueOf(Util.null2String(needClockIn.get("tqdkfzs"))); Map> ksdkNearestClcokInTime = Utils.getNearestClcokInTimeCmd(dtkssj,clockInTimeList); String timeType = ClockPointEnum.EMPTY.getKey(); Map> clcokInTimeMap = Maps.newHashMap(); if (ksdkNearestClcokInTime.get(ClockPointEnum.EQUAL.getKey()) != null){ timeType=ClockPointEnum.EQUAL.getKey(); } if (ClockPointEnum.EMPTY.getKey().equals(timeType) && ksdkNearestClcokInTime.get(ClockPointEnum.BEFORE.getKey()) != null){ String clockInTime = ksdkNearestClcokInTime.get(ClockPointEnum.BEFORE.getKey()).get("signdate") +" "+ksdkNearestClcokInTime.get(ClockPointEnum.BEFORE.getKey()).get("signtime"); if (DateUtil.getTime(clockInTime).compareTo(DateUtil.getTime(DateUtil.beforeMinutes(dtkssj,tqdkfzs))) >=0){ //打卡时间大于等于最早打卡时间 timeType=ClockPointEnum.BEFORE.getKey(); } } if (ClockPointEnum.EMPTY.getKey().equals(timeType) && ksdkNearestClcokInTime.get(ClockPointEnum.AFTER.getKey()) != null){ String clockInTime = ksdkNearestClcokInTime.get(ClockPointEnum.AFTER.getKey()).get("signdate") +" "+ksdkNearestClcokInTime.get(ClockPointEnum.AFTER.getKey()).get("signtime"); if (DateUtil.getTime(clockInTime).compareTo(DateUtil.getTime(dtjssj)) < 0){ //打卡时间小于结束时间 timeType=ClockPointEnum.AFTER.getKey(); } } clcokInTimeMap.put(dtkssj+"|"+ClockPointEnum.START.getKey()+"|"+timeType,ksdkNearestClcokInTime.get(timeType)); clcokInTimeData.add(clcokInTimeMap); } if (CheckBoxEnum.CHECKED.getKey().equals(needClockIn.get("jsdk"))){ String dtkssj = analysisDate+" "+needClockIn.get("dtkssj"); String dtjssj = analysisDate+" "+needClockIn.get("dtjssj"); int thdkfzs = Integer.valueOf(Util.null2String(needClockIn.get("thdkfzs"))); if (DateUtil.getTime(dtkssj).compareTo(DateUtil.getTime(dtjssj)) > 0){ dtjssj = DateUtil.AfterDay(analysisDate,1) +" "+needClockIn.get("dtjssj"); } Map> jsdkNearestClcokInTime = Utils.getNearestClcokInTimeCmd(dtjssj,clockInTimeList); String timeType = ClockPointEnum.EMPTY.getKey(); Map> clcokInTimeMap = Maps.newHashMap(); if (jsdkNearestClcokInTime.get(ClockPointEnum.EQUAL.getKey()) != null){ timeType=ClockPointEnum.EQUAL.getKey(); } if (ClockPointEnum.EMPTY.getKey().equals(timeType) && jsdkNearestClcokInTime.get(ClockPointEnum.AFTER.getKey()) != null){ String clockInTime = jsdkNearestClcokInTime.get(ClockPointEnum.AFTER.getKey()).get("signdate") +" "+jsdkNearestClcokInTime.get(ClockPointEnum.AFTER.getKey()).get("signtime"); if (DateUtil.getTime(clockInTime).compareTo(DateUtil.getTime(DateUtil.AfterMinutes(dtjssj,thdkfzs))) <=0){ timeType=ClockPointEnum.AFTER.getKey(); } } if (ClockPointEnum.EMPTY.getKey().equals(timeType) && jsdkNearestClcokInTime.get(ClockPointEnum.BEFORE.getKey()) != null){ String clockInTime = jsdkNearestClcokInTime.get(ClockPointEnum.BEFORE.getKey()).get("signdate") +" "+jsdkNearestClcokInTime.get(ClockPointEnum.BEFORE.getKey()).get("signtime"); if (DateUtil.getTime(clockInTime).compareTo(DateUtil.getTime(dtkssj)) >0){ timeType=ClockPointEnum.BEFORE.getKey(); } } clcokInTimeMap.put(dtjssj+"|"+ClockPointEnum.END.getKey()+"|"+timeType,jsdkNearestClcokInTime.get(timeType)); clcokInTimeData.add(clcokInTimeMap); } } clcokInTimeData = clcokInTimeData.stream().sorted(Comparator.comparing(e->{ //卡点 String point = ""; //当天打卡数据 for (Map.Entry> entry :e.entrySet()){ point = entry.getKey(); } return DateUtil.getTime(point.split("\\|")[0]).toInstant(ZoneOffset.of("+8")).toEpochMilli(); })).collect(Collectors.toList()); //当弹性上下班时 if (needClockInSchedule.size() > 0){ //是否弹性 String sfdx = Util.null2String(needClockInSchedule.get(0).get("sfdx")); //最大弹性分钟 int zddxfz = Integer.valueOf(Util.null2String(needClockInSchedule.get(0).get("zddxfz"))); //弹性核算 int dxhs = Integer.valueOf(Util.null2String(needClockInSchedule.get(0).get("dxhs"))); if (CheckBoxEnum.CHECKED.getKey().equals(sfdx)){ //上下班弹性 List> needClockInStartList = needClockInSchedule.stream().filter(e -> ClassSegmentTypeEnum.WORK_TIME.getKey().equals(e.get("bdlx")) && CheckBoxEnum.CHECKED.getKey().equals(e.get("ksdk"))).collect(Collectors.toList()); List> needClockInEndList = needClockInSchedule.stream().filter(e -> ClassSegmentTypeEnum.WORK_TIME.getKey().equals(e.get("bdlx")) && CheckBoxEnum.CHECKED.getKey().equals(e.get("jsdk"))).collect(Collectors.toList()); if (needClockInStartList.size()>0 && needClockInEndList.size()>0){ Map ksdkMap = needClockInStartList.get(0); String kssjStart = analysisDate + " " +Util.null2String(ksdkMap.get("dtkssj")); String jssjStart = analysisDate +" "+Util.null2String(ksdkMap.get("dtjssj")); Map jsdkMap = needClockInEndList.get(needClockInEndList.size()-1); int thdkfzs = Integer.valueOf(Util.null2String(jsdkMap.get("thdkfzs"))); String kssjEnd = analysisDate + " " +Util.null2String(jsdkMap.get("dtkssj")); String jssjEnd = analysisDate +" "+Util.null2String(jsdkMap.get("dtjssj")); if (ksdkMap != jsdkMap){ if (DateUtil.getTime(kssjStart).compareTo(DateUtil.getTime(jssjStart)) > 0){ kssjStart = DateUtil.beforeDay(analysisDate,1)+" "+Util.null2String(ksdkMap.get("dtkssj")); } if (DateUtil.getTime(kssjEnd).compareTo(DateUtil.getTime(jssjEnd)) > 0){ jssjEnd = DateUtil.AfterDay(analysisDate,1)+" "+Util.null2String(jsdkMap.get("dtjssj")); } }else { jssjEnd = DateUtil.AfterDay(analysisDate,1)+" "+Util.null2String(jsdkMap.get("dtjssj")); } //弹性上班卡 String flexibleWork = ""; for (int i=0;i> clcokInTimeMap = clcokInTimeData.get(i); //卡点 String point = ""; //当天打卡数据 Map clcokInTime = null; for (Map.Entry> entry :clcokInTimeMap.entrySet()){ point = entry.getKey(); clcokInTime = entry.getValue(); } //需要计算的班次打卡时间点 String pointTime = point.split("\\|")[0]; //start:开始打卡时间点,end:结束打卡时间点 String pointType = point.split("\\|")[1]; //empty:漏卡,equal:打卡时间和班次时间相等,before:打卡时间在班次时间之前,after:打卡时间在班次时间之后 String timeType = point.split("\\|")[2]; if (pointTime.equals(kssjStart) && ClockPointEnum.START.getKey().equals(pointType) && clcokInTime != null){ String signTime = clcokInTime.get("signdate")+" "+clcokInTime.get("signtime"); //该卡点是弹性开始时间 if (ClockPointEnum.BEFORE.getKey().equals(timeType)){ int betWeenTime = DateUtil.getBetWeenMinutes(signTime,kssjStart); String newPonit = point; if (betWeenTime <= zddxfz){ betWeenTime = Double.valueOf(Utils.getItemdurationDown(5.0, AccountingUnitEnum.MINUTES.getKey(),betWeenTime,AccountingUnitEnum.MINUTES)).intValue(); flexibleWork = DateUtil.beforeMinutes(kssjStart,betWeenTime); newPonit = pointTime+"|"+pointType+"|"+ClockPointEnum.EQUAL.getKey()+"|"+flexibleWork; }else if (betWeenTime > zddxfz){ newPonit = point +"|"+DateUtil.beforeMinutes(kssjStart,zddxfz); flexibleWork = DateUtil.beforeMinutes(kssjStart,zddxfz); } clcokInTimeMap.remove(point); clcokInTimeMap.put(newPonit,clcokInTime); }else if (ClockPointEnum.AFTER.getKey().equals(timeType)){ //迟到 String newPonit = point; int betWeenTime = DateUtil.getBetWeenMinutes(kssjStart,signTime); if (betWeenTime <= zddxfz){ betWeenTime = Double.valueOf(Utils.getItemduration(5.0, AccountingUnitEnum.MINUTES.getKey(),betWeenTime,AccountingUnitEnum.MINUTES)).intValue(); flexibleWork = DateUtil.AfterMinutes(kssjStart,betWeenTime); newPonit = pointTime+"|"+pointType+"|"+ClockPointEnum.EQUAL.getKey()+"|"+flexibleWork; }else if (betWeenTime > zddxfz){ newPonit = point +"|"+DateUtil.AfterMinutes(kssjStart,zddxfz); flexibleWork = DateUtil.AfterMinutes(kssjStart,zddxfz); } clcokInTimeMap.remove(point); clcokInTimeMap.put(newPonit,clcokInTime); } }else if (pointTime.equals(jssjEnd) && ClockPointEnum.END.getKey().equals(pointType) && clcokInTime != null){ //该卡点是弹性下班点 int betweenToWorkTime = DateUtil.getBetWeenMinutes(flexibleWork,kssjStart); //弹性下班时间点 String flexibleOffWork = jssjEnd; if (betweenToWorkTime >=0){ flexibleOffWork = DateUtil.beforeMinutes(flexibleOffWork,Math.abs(betweenToWorkTime)); }else if (betweenToWorkTime < 0){ flexibleOffWork = DateUtil.AfterMinutes(flexibleOffWork,Math.abs(betweenToWorkTime)); } //根据弹性下班时间点重新计算 Map> jsdkNearestClcokInTime = Utils.getNearestClcokInTimeCmd(flexibleOffWork,clockInTimeList); String newtimeType = ClockPointEnum.EMPTY.getKey(); if (jsdkNearestClcokInTime.get(ClockPointEnum.EQUAL.getKey()) != null){ newtimeType=ClockPointEnum.EQUAL.getKey(); } if (ClockPointEnum.EMPTY.getKey().equals(newtimeType) && jsdkNearestClcokInTime.get(ClockPointEnum.AFTER.getKey()) != null){ String clockInTime = jsdkNearestClcokInTime.get(ClockPointEnum.AFTER.getKey()).get("signdate") +" "+jsdkNearestClcokInTime.get(ClockPointEnum.AFTER.getKey()).get("signtime"); if (DateUtil.getTime(clockInTime).compareTo(DateUtil.getTime(DateUtil.AfterMinutes(flexibleOffWork,thdkfzs))) <=0){ newtimeType=ClockPointEnum.AFTER.getKey(); } } if (ClockPointEnum.EMPTY.getKey().equals(newtimeType) && jsdkNearestClcokInTime.get(ClockPointEnum.BEFORE.getKey()) != null){ String clockInTime = jsdkNearestClcokInTime.get(ClockPointEnum.BEFORE.getKey()).get("signdate") +" "+jsdkNearestClcokInTime.get(ClockPointEnum.BEFORE.getKey()).get("signtime"); if (DateUtil.getTime(clockInTime).compareTo(DateUtil.getTime(kssjEnd)) >0){ newtimeType=ClockPointEnum.BEFORE.getKey(); } } String newPonit = jssjEnd+"|"+ClockPointEnum.END.getKey()+"|"+newtimeType+"|"+flexibleOffWork; clcokInTimeMap.remove(point); clcokInTimeMap.put(newPonit,jsdkNearestClcokInTime.get(newtimeType)); } } } } } //当有2笔需要打卡时,可能会有打卡歧义的情况,歧义情况取2个时间点的中间值,当打卡时间小于中间值归属前一个打卡,大于则相反 if (clcokInTimeData.size() >1){ for (int i=0;i> beforeClcokInTimeData = clcokInTimeData.get(i); Map> afterClcokInTimeData = clcokInTimeData.get(i+1); Map beforeClcokInTimeMap = null; String beforeClcokInTime = ""; String afterClcokInTime = ""; Map afterClcokInTimeMap = null; for (Map.Entry> beforeEntry :beforeClcokInTimeData.entrySet()){ beforeClcokInTimeMap = beforeEntry.getValue(); beforeClcokInTime = beforeEntry.getKey(); } for (Map.Entry> afterEntry :afterClcokInTimeData.entrySet()){ afterClcokInTimeMap = afterEntry.getValue(); afterClcokInTime = afterEntry.getKey(); } //重复 if (beforeClcokInTimeMap != null && beforeClcokInTimeMap == afterClcokInTimeMap ){ String beforeTime = beforeClcokInTime.split("\\|")[0]; String afterTime = afterClcokInTime.split("\\|")[0]; long betWeenMinutes = DateUtil.getBetWeenMinutes(beforeTime,afterTime); String middileTime = DateUtil.AfterMinutes(beforeTime,betWeenMinutes/2); String signdateTime = beforeClcokInTimeMap.get("signdate") +" "+beforeClcokInTimeMap.get("signtime"); if (DateUtil.getTime(signdateTime).compareTo(DateUtil.getTime(middileTime)) <=0){ //该打卡归属前一个打卡点 afterClcokInTimeData.put(afterClcokInTime,null); }else if (DateUtil.getTime(signdateTime).compareTo(DateUtil.getTime(middileTime)) >0){ //该打卡归属后一个打卡点 beforeClcokInTimeData.put(beforeClcokInTime,null); } } } } return clcokInTimeData; } public static Map getNeedRecordClockInTime(List>> clcokInTimeData) { Map resultMap = Maps.newHashMap(); int inIndex = 1; int outIndex = 1; for (Map> clcokInTimeMap : clcokInTimeData){ //卡点 String point = ""; //当天打卡数据 Map clcokInTime = null; for (Map.Entry> entry :clcokInTimeMap.entrySet()){ point = entry.getKey(); clcokInTime = entry.getValue(); } //需要计算的班次打卡时间点 String pointTime = point.split("\\|")[0]; //start:开始打卡时间点,end:结束打卡时间点 String pointType = point.split("\\|")[1]; //empty:漏卡,equal:打卡时间和班次时间相等,before:打卡时间在班次时间之前,after:打卡时间在班次时间之后 String timeType = point.split("\\|")[2]; if (ClockPointEnum.START.getKey().equals(pointType)){ //开始时间打卡 String key = "j"+inIndex; if (!ClockPointEnum.EMPTY.getKey().equals(timeType) && clcokInTimeData != null){ String value = clcokInTime.get("signdate")+" "+clcokInTime.get("signtime"); resultMap.put(key,value); } inIndex++; }else if (ClockPointEnum.END.getKey().equals(pointType)){ //结束时间打卡 String key = "c"+outIndex; if (!ClockPointEnum.EMPTY.getKey().equals(timeType) && clcokInTimeData != null){ String value = clcokInTime.get("signdate")+" "+clcokInTime.get("signtime"); resultMap.put(key,value); } outIndex++; } } return resultMap; } }