using Dlhis.Entity.Basic.Entity; using Dlhis.Entity.Basic.Enums; using Dlhis.Entity.Device.Entities; using Dlhis.Entity.Device.Enums; using Dlhis.ScheduledTask.DeviceUploadLog.Request; using Dlhis.ScheduledTask.DeviceUploadLog.Response; using Kurisu.AspNetCore.DataAccess.SqlSugar.Extensions; using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; using Kurisu.AspNetCore.Utils.Extensions; using SqlSugar; namespace Dlhis.ScheduledTask.DeviceUploadLog.Services.Implements; public class UserAttendanceDataAnalysisService : IUserAttendanceDataAnalysisService { private readonly IDbContext _dbContext; public UserAttendanceDataAnalysisService(IDbContext dbContext) { _dbContext = dbContext; } public async Task SaveAsync(UserAttendanceUserCodesRequest request) { //获取人员时候,自动过滤存在的人员,并且只获取在职的 var gainfos = await _dbContext.Queryable() .Where(x => request.UserCodes.Contains(x.Code)) .Where(x => SqlFunc.Subqueryable() .Where(p => p.UserCode == x.Code) .NotAny() ) .ToListAsync(); await _dbContext.InsertAsync(gainfos.Select(x => new UserAttendanceDataAnalysisEntity { UserCode = x.Code }).ToList() ); } public async Task DeleteAsync(UserAttendanceUserCodesRequest request) { //直接删除 await _dbContext.Deleteable() .Where(x => request.UserCodes.Contains(x.UserCode)) .ExecuteCommandAsync(); } public async Task GetPageAsync(UserAttendanceDataAnalysisPageRequest request) { var searchDate = DateOnly.FromDateTime(request.SearchDate); //查询日期的时间范围 var searchStart = searchDate.ToDateTime(TimeOnly.MinValue); var searchEnd = searchDate.ToDateTime(TimeOnly.MaxValue).AddSeconds(1); var page = await _dbContext.Queryable() .LeftJoin((x, user) => x.UserCode == user.Code) .LeftJoin((x, user, dept) => user.DepartmentNo == dept.No) .WhereIF(request.Search.IsPresent(), x => x.UserCode == request.Search) .Where(x => SqlFunc.Subqueryable().Where(p => p.Code == x.UserCode).Any()) .OrderBy(x => x.CreateTime, OrderByType.Desc) .Select((x, user, dept) => new UserAttendanceDataAnalysisResponse { UserCode = x.UserCode, UserName = user.Name, DepartmentName = dept.Name }) .ToPageListAsync(request); var (shiftStartRange, shiftEndRange) = await GetShiftTimeRangesAsync(searchDate); if (await IsRestDayAsync(searchDate)) { foreach (var item in page.Data) { item.StartAnalysisResultTypes.Add(AttendanceDataAnalysisResultEnumType.Rest); item.EndAnalysisResultTypes.Add(AttendanceDataAnalysisResultEnumType.Rest); } } else { var userCodes = page.Data.Select(x => x.UserCode).ToList(); var logs = await _dbContext.Queryable() .Where(x => x.RaiseDateTime >= searchStart && x.RaiseDateTime < searchEnd) .Where(x => x.Type == DeviceUploadLogTypeEnumType.Attendance) .Where(x => userCodes.Contains(x.UserUniqueNo)) .ToListAsync(); foreach (var item in page.Data) { var userLogs = logs.Where(x => x.UserUniqueNo == item.UserCode).ToList(); //无考勤记录 if (userLogs.Count == 0) { item.StartAnalysisResultTypes.Add(AttendanceDataAnalysisResultEnumType.NoAttendanceRecord); item.EndAnalysisResultTypes.Add(AttendanceDataAnalysisResultEnumType.NoAttendanceRecord); continue; } HandlerClockIn(item.StartAnalysisResultTypes, userLogs, shiftStartRange); HandlerClockOut(item.EndAnalysisResultTypes, userLogs, shiftEndRange); } } return new UserAttendanceDataAnalysisInfoResponse { ShiftTimes = new List(2) { shiftStartRange, shiftEndRange }, LastAnalysisDateTime = searchDate < DateOnly.FromDateTime(DateTime.Now.Date) ? searchDate.ToDateTime(TimeOnly.MaxValue) : DateTime.Now, Pagination = page }; } /// /// 处理上班考勤数据分析结果。 /// /// /// /// private void HandlerClockIn(List analysisResultTypes, List userLogs, ShiftTimeRange shift) { var inLogs = userLogs.OrderBy(x => x.RaiseDateTime) .Where(x => x.RaiseDateTime >= shift.Start && x.RaiseDateTime <= shift.End) .ToList(); if (inLogs.Count == 0) { analysisResultTypes.Add(AttendanceDataAnalysisResultEnumType.NoAttendanceRecord); } else { var isClockIn = inLogs.Any(x => x.RaiseDateTime <= shift.Time); if (!isClockIn) { var isLate = inLogs.Any(x => !(x.RaiseDateTime <= shift.Time) && x.RaiseDateTime > shift.Time && x.RaiseDateTime <= shift.End); analysisResultTypes.Add( isLate ? AttendanceDataAnalysisResultEnumType.Late : AttendanceDataAnalysisResultEnumType.Missing ); } else { analysisResultTypes.Add(AttendanceDataAnalysisResultEnumType.Normal); } } } /// /// 处理下班考勤数据分析结果。 /// /// /// /// private void HandlerClockOut(List analysisResultTypes, List userLogs, ShiftTimeRange shift) { var outLogs = userLogs.OrderBy(x => x.RaiseDateTime) .Where(x => x.RaiseDateTime >= shift.Start) .ToList(); if (outLogs.Count == 0) { analysisResultTypes.Add(AttendanceDataAnalysisResultEnumType.NoAttendanceRecord); } else { var isClockOut = outLogs.Any(x => x.RaiseDateTime >= shift.Time); if (!isClockOut) { var isEarly = outLogs.Any(x => x.RaiseDateTime < shift.Time); analysisResultTypes.Add( isEarly ? AttendanceDataAnalysisResultEnumType.Early : AttendanceDataAnalysisResultEnumType.Missing ); } else { analysisResultTypes.Add(AttendanceDataAnalysisResultEnumType.Normal); } } } private async Task<(ShiftTimeRange ShiftStartRange, ShiftTimeRange shiftEndRange)> GetShiftTimeRangesAsync(DateOnly date) { var longDayShifts = await _dbContext.Queryable() .Where(x => x.Type.StartsWith("LongDayShift")) .Select(x => new { x.Code, x.Type }) .ToListAsync(); var longDayShift = longDayShifts.FirstOrDefault(x => x.Type == "LongDayShift")?.Code; longDayShift.IsPresent().ThrowIfFalse("请先配置长白班的时间段LongDayShift"); // 上班时间范围 var longDayShiftStart = longDayShifts.FirstOrDefault(x => x.Type == "LongDayShift-Start")?.Code; longDayShiftStart.IsPresent().ThrowIfFalse("请先配置长白班的时间段LongDayShift-Start"); var startArr = longDayShiftStart!.Split(","); // 下班时间范围 var longDayShiftEnd = longDayShifts.FirstOrDefault(x => x.Type == "LongDayShift-End")?.Code; longDayShiftEnd.IsPresent().ThrowIfFalse("请先配置长白班的时间段LongDayShift-End"); var arr = longDayShift!.Split("-"); var shiftStart = TimeOnly.Parse(arr[0]); var shiftEnd = TimeOnly.Parse(arr[1]); var startRange = new ShiftTimeRange(date, shiftStart.AddMinutes(int.Parse(startArr[0])), shiftStart, shiftStart.AddMinutes(int.Parse(startArr[1]))); var endRange = new ShiftTimeRange(date, shiftEnd.AddMinutes(int.Parse(longDayShiftEnd!)), shiftEnd, TimeOnly.MaxValue); return (startRange, endRange); } /// /// 检查指定日期是否为休息日或节假日。 /// /// /// private async Task IsRestDayAsync(DateOnly searchDate) { var now = DateTime.Now; var currentHolidays = await _dbContext.Queryable() .Where(x => x.Year == now.Year && searchDate >= x.Start && searchDate <= x.End) .Select(x => new { x.Type, x.Name, x.Start, x.End }) .ToListAsync(); // 优先判断节假日类型 if (currentHolidays.Any(x => x.Type == HolidayEnumType.Rest)) { return true; } if (currentHolidays.Any(x => x.Type == HolidayEnumType.NeedWork)) { return false; } // 否则按周末判断 var isWeekend = searchDate.DayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday; return isWeekend; } } /// /// /// public class ShiftTimeRange { /// /// /// /// /// /// /// public ShiftTimeRange(DateOnly date, TimeOnly start, TimeOnly time, TimeOnly end) { Start = date.ToDateTime(start); Time = date.ToDateTime(time); End = date.ToDateTime(end); } public DateTime Start { get; set; } public DateTime Time { get; set; } public DateTime End { get; set; } }