You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在指定时间段内配对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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 06:17:14