Files
jinko.yikatong/Src/Dlhis.ScheduledTask/DeviceUploadLog/Services/Implements/UserAttendanceDataAnalysisService.cs

270 lines
10 KiB
C#

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<GainfoEntity>()
.Where(x => request.UserCodes.Contains(x.Code))
.Where(x => SqlFunc.Subqueryable<UserAttendanceDataAnalysisEntity>()
.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<UserAttendanceDataAnalysisEntity>()
.Where(x => request.UserCodes.Contains(x.UserCode))
.ExecuteCommandAsync();
}
public async Task<UserAttendanceDataAnalysisInfoResponse> 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<UserAttendanceDataAnalysisEntity>()
.LeftJoin<GainfoEntity>((x, user) => x.UserCode == user.Code)
.LeftJoin<DepartmentEntity>((x, user, dept) => user.DepartmentNo == dept.No)
.WhereIF(request.Search.IsPresent(), x => x.UserCode == request.Search)
.Where(x => SqlFunc.Subqueryable<GainfoEntity>().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<DeviceUploadLogEntity>()
.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<ShiftTimeRange>(2) { shiftStartRange, shiftEndRange },
LastAnalysisDateTime = searchDate < DateOnly.FromDateTime(DateTime.Now.Date)
? searchDate.ToDateTime(TimeOnly.MaxValue)
: DateTime.Now,
Pagination = page
};
}
/// <summary>
/// 处理上班考勤数据分析结果。
/// </summary>
/// <param name="analysisResultTypes"></param>
/// <param name="userLogs"></param>
/// <param name="shift"></param>
private void HandlerClockIn(List<AttendanceDataAnalysisResultEnumType> analysisResultTypes, List<DeviceUploadLogEntity> 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);
}
}
}
/// <summary>
/// 处理下班考勤数据分析结果。
/// </summary>
/// <param name="analysisResultTypes"></param>
/// <param name="userLogs"></param>
/// <param name="shift"></param>
private void HandlerClockOut(List<AttendanceDataAnalysisResultEnumType> analysisResultTypes, List<DeviceUploadLogEntity> 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<BasicTypeEntity>()
.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);
}
/// <summary>
/// 检查指定日期是否为休息日或节假日。
/// </summary>
/// <param name="searchDate"></param>
/// <returns></returns>
private async Task<bool> IsRestDayAsync(DateOnly searchDate)
{
var now = DateTime.Now;
var currentHolidays = await _dbContext.Queryable<HolidayEntity>()
.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;
}
}
/// <summary>
///
/// </summary>
public class ShiftTimeRange
{
/// <summary>
///
/// </summary>
/// <param name="date"></param>
/// <param name="start"></param>
/// <param name="time"></param>
/// <param name="end"></param>
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; }
}