有关 签到/签退 业务逻辑 的梳理与学习

  导言

     最近搞到了个签到管理,其中的业务逻辑感觉有点复杂(可能是我的方向不对),虽然是实现了,不过代码和逻辑很多,也有些乱,想趁着还记得逻辑来记录梳理一下,看看自己以后有没有更好的思路,或者有大佬有思路也可以在评论指导一下,非常感谢( ̄▽ ̄)!

 业务

    签到可以有时间段限制,一天可以有多段可签到时间(如9:00到12:00,15:00到18:00...),签退不能超过最大签到时间,也不能跨时间段签到)。

 逻辑

  由于可签到时间段我是按一天来想,所以格式是从一天的零点算起到当天的24:结束,所以下面的一些逻辑和实现也参照这个来。

    签到:

    这个比较简单,可以拿个bool变量IsInSignInTime 开始为假,判断当前签到时间是否在签到时间段里,直接拿到存数据库的时间段列表来循环判断就行,如果当前时间既大于可签到时间段开始时间又小于结束时间的话,就说明在可签到时间段里反之直接返回前端错误。

    签退:

    签退除了前端传的数据有误之外不管是 超过最大签到时间 还是 跨时间段 都是可以直接签退成功的,就是有上面两种情况的就要把本次签到的时间记为零

   1.跨时间段 

    由于上面在创建签到时做了限制,如果有可签到时间段,那最大签到时间不能超过24小时(如果超24小时的话肯定会跨时间段的)。所以签退只需考虑签到时间为一天以内的情况,有以下两种状态:

 1)签退跨时间段,但是没跨天

  这样直接和签到的逻辑差不多,拿个bool变量,如果跨了时间段就把其变真,后面做个置零判断。

  2)签退跨天,但是没跨可签到时间段

  如果有签到时间段:

这样还要在1)的循环中作判断,看有没有这样从今天晚上12点之前到明天凌晨0点的时间段,如果有的话就额外判断用户的签到时间段在不在这里面。在的话就不能判为跨时间。

C#代码实现

说明:

签到记录的时间是按分钟来记的,时间段的比较是用了C#的TimeOnly来比。里面有点注释掉的代码,不影响实现。


        #region 签到记录接口
        /// <summary>
        /// 签到定义
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        [Authorization]
        public IActionResult Create([FromBody] SignInOrSignOutDto parm)
        {


            var signIn = _signInService.GetId(parm.SignInID);

            if (_signInService.Any(m => m.SignInNo == signIn.SignInNo && m.Enable == false))
            {
                return toResponse(StatusCodeType.Error, $"该签到未启用,不能签到!");
            }

            //签到启用
            //没有同一条签到记录
            //在签到时间段

          
            //存在同一签到记录且状态为1的签到记录
            if (_signInRecordService.Any(m => m.SignInID == parm.SignInID && m.UserID == parm.UserID && m.Status == 1))
            {
                return toResponse(StatusCodeType.Error, $"不能重复签到!");
            }

            //不在签到时间段
            //从中拿到签到时间段
            // 将JSON字符串反序列化为List<SignInPeriods>对象列表
            var signInPeriodsList = JsonConvert.DeserializeObject<List<SignInPeriodsDto>>(signIn.SignInPeriods);

            TimeOnly currentTime = TimeOnly.FromDateTime(DateTime.Now);


            //拿个变量在循环里,如果当前时间在签到时间段就为真,反之为假
            bool IsInSignInTime = false;
            //循环比较判断
            foreach(SignInPeriodsDto signInPeriod in signInPeriodsList)
            {
                if(signInPeriod.StartTime < currentTime&& signInPeriod.EndTime > currentTime)
                {//在里面变真
                    IsInSignInTime = true;

                }

            }
            //为假就是不在签到时间段,返回错误
            if (!IsInSignInTime)
            {
                return toResponse(StatusCodeType.Error, $"不在签到时间段!");

            }
            try
            {
                var signInRecord = parm.Adapt<Base_SignInRecord>().ToCreate(_tokenManager.GetSessionInfo());

                signInRecord.Status = 1;
                signInRecord.StartTime = DateTime.Now;

                _unitOfWork.BeginTran();


               //新增签到记录表
                var response = _signInRecordService.Add(signInRecord);

                //改签到表对应签到的当前签到人数
                _signInService.Update(m => m.ID == parm.SignInID, m => new Base_SignIn()
                {
                    CurrentSignInNum = signIn.CurrentSignInNum + 1,
                });
                //改用户-签到表对应用户状态为在线/签到
                _signInUsersService.Update(m => m.SignInNo == signIn.SignInNo && m.UserID == parm.UserID, m => new Base_SignIn_Users()
                {
                    IsOnline = true
                });


                _unitOfWork.CommitTran();

                return toResponse(response);
            }
            catch (Exception)
            {
                _unitOfWork.RollbackTran();
                throw;
            }
        }


        /// <summary>
        /// 签退定义
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        [Authorization]
        public IActionResult Delete([FromBody] SignInOrSignOutDto parm)
        {



            //拿到数据
            var signIn = _signInService.GetId(parm.SignInID);
            var signInRecord = _signInRecordService.GetFirst(m => m.SignInID == signIn.ID && m.UserID == parm.UserID && m.Status == 1);


            if (_signInService.Any(m => m.SignInNo == signIn.SignInNo && m.Enable == false))
            {
                return toResponse(StatusCodeType.Error, $"该签到未启用,不能签退!");
            }

            //不做数的 体现在Time字段上
            //超出最大签到时间的
            //横跨签到时间段的



            //算出签到时间Time
            DateTime currentTimeForDateTime = DateTime.Now;
            var TimePeriod = currentTimeForDateTime - signInRecord.StartTime;
            var currentTime = TimeOnly.FromDateTime(currentTimeForDateTime);

            测试 可删
            算出签到时间Time
            //DateTime currentTimeForDateTime = new DateTime(2024, 9, 24, 07, 49, 15);
            //var TimePeriod = currentTimeForDateTime - signInRecord.StartTime;
            var currentTime = TimeOnly.FromDateTime(currentTimeForDateTime);
            //TimeOnly currentTime = TimeOnly.Parse("07:49:15");

            int  Time = (int)TimePeriod.TotalMinutes;
            //签到提示信息,如果签到没问题就直接输成功
            var remindMessager = "成功";


            //从中拿到签到时间段
            // 将JSON字符串反序列化为List<SignInPeriods>对象列表
            var signInPeriodsList = JsonConvert.DeserializeObject<List<SignInPeriodsDto>>(signIn.SignInPeriods);

            //看是否超了最大时间
            if (Time > signIn.MaxSignInTime && signIn.MaxSignInTime != 0)
            {
                Time = 0;
                remindMessager = "签到时间超出最大签到时间,本次签到时间置零!";

            }


            //如果是开了可签到时间段
            if (!string.IsNullOrEmpty(signIn.SignInPeriods))
            {
                直接拿开始时间和现在时间做判断,看其是这个时间段是否在某一时间段里面,其他不满足的都是跨时间段的
                //if (Time < 1440)//没隔天 一天的分钟计数
                //{
                    //把签到记录表的开始签到时间转成TimeOnly
                    var StartTime = TimeOnly.FromDateTime(signInRecord.StartTime);


                    //拿个变量在循环里,如果时间段没跨签到时间段就为真,反之为假
                    bool IsInSignInTime = false;

                    //看有没有开始时间为0:00的时间段和结束时间为24:00的时间段
                    SignInPeriodsDto todayPeriod = null; //结束时间为24:00的时间段
                    SignInPeriodsDto nextDayPeriod = null;//开始时间为0:00的时间段

                    //循环比较判断
                    foreach (SignInPeriodsDto signInPeriod in signInPeriodsList)
                    {
                        if (signInPeriod.StartTime < StartTime && signInPeriod.EndTime > currentTime)
                        {//在里面变真
                            IsInSignInTime = true;

                        }
                        if (signInPeriod.StartTime == TimeOnly.MinValue) //看其开始时间段是否为为0:00
                        {
                            nextDayPeriod = signInPeriod;

                        }
                        if (signInPeriod.EndTime == new TimeOnly(23, 59, 59)) //看其结束时间段是否为为24:00
                        {
                            todayPeriod = signInPeriod;

                        }
                    }

                    //有的话看开始签到时间是否在大于结束时间为24:00的时间段里   及  签退时间是否在在开始时间为0:00的时间段里
                    //不满足条件的直接判跨时间段
                    if (todayPeriod != null && nextDayPeriod != null)
                    {   //可以考虑将跨天取消掉,分别判是否在签到时间段里和单独看是否在跨天时间段里就行
                        //可以在添加签到时加个判断,如过加了时间段,那最大时间就不能出现超过24小时的情况,
                        //可以免除上面签到和签退时间隔天了还在同一签到时间段的情况
                        if(todayPeriod.StartTime< StartTime&& nextDayPeriod.EndTime >currentTime)
                        {
                            IsInSignInTime = true;
                        }

                    }


                    //如果没设置最大签到时间但是设置了时间段
                    //只判断其有没有超时间段,在关键判断那看其签到时间是不是超了一天
                    // Time > 1440由于上面已经验证了有没有时间段,所以到这的都是有时间段的,不用额外判断了
                    if (!IsInSignInTime|| Time > 1440)//跨时间段了
                    {
                        Time = 0;
                        remindMessager = "横跨时间段,本次签到时间置零!";
                    }

                //}
               
            }
            
            //至此留下了符合条件的
            try
            {

                var userSession = _tokenManager.GetSessionInfo();
                _unitOfWork.BeginTran();
                //改签到记录表状态为 签退,补上签到时间Time
                var response = _signInRecordService.Update(m => m.SignInID == signIn.ID && m.UserID == parm.UserID && m.Status == 1, m => new Base_SignInRecord()
                {
                    Status = 0,
                    EndTime = currentTimeForDateTime,
                    Time = Time,
                });

                //改签到表在线签到人数-1
                 _signInService.Update(m => m.ID == parm.SignInID, m => new Base_SignIn()
                {
                     CurrentSignInNum = signIn.CurrentSignInNum - 1,
                     UpdateID = userSession.UserID,
                     UpdateName = userSession.UserName,
                     UpdateTime = DateTime.Now
                 });
                //改 用户-签到表对应用户状态为离线/签退
                _signInUsersService.Update(m => m.SignInNo == signIn.SignInNo && m.UserID == parm.UserID,m=> new Base_SignIn_Users()
                {
                    IsOnline = false
                });

                _unitOfWork.CommitTran();

                return toResponse(StatusCodeType.Success, remindMessager);
            }
            catch (Exception)
            {
                _unitOfWork.RollbackTran();
                throw;
            }
       
        
        }


        #endregion

结语

   这个搞了有些久,还是怕有些情况没考虑,不过接口是考虑到的情况都测试通过了的。想着还有没有更优雅,更便捷的解决方法,不管是从业务开始考虑,还是从我这方法开始优化,那就看以后的我有没有时间看了,或者各位大佬指点一下,我会很开心的!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值