package com.engine.kq.job; import cn.hutool.core.convert.Convert; import com.alibaba.fastjson.JSON; import com.engine.kq.biz.KQShiftManagementComInfo; import com.engine.kq.biz.KQShiftRestTimeSectionComInfo; import com.engine.kq.biz.KQWorkTime; import com.engine.kq.entity.WorkTimeEntity; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import weaver.common.DateUtil; import weaver.conn.RecordSet; import weaver.general.BaseBean; import java.time.Duration; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.*; /** * @author:dxfeng * @createTime: 2024/03/29 * @version: 1.0 */ public class UpdateEffectiveDuration { /** * 日期时间格式化 */ private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); /** * 有效时长计算逻辑: * 1、最早、最晚打卡时间,与开始时间、结束时间没有交集=》有效时长=共计时长 * 2、最早、最晚打卡时间,与开始时间、结束时间有交集=》 * (1)比较最早打卡时间与开始时间,如果最早打卡时间晚于开始时间,统计晚打卡的分钟数 * (2)比较最晚打卡时间与结束时间,如果最晚打卡时间早于结束时间,统计早打卡的分钟数 * (3)统计相差的分钟数,按半小时起算,有效时长=共计时长-相差时长 * * @param mainId * @param userId * @param gzrq * @param earlyStartTime * @param afterEndTime * @param startTime * @param endTime */ public static void execute(String mainId, String userId, String gzrq, String earlyStartTime, String afterEndTime, String startTime, String endTime) { RecordSet recordSet = new RecordSet(); try { LocalDateTime startSignTime = LocalDateTime.parse(earlyStartTime, DATE_TIME_FORMATTER); LocalDateTime endSignTime = LocalDateTime.parse(afterEndTime, DATE_TIME_FORMATTER); LocalDateTime startWorkTime = LocalDateTime.parse(startTime, DATE_TIME_FORMATTER); LocalDateTime endWorkTime = LocalDateTime.parse(endTime, DATE_TIME_FORMATTER); // 最早、最晚打卡时间,与开始时间、结束时间,是否存在交集 boolean hasIntersection = startSignTime.isBefore(endWorkTime) && endSignTime.isAfter(startWorkTime); recordSet.executeQuery("select gjsc from uf_jbtz where id = ?", mainId); double gjscHours = 0.0; if (recordSet.next()) { gjscHours = recordSet.getDouble("gjsc"); } if (gjscHours < 0) { throw new Exception("mainId===" + mainId + ",共计时长小于0"); } if (hasIntersection) { int totalMinutes = 0; if (startSignTime.isAfter(startWorkTime)) { int startDifference = (int) (startWorkTime.until(startSignTime, ChronoUnit.MINUTES)); recordSet.execute("mainId=" + mainId + ",userId=" + userId + ",gzrq=" + gzrq + ",startDifference=" + startDifference); totalMinutes += startDifference; } if (endSignTime.isBefore(endWorkTime)) { int endDifference = (int) (endSignTime.until(endWorkTime, ChronoUnit.MINUTES)); recordSet.execute("mainId=" + mainId + ",userId=" + userId + ",gzrq=" + gzrq + ",endDifference=" + endDifference); totalMinutes += endDifference; } // 总计相差时间,按照半小时,向上取整 double totalHours = (double) totalMinutes / 60; double timeDifference = gjscHours - totalHours; // 将小时数按照0.5小时的单位向下取整 double roundedHours = (int) Math.floor(timeDifference / 0.5) * 0.5; recordSet.writeLog("userId==" + userId + ",totalMinutes===" + totalMinutes + ",gjscHours===" + gjscHours + ",totalHours==" + totalHours + ",roundedHours==" + roundedHours); recordSet.executeUpdate("update uf_jbtz set yxsc=? where id=? ", roundedHours, mainId); } else { // 最早、最晚打卡时间,与开始时间、结束时间没有交集=》有效时长=共计时长 recordSet.executeUpdate("update uf_jbtz set yxsc=? where id=? ", gjscHours, mainId); } } catch (Exception e) { recordSet.writeLog(e); } } /** * 更新加班台账表,有效时长字段 *

*

导入的加班数据增加根据班次的最早最晚打卡时间取交集,按照系统的加班单和打卡数据取交集处理

* * @param mainId 当前数据ID * @param userId 加班人ID * @param gzrq 归属日志 * @param earlyStartTime 最早打卡时间 * @param afterEndTime 最晚打卡时间 */ public static void execute1(String mainId, String userId, String gzrq, String earlyStartTime, String afterEndTime, String startTime, String endTime) { RecordSet recordSet = new RecordSet(); try { KQWorkTime kqWorkTime = new KQWorkTime(); WorkTimeEntity workTimeEntity = kqWorkTime.getWorkTime(userId, gzrq); String serialId = workTimeEntity.getSerialId(); recordSet.writeLog("serialId===" + serialId); if (StringUtils.isBlank(serialId) || "-1".equals(serialId)) { kqWorkTime.setIsFormat(true); workTimeEntity = kqWorkTime.getWorkTime(userId, gzrq); serialId = workTimeEntity.getSerialId(); } recordSet.writeLog("userId==" + userId + ",serialId===" + JSON.toJSONString(serialId) + ",workTimeEntity===" + JSON.toJSONString(workTimeEntity)); // 转换所有的工作时间 List> workTimeList = new ArrayList<>(); Map workMap = new HashMap<>(); workMap.put("startTimes", startTime); workMap.put("endTimes", endTime); workTimeList.add(workMap); recordSet.writeLog("userId==" + userId + ",workTimeList===" + JSON.toJSONString(workTimeList)); // 转换所有的休息时间 List> restTimeList = new ArrayList<>(); KQShiftManagementComInfo kqShiftManagementComInfo = new KQShiftManagementComInfo(); String isRestTimeOpen = kqShiftManagementComInfo.getIsresttimeopen(serialId); // 排除休息时间是否开启 1表示开启 recordSet.writeLog("isRestTimeOpen===" + isRestTimeOpen); if ("1".equals(isRestTimeOpen)) { List restSectionTimes = new KQShiftRestTimeSectionComInfo().getRestSectionTimes(serialId); recordSet.writeLog("userId==" + userId + ",restSectionTimes===" + JSON.toJSONString(restSectionTimes)); if (CollectionUtils.isNotEmpty(restSectionTimes)) { //convertDateTime(gzrq, restSectionTimes, restTimeList); convertDateTime2(gzrq, restSectionTimes, restTimeList); recordSet.writeLog("userId==" + userId + ",restTimeList===" + JSON.toJSONString(restTimeList)); } } // 所有的有效时间区间 recordSet.writeLog("userId==" + userId + ",earlyStartTime===" + earlyStartTime + ",afterEndTime===" + afterEndTime); List effectiveTime = getEffectiveTime(earlyStartTime, afterEndTime, startTime, endTime, restTimeList); recordSet.writeLog("userId==" + userId + ",effectiveTime===" + JSON.toJSONString(effectiveTime)); // 有效区间总计分钟数 long totalMinutes = effectiveTime.stream() .mapToInt(interval -> { String[] parts = interval.split(","); LocalDateTime start = LocalDateTime.parse(parts[0], DATE_TIME_FORMATTER); LocalDateTime end = LocalDateTime.parse(parts[1], DATE_TIME_FORMATTER); return (int) (start.until(end, ChronoUnit.MINUTES)); }) .sum(); double totalHours = (double) totalMinutes / 60; // 将小时数按照0.5小时的单位向下取整 double roundedHours = (int) Math.floor(totalHours / 0.5) * 0.5; recordSet.writeLog("userId==" + userId + ",totalMinutes===" + totalMinutes + ",totalHours==" + totalHours + ",roundedHours==" + roundedHours); // 更新当前数据的有效时长 recordSet.executeUpdate("update uf_jbtz set yxsc=? where id=? ", roundedHours, mainId); } catch (Exception e) { recordSet.writeLog(e); } } /** * 获取有效时长时间区间 */ private static List getEffectiveTime(String earlyStartTime, String afterEndTime, String startTime, String endTime, List> restTimeList) { // 打卡时间区间,去除所有的休息时间 List effectiveTimeList = new ArrayList<>(); LocalDateTime startSignTime = LocalDateTime.parse(earlyStartTime, DATE_TIME_FORMATTER); LocalDateTime endSignTime = LocalDateTime.parse(afterEndTime, DATE_TIME_FORMATTER); LocalDateTime startWorkTime = LocalDateTime.parse(startTime, DATE_TIME_FORMATTER); LocalDateTime endWorkTime = LocalDateTime.parse(endTime, DATE_TIME_FORMATTER); // 计算交集 LocalDateTime intersectionStart = startSignTime.isAfter(startWorkTime) ? startSignTime : startWorkTime; LocalDateTime intersectionEnd = endSignTime.isBefore(endWorkTime) ? endSignTime : endWorkTime; List restIntervals = new ArrayList<>(); for (Map restTimeMap : restTimeList) { restIntervals.add(new TimeInterval( LocalDateTime.parse(restTimeMap.get("startTimes"), DATE_TIME_FORMATTER), LocalDateTime.parse(restTimeMap.get("endTimes"), DATE_TIME_FORMATTER) )); } // 休息时间区间排序 restIntervals.sort(Comparator.comparing(TimeInterval::getStart)); // 实际的工作开始时间、结束时间 List workIntervals = calculateWorkIntervals(intersectionStart, intersectionEnd, restIntervals); //new BaseBean().writeLog("workIntervals===" + JSON.toJSONString(workIntervals)); for (TimeInterval workInterval : workIntervals) { effectiveTimeList.add(workInterval.getStart().format(DATE_TIME_FORMATTER) + "," + workInterval.getEnd().format(DATE_TIME_FORMATTER)); } long totalMinutes = calculateTotalWorkMinutes(workIntervals); //new BaseBean().writeLog("totalMinutes==="+totalMinutes); //if (intersectionStart.isBefore(intersectionEnd)) { // // 去除休息时间 // for (Map restTimeMap : restTimeList) { // LocalDateTime startRestTime = LocalDateTime.parse(restTimeMap.get("startTimes"), DATE_TIME_FORMATTER); // LocalDateTime endRestTime = LocalDateTime.parse(restTimeMap.get("endTimes"), DATE_TIME_FORMATTER); // // if (!intersectionEnd.isBefore(startRestTime) && !intersectionStart.isAfter(endRestTime)) { // if (intersectionStart.isBefore(startRestTime)) { // // 交集在休息时间之前 // intersectionEnd = intersectionEnd.isBefore(startRestTime) ? intersectionEnd : startRestTime; // } else if (intersectionEnd.isAfter(endRestTime)) { // // 交集在休息时间之后 // intersectionStart = intersectionStart.isAfter(endRestTime) ? intersectionStart : endRestTime; // } else { // // 休息时间在交集中间 // intersectionStart = startRestTime; // intersectionEnd = endRestTime; // } // } // } // effectiveTimeList.add(intersectionStart.format(DATE_TIME_FORMATTER) + "," + intersectionEnd.format(DATE_TIME_FORMATTER)); //} return effectiveTimeList; } /** * 转换日期时间 * * @param gzrq 归属日期 * @param sectionTimes 转换前时间 * @param timeList 转换后日期时间 */ private static void convertDateTime(String gzrq, List sectionTimes, List> timeList) { String currentDate = gzrq; String preTime = ""; for (Object sectionTime : sectionTimes) { List> sectionTime1 = (List) sectionTime; if (sectionTime1.size() != 2) { break; } // 获取当前的打卡时间 Map startMap = sectionTime1.get(0); String startTimes = startMap.get("times"); Map map = new HashMap<>(); currentDate = getDateStr(currentDate, preTime, startTimes); map.put("startTimes", currentDate + " " + startTimes + ":00"); Map endMap = sectionTime1.get(1); String endTimes = endMap.get("times"); currentDate = getDateStr(currentDate, startTimes, endTimes); map.put("endTimes", currentDate + " " + endTimes + ":00"); preTime = endTimes; timeList.add(map); } } private static void convertDateTime2(String gzrq, List sectionTimes, List> timeList) { String currentDate = gzrq; String preTime = ""; for (Object sectionTime : sectionTimes) { List> sectionTime1 = (List) sectionTime; if (sectionTime1.size() != 2) { break; } // 获取当前的打卡时间 Map startMap = sectionTime1.get(0); String startTimes = startMap.get("times"); String startAcross = startMap.get("across"); Map map = new HashMap<>(); //currentDate = getDateStr(currentDate, preTime, startTimes); if ("1".equals(startAcross)) { map.put("startTimes", DateUtil.addDate(currentDate, 1) + " " + startTimes + ":00"); } else { map.put("startTimes", currentDate + " " + startTimes + ":00"); } Map endMap = sectionTime1.get(1); String endTimes = endMap.get("times"); String endAcross = endMap.get("across"); //currentDate = getDateStr(currentDate, startTimes, endTimes); if ("1".equals(endAcross)) { map.put("endTimes", DateUtil.addDate(currentDate, 1) + " " + endTimes + ":00"); } else { map.put("endTimes", currentDate + " " + endTimes + ":00"); } preTime = endTimes; timeList.add(map); } } /** * 组合日期、时间 * * @param currentDate 当前日期 * @param preTime 之前处理的时间 * @param currentTime 当前需要处理的时间 * @return */ private static String getDateStr(String currentDate, String preTime, String currentTime) { // 前一个时间为空,返回当前日期+当前时间 if (StringUtils.isBlank(preTime)) { return currentDate; } DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm"); LocalTime localPreTime = LocalTime.parse(preTime, formatter); LocalTime localCurrentTime = LocalTime.parse(currentTime, formatter); if (localPreTime.isAfter(localCurrentTime)) { return DateUtil.addDate(currentDate, 1); } else { return currentDate; } } public static List calculateWorkIntervals(LocalDateTime startWork, LocalDateTime endWork, List restIntervals) { List workIntervals = new ArrayList<>(); LocalDateTime currentStart = startWork; for (TimeInterval restInterval : restIntervals) { LocalDateTime restStart = restInterval.getStart(); LocalDateTime restEnd = restInterval.getEnd(); // 如果休息时间与工作时间重合,则调整工作时间段 if (currentStart.isBefore(restEnd) && endWork.isAfter(restStart)) { if (currentStart.isBefore(restStart)) { workIntervals.add(new TimeInterval(currentStart, restStart)); } currentStart = restEnd; } } // 添加剩余的工作时间段 if (currentStart.isBefore(endWork)) { workIntervals.add(new TimeInterval(currentStart, endWork)); } return workIntervals; } public static long calculateTotalWorkMinutes(List workIntervals) { long totalMinutes = 0; for (TimeInterval interval : workIntervals) { totalMinutes += Duration.between(interval.getStart(), interval.getEnd()).toMinutes(); } return totalMinutes; } static class TimeInterval { private LocalDateTime start; private LocalDateTime end; public TimeInterval(LocalDateTime start, LocalDateTime end) { this.start = start; this.end = end; } public LocalDateTime getStart() { return start; } public LocalDateTime getEnd() { return end; } } }