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

ASP.NET MVC考勤系统:先更新上条下班记录再新增打卡记录

考勤刷卡逻辑修复方案

现存问题定位

  • 业务判断逻辑放前端完全无效:前端无法获取数据库中该卡号的历史打卡状态,原有es.HoraEntrada == null的判断永远不成立,所有请求都会被错误标记为下班打卡
  • 后端接口仅实现了新增记录逻辑,缺失「补全上条未闭合记录下班时间、计算工时」的核心流程
  • 前端传参使用拼接的dd/MM/yyyy格式时间字符串,无法被EF正确绑定为DateTime类型,容易出现日期格式解析错误
  • AJAX回调函数名拼写错误:sucess少写字母c,导致成功回调永远无法触发
  • 刷卡后文本域未清空,同一张卡连续刷卡时不会触发change事件

后端修复实现(Controller层)

所有时间、打卡状态判断全部移到后端处理,统一取服务器时间,避免前端篡改本地时间作弊,同时用EF事务保证数据一致性,不会出现只更新没新增、或者只新增没更新的脏数据。

[HttpPost]
public ActionResult AddReport(int IdCartao)
{
    using (EMSistec bd = new EMSistec())
    {
        // 开启事务,保证更新和新增操作原子性
        using (var trans = bd.Database.BeginTransaction())
        {
            try
            {
                DateTime now = DateTime.Now;
                // 1. 查询当前卡号最新的未闭合记录(下班时间为空)
                var lastUnclosedRecord = bd.entradas_saidas
                    .Where(r => r.IdCartao == IdCartao && r.HoraSaida == null)
                    .OrderByDescending(r => r.IdE_S)
                    .FirstOrDefault();

                // 2. 如果存在未闭合记录,补全下班时间、计算当日工时
                if (lastUnclosedRecord != null)
                {
                    lastUnclosedRecord.HoraSaida = now;
                    // 计算工时,单位为小时,如需精确到分钟可以改存TotalMinutes
                    double workHours = (now - lastUnclosedRecord.HoraEntrada.Value).TotalHours;
                    // 兼容跨天夜班场景:如果算出来是负数说明跨天,加24小时
                    if (workHours < 0) workHours += 24;
                    lastUnclosedRecord.HoraDiarias = (int)Math.Round(workHours, 0);
                    bd.Entry(lastUnclosedRecord).State = System.Data.Entity.EntityState.Modified;
                }

                // 3. 新增本次的上班打卡记录
                entradas_saidas newRecord = new entradas_saidas()
                {
                    IdCartao = IdCartao,
                    DataRegisto = now.Date,
                    HoraEntrada = now,
                    HoraSaida = null,
                    HoraDiarias = null
                };
                bd.entradas_saidas.Add(newRecord);

                // 4. 统一提交变更
                bd.SaveChanges();
                trans.Commit();
                return Json(new { msg = "SUCCESS" }, JsonRequestBehavior.AllowGet);
            }
            catch (Exception ex)
            {
                trans.Rollback();
                return Json(new { msg = "ERROR", error = ex.Message }, JsonRequestBehavior.AllowGet);
            }
        }
    }
}

注意:接口参数只需要接收卡号即可,所有时间值由后端服务器生成,不要信任前端传入的时间参数,从根源上避免代打卡、篡改时间的问题。


前端修复实现(View脚本部分)

简化前端逻辑,只负责读取卡号、发送请求、处理结果,去掉所有前端业务判断:

$(function () {
    // 页面加载后自动聚焦刷卡输入框,避免手动点击
    $("#readONchange").focus();
    
    $("#readONchange").change(function () {
        // 读取卡号,去掉前后空格、读卡器带的特殊字符
        let cardId = $.trim($(this).val());
        if (!cardId) {
            alert("未读取到有效卡号");
            $(this).val('').focus();
            return;
        }

        $.ajax({
            type: "POST",
            url: '@Url.Action("AddReport")',
            // 只传卡号参数
            data: JSON.stringify({ IdCartao: parseInt(cardId) }),
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            // 修正原拼写错误:sucess -> success
            success: function (res) {
                if (res.msg === "SUCCESS") {
                    alert("打卡成功");
                    // 清空输入框、重新聚焦,准备下次刷卡
                    $("#readONchange").val('').focus();
                    // 刷新页面显示最新记录
                    location.reload();
                } else {
                    alert("打卡失败:" + res.error);
                    $("#readONchange").val('').focus();
                }
            },
            error: function () {
                alert("网络错误,打卡失败,请重试");
                $("#readONchange").val('').focus();
            }
        });
    });
});

优化建议

  • 数据库层面对IdCartao和HoraSaida字段建立联合索引,大幅提升未闭合记录的查询速度,适配大量打卡数据的场景
  • 增加卡号合法性校验:后端查询卡号是否在员工表中存在,不存在直接返回错误,不写入无效记录
  • 如果需要支持多次进出(比如一天打4次卡:上午上班、中午下班、下午上班、下午下班),现有逻辑完全兼容,每次刷卡都会自动闭合上一条、新增新的上班记录,工时会自动分段存储
  • 可以给刷卡输入框加输入长度限制,避免读卡器异常时读入过长的无效字符串

内容的提问来源于stack exchange,提问作者NewbieProgrammer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 22:06:51