如何在指定时间段内配对Attendancelogs的In/Out记录并计算员工总工时
嘿,很高兴看到你已经搞定了Attendancelogs按CheckType排序的问题,接下来要处理考勤记录配对和工时计算对吧?结合你描述的场景,我来分享下具体的实现思路和代码参考:
核心思路:In/Out记录的正确配对逻辑
首先得明确:考勤记录的配对必须基于时间顺序,而不是单纯的CheckType排序。如果之前只是把所有In放在前面、所有Out放在后面,那配对肯定会出错——毕竟员工是按时间先后打卡In和Out的,所以第一步要确保每个员工的记录是按CheckTime升序排列的(你提到已经排序完成,这里要确认是按时间排序哦)。
配对的逻辑很直观:对每个员工的打卡记录列表,我们维护一个“待配对的In记录”变量,然后逐个遍历:
- 遇到
CheckType = 'In':如果已有待配对的In,说明上一次的In没有对应的Out(属于你提到的异常场景,按前置规则处理),然后把当前In设为新的待配对记录; - 遇到
CheckType = 'Out':如果有对应的待配对In,就生成一对有效考勤记录,计算这段工时;如果没有待配对In,说明这是无前置In的异常Out记录,同样按前置规则处理。
具体实现代码参考
假设你的实体类和已有方法结构如下,我给你写了完整的配对和工时计算逻辑:
// 你的考勤日志实体 public class Attendancelog { public int EmployeeID { get; set; } public DateTime CheckTime { get; set; } public string CheckType { get; set; } // 取值为"In"或"Out" // 其他属性:比如DeviceID等 } // 配对后的考勤结果实体 public class PairedAttendance { public int EmployeeID { get; set; } public DateTime? InTime { get; set; } public DateTime? OutTime { get; set; } public TimeSpan WorkHours { get; set; } public bool IsException { get; set; } // 标记是否为异常记录 } // 生成配对考勤列表的方法 public List<PairedAttendance> GeneratePairedAttendances(DateTime startDate, DateTime endDate) { // 调用你提到的getSingleDevice...方法,获取指定时间段内的记录,这里要确保按时间排序! var rawLogs = GetSingleDeviceAttendanceLogs(startDate, endDate) .GroupBy(log => log.EmployeeID) .Select(group => group.OrderBy(log => log.CheckTime).ToList()) // 按时间升序排列每组记录 .ToList(); var pairedAttendances = new List<PairedAttendance>(); foreach (var employeeLogs in rawLogs) { Attendancelog pendingInLog = null; foreach (var log in employeeLogs) { if (log.CheckType == "In") { // 处理未配对的前置In记录(异常) if (pendingInLog != null) { pairedAttendances.Add(new PairedAttendance { EmployeeID = log.EmployeeID, InTime = pendingInLog.CheckTime, OutTime = null, // 你可以按前置规则补全,比如当天23:59:59 WorkHours = TimeSpan.Zero, IsException = true }); } // 将当前In设为待配对 pendingInLog = log; } else if (log.CheckType == "Out") { if (pendingInLog != null) { // 正常配对,计算工时 var workDuration = log.CheckTime - pendingInLog.CheckTime; pairedAttendances.Add(new PairedAttendance { EmployeeID = log.EmployeeID, InTime = pendingInLog.CheckTime, OutTime = log.CheckTime, WorkHours = workDuration, IsException = false }); // 清空待配对In pendingInLog = null; } else { // 处理无前置In的Out记录(异常) pairedAttendances.Add(new PairedAttendance { EmployeeID = log.EmployeeID, InTime = null, // 按前置规则补全,比如当天00:00:00 OutTime = log.CheckTime, WorkHours = TimeSpan.Zero, IsException = true }); } } } // 遍历结束后,处理剩余的未配对In记录 if (pendingInLog != null) { pairedAttendances.Add(new PairedAttendance { EmployeeID = pendingInLog.EmployeeID, InTime = pendingInLog.CheckTime, OutTime = null, WorkHours = TimeSpan.Zero, IsException = true }); } } return pairedAttendances; } // 你的getSingleDevice...方法(调整为按时间排序) public List<Attendancelog> GetSingleDeviceAttendanceLogs(DateTime startDate, DateTime endDate) { // 这里替换成你原有的数据查询逻辑,重点是按CheckTime排序 return YourDbContext.Attendancelogs .Where(log => log.DeviceID == YourTargetDeviceID && log.CheckTime >= startDate && log.CheckTime <= endDate.AddDays(1).AddTicks(-1)) // 包含结束日的最后一刻 .OrderBy(log => log.CheckTime) // 必须按时间排序,而不是CheckType! .ToList(); }
计算员工总工时
得到配对列表后,按员工分组求和即可得到总工时:
var totalWorkHours = pairedAttendances .GroupBy(p => p.EmployeeID) .Select(g => new { EmployeeID = g.Key, TotalHours = g.Sum(p => p.WorkHours.TotalHours) }) .ToList();
关键注意事项
- 排序优先级:一定要按
CheckTime升序排序,而不是CheckType!如果单纯按CheckType把所有In放前面,会完全打乱时间顺序,导致配对错误。 - 时间段边界:查询时注意包含EndDate的完整日期,比如用
endDate.AddDays(1).AddTicks(-1)来覆盖当天的最后一秒。 - 异常适配:代码里的异常处理逻辑可以根据你前置问题中定义的规则调整,比如补全默认时间、标记具体异常类型(如“无Out记录”“无In记录”)等。
内容的提问来源于stack exchange,提问作者user9630128
相关产品推荐
相关产品推荐

