using Dlhis.Basic.Api; using Dlhis.Entity.Basic.Entity; using Dlhis.Entity.Device.Entities; using Dlhis.Entity.Device.Enums; using Dlhis.Entity.Device.Models; using Dlhis.ScheduledTask.TaskAssignment.Dto; using Dlhis.ScheduledTask.TaskAssignment.Request; using Dlhis.ScheduledTask.TaskAssignment.Response; using Kurisu.AspNetCore.CustomClass; using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; using Kurisu.AspNetCore.Utils; using Kurisu.AspNetCore.Utils.Extensions; using Microsoft.AspNetCore.DataProtection; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Options; using Newtonsoft.Json.Linq; using SqlSugar; namespace Dlhis.ScheduledTask.TaskAssignment.Services.Implements; public class AssignmentService : IAssignmentService { private readonly IDbContext _dbContext; private readonly IDataProtectionProvider _dataProtectionProvider; private readonly IMemoryCache _memoryCache; private readonly IGainfoPermissionApi _gainfoPermissionApi; private readonly ILogger _logger; private readonly FaceUrlOptions _faceUrlOptions; public AssignmentService(IDbContext dbContext, IDataProtectionProvider dataProtectionProvider, IOptions options, IMemoryCache memoryCache, IGainfoPermissionApi gainfoPermissionApi, ILogger logger) { _dbContext = dbContext; _dataProtectionProvider = dataProtectionProvider; _memoryCache = memoryCache; _gainfoPermissionApi = gainfoPermissionApi; _logger = logger; _faceUrlOptions = options.Value; } public async Task> DeviceGetTasksAsync(DeviceTaskGetRequest request) { var data = await GetDataAsync(request); if (data.IsEmpty()) return new List(); await HandlerTimePeriodAsync(data); var deviceUploadInfos = await GetDeviceUploadRecordInfosAsync(data); var timeLimitedDataProtector = _dataProtectionProvider .CreateProtector("down:face") .ToTimeLimitedDataProtector(); return data.GroupBy(x => new { x.DeviceUniqueNo, x.DeviceConnectionInfo, x.FuncStr }) .Select(x => { var d = x.Key.DeviceUniqueNo; var dc = x.Key.DeviceConnectionInfo ?? "{}"; var func = x.Key.FuncStr ?? "[]"; var jObj = new JObject { { "connectionInfo", JToken.Parse(dc) }, { "function", JArray.Parse(func) } }; return new DeviceTaskToDeviceByDeviceResponse { DeviceUniqueNo = d, DeviceInfo = jObj, Tasks = x.Select(item => { var key = $"face:{item.UserCode}"; var token = item.Type == DeviceTaskEnumType.Delete ? string.Empty : timeLimitedDataProtector.Protect(key, TimeSpan.FromSeconds(_faceUrlOptions.Limit)); var dto = new TaskCombineDto { TaskId = item.TaskId, Type = item.Type, }; switch (dto.Type) { case DeviceTaskEnumType.Add: case DeviceTaskEnumType.Delete: case DeviceTaskEnumType.Update: { dto.Info = new UserToDeviceResponse { UserId = item.UserId ?? 0, UserNo = item.UserNo, OldUserNo = item.OldUserNo, UserCode = item.UserCode, UserType = item.UserType, UserName = item.UserName, DepartmentNo = item.DepartmentNo, DepartmentName = item.DepartmentName, CardNo = item.CardNo, Face = item.Type == DeviceTaskEnumType.Delete ? string.Empty : _faceUrlOptions.Url + token, //下发才附带照片信息 ValidDatePeriod = new DatePeriod { Start = DateOnly.FromDateTime(DateTime.Now.AddDays(-1)), End = DateOnly.FromDateTime(DateTime.Now.AddYears(20)), }, TimePeriods = new List { item.CardType }, Doors = new List { 1 }, Schedule = 1 }; } break; case DeviceTaskEnumType.TimePeriodAdd: case DeviceTaskEnumType.TimePeriodDelete: { dto.Info = item.TimePeriods.Select(t => new TimePeriodToDeviceResponse { Id = t.CardType, Name = t.CardName, Enable = item.EnableTimePeriod, Sections = t.Sections.Select(s => new TimePeriodWeekDaySection { DayOfWeek = s.DayOfWeek, TimePeriods = s.TimePeriods.Select(p => new DeviceTimePeriod { Enable = p.Enable, Start = p.Enable ? p.Start : new TimeOnly(00, 00, 00), End = p.Enable ? p.End : new TimeOnly(00, 00, 00) }).ToList() }).ToList() }); } break; case DeviceTaskEnumType.ScheduleAdd: case DeviceTaskEnumType.ScheduleDelete: { dto.Info = new ScheduleToDeviceResponse { Id = 1, Enable = true, Name = "计划的一部分", Schedules = new List { new() { Name = "国庆节", DateTimePeriod = new DateTimePeriod { Start = new DateTime(2024, 10, 1), End = new DateTime(2024, 10, 7) } } } }; } break; case DeviceTaskEnumType.UploadRecord: { var timeInfo = deviceUploadInfos[d]; dto.Info = new UploadRecordToDeviceResponse { DateTimePeriod = new DateTimePeriod { Start = timeInfo.Item1, End = timeInfo.Item2 } }; } break; } return dto; }).ToList() }; }).ToList(); } public async Task> ElevatorAcsDeviceGetTasksGetAsync(string deviceUniqueNo) { //梯控 var elevatorAcsFloors = await _dbContext.Queryable().Where(x => x.RealDeviceUniqueNo == deviceUniqueNo).ToListAsync(); if (elevatorAcsFloors.IsPresent()) { var virtualDeviceUniqueNos = elevatorAcsFloors.Select(x => x.VirtualDeviceUniqueNo).ToList(); var allUserNos = new List(); foreach (var virtualDeviceUniqueNo in virtualDeviceUniqueNos) { var userNos = await _dbContext.Queryable().Where(x => x.DeviceUniqueNo == virtualDeviceUniqueNo) .Where(x => (x.Status == DeviceTaskStatusEnumType.Wait || x.Status == DeviceTaskStatusEnumType.Process) && !string.IsNullOrEmpty(x.UserNo)) .OrderBy(x => new { x.Id, Priority = SqlFunc.Desc(x.Priority) }) .Select(x => x.UserNo) .ToListAsync(); allUserNos.AddRange(userNos); } allUserNos = allUserNos.Distinct().ToList(); if (allUserNos.IsEmpty()) { return new List(); } var userCodes = await _dbContext.Queryable().Where(x => allUserNos.Contains(x.No)).Select(x => x.Code).ToListAsync(); var leaveUserCodes = await _dbContext.Queryable().Where(x => allUserNos.Contains(x.No)).Select(x => x.Code).ToListAsync(); return userCodes.Concat(leaveUserCodes).Distinct().ToList(); } return new List(); } public async Task GetElevatorAcsTaskAsync(string deviceUniqueNo, string userCode) { var userInfo = await _dbContext.Queryable() .Where(x => x.Code == userCode) .Select(x => new { UserId = x.Id, UserNo = x.No, UserCode = x.Code, UserName = x.Name, x.CardNo }) .FirstAsync() ?? await _dbContext.Queryable() .Where(x => x.Code == userCode) .Select(x => new { UserId = x.Id, UserNo = x.No, UserCode = x.Code, UserName = x.Name, x.CardNo }) .FirstAsync(); var elevatorAcsFloors = await _dbContext.Queryable() .Where(x => x.RealDeviceUniqueNo == deviceUniqueNo) .Select(x => new { x.VirtualDeviceUniqueNo, x.AcsValue }) .ToListAsync(); var virtualDeviceUniqueNos = elevatorAcsFloors.Select(x => x.VirtualDeviceUniqueNo).ToList(); var floorAcsValues = elevatorAcsFloors.Select(x => x.AcsValue).Distinct().ToList(); var all = await CommonHelper.RetryAsync(async () => { var taskIds = await _dbContext.Queryable() .LeftJoin((deviceTask, device) => deviceTask.DeviceUniqueNo == device.UniqueNo) .Where((deviceTask, device) => deviceTask.UserNo == userInfo.UserNo) .Where((deviceTask, device) => virtualDeviceUniqueNos.Contains(deviceTask.DeviceUniqueNo)) .Where((deviceTask, device) => device.Enable) .Where(deviceTask => (deviceTask.Status == DeviceTaskStatusEnumType.Wait || deviceTask.Status == DeviceTaskStatusEnumType.Process) && !string.IsNullOrEmpty(deviceTask.UserNo)) .OrderBy(deviceTask => new { deviceTask.Id, Priority = SqlFunc.Desc(deviceTask.Priority) }) .Select((deviceTask, device) => deviceTask.Id) .ToListAsync(); var effect = await _dbContext.Updateable() .SetColumns(x => x.Status == DeviceTaskStatusEnumType.Process) .SetColumns(x => x.ModifiedTime == DateTime.Now) .Where(x => taskIds.Contains(x.Id)) .ExecuteCommandAsync(); if (effect != taskIds.Count) { throw new VersionExceptions("version exceptions"); } return taskIds; }, 3); if (!all.Any()) { return null; } var oldCardNo = await _dbContext.Queryable() .Where(x => x.UserNo == userInfo.UserCode) .Where(x => x.CardNo != userInfo.CardNo) .Where(x => SqlFunc.Subqueryable().Where(p => !string.IsNullOrEmpty(p.CardNo) && p.CardNo == x.CardNo).NotAny()) .OrderByDescending(x => x.Id) .Select(x => x.CardNo) .FirstAsync(); return new ElevatorAcsDeviceTaskToDeviceByDeviceResponse { DeviceUniqueNo = deviceUniqueNo, UserId = userInfo.UserId, UserCode = userInfo.UserCode, OldCardNo = oldCardNo, CardNo = userInfo.CardNo, UserName = userInfo.UserName, FloorAcsValues = floorAcsValues, TaskIds = all }; } public async Task UpdateDeviceTaskStatusAsync(List requests) { var taskIds = requests.Select(x => x.TaskId).ToList(); var deviceTasks = await _dbContext.Queryable() .Where(x => taskIds.Contains(x.Id)) .Where(x => x.Status == DeviceTaskStatusEnumType.Process) .ToListAsync(); var now = DateTime.Now; deviceTasks.ForEach(task => { var callbackMessage = requests.First(x => x.TaskId == task.Id); task.Status = callbackMessage.IsSuccess ? DeviceTaskStatusEnumType.Success : DeviceTaskStatusEnumType.Error; task.ResultMessage = callbackMessage.ResultMessage; task.ModifiedTime = now; }); await _dbContext.UpdateAsync(deviceTasks); } //**************************************************** help method ************************************************** private async Task> GetDataAsync(DeviceTaskGetRequest request) { var startId = await GetStartIdAsync(); var data = await CommonHelper.RetryAsync(async () => { var rows = await _dbContext.Queryable() .LeftJoin((deviceTask, device) => deviceTask.DeviceUniqueNo == device.UniqueNo) .LeftJoin((deviceTask, device, ga) => deviceTask.UserNo == ga.No) .LeftJoin((deviceTask, device, ga, gaLeave) => deviceTask.UserNo == gaLeave.No) .LeftJoin((deviceTask, device, ga, gaLeave, department) => department.No == ga.DepartmentNo) .LeftJoin((deviceTask, device, ga, gaLeave, department, deviceRegistry) => deviceRegistry.DeviceUniqueNo == deviceTask.DeviceUniqueNo) .Where((deviceTask, device) => deviceTask.Id >= startId && deviceTask.DeviceUniqueNo == request.DeviceUniqueNo ) .WhereIF(!request.IsProcess, deviceTask => deviceTask.Status == DeviceTaskStatusEnumType.Wait) .WhereIF(request.IsProcess, deviceTask => deviceTask.Status == DeviceTaskStatusEnumType.Process && SqlFunc.DateDiff(DateType.Minute, deviceTask.ModifiedTime, DateTime.Now) > 10 && deviceTask.Version < 10 ) .WhereIF(request.Types.IsPresent(), deviceTask => request.Types.Contains(deviceTask.Type)) .Where((deviceTask, device, ga, gaLeave, department, deviceRegistry) => device.HeartbeatInterval == 0 || SqlFunc.DateAdd(deviceRegistry.LastHeartbeatTime, device.HeartbeatInterval * 3, DateType.Second) > DateTime.Now ) .Where((deviceTask, device) => device.Enable) .OrderBy(deviceTask => new { deviceTask.Id, Priority = SqlFunc.Desc(deviceTask.Priority) }) .Select((deviceTask, device, ga, gaLeave, department) => new TaskDto { UserId = deviceTask.UserId == 0 || deviceTask.UserId == null ? ga.Id == 0 ? gaLeave.IdentityUserId : ga.Id : deviceTask.UserId, TaskId = deviceTask.Id, Type = deviceTask.Type, UserType = ga.Type, DeviceUniqueNo = deviceTask.DeviceUniqueNo, DeviceConnectionInfo = device.ConnectionInfo, FuncStr = device.Funcs.ToString(), UserNo = deviceTask.UserNo, UserCode = SqlFunc.IsNull(SqlFunc.IsNull(ga.Code, gaLeave.Code), deviceTask.UserNo), UserName = SqlFunc.IsNull(ga.Name, gaLeave.Name), DepartmentNo = ga.DepartmentNo, DepartmentName = department.Name, CardNo = ga.CardNo, Version = deviceTask.Version, Status = deviceTask.Status, EnableTimePeriod = device.EnableTimePeriod, CardType = device.EnableTimePeriod ? SqlFunc.IsNullOrEmpty(ga.CardType) ? 1 : SqlFunc.ToInt32(ga.CardType) : 1 }) .ToPageListAsync(request.PageIndex, request.PageSize); if (!rows.Any()) { return rows; } var taskIdVs = rows.Select(x => new { x.TaskId, x.Version, x.Status }).ToList(); var where = LinqExpressionExtensions.False(); taskIdVs.ForEach(p => { where = where.Or(x => x.Id == p.TaskId && x.Version == p.Version && x.Status == p.Status); }); var effect = await _dbContext.Updateable() .SetColumns(x => x.Version == x.Version + 1) .SetColumns(x => x.Status == DeviceTaskStatusEnumType.Process) .SetColumns(x => x.ModifiedTime == DateTime.Now) .Where(where) .ExecuteCommandAsync(); if (taskIdVs.Count != effect) { throw new VersionExceptions("version exceptions"); } return rows; }, 5); return data; } private async Task GetStartIdAsync() { const string cacheKey = "DeviceTaskStartId"; if (_memoryCache.TryGetValue(cacheKey, out var cachedStartId)) { return cachedStartId; } var limitTime = DateTime.Now.AddDays(-3); var limitData = await _dbContext.Queryable() .Where(x => x.ModifiedTime >= limitTime) .OrderBy(x => x.ModifiedTime) .Select(x => x.Id) .FirstAsync(); _memoryCache.Set(cacheKey, limitData, TimeSpan.FromMinutes(30)); return limitData; } /// /// 处理获取上传记录的任务 /// /// /// private async Task> GetDeviceUploadRecordInfosAsync(List data) { if (data.All(x => x.Type != DeviceTaskEnumType.UploadRecord)) { return new Dictionary(); } var deviceNos = data.Select(x => x.DeviceUniqueNo).Distinct().ToList(); var result = new Dictionary(deviceNos.Count); var manualSettings = await _dbContext.Queryable().Where(x => deviceNos.Contains(x.DeviceNo)) .Where(x => x.IsFinished == false) .OrderBy(x => x.CreateTime) .Select(x => new { x.Id, x.DeviceNo, x.Start, x.End }) .ToListAsync(); var logInfos = await _dbContext.Queryable() .GroupBy(x => x.DeviceUniqueNo) .Select(x => new { x.DeviceUniqueNo, RaiseDateTime = SqlFunc.AggregateMax(x.RaiseDateTime) }) .ToListAsync(); var commonEnd = DateTime.Now.AddDays(1); foreach (var item in deviceNos) { var setting = manualSettings.FirstOrDefault(x => x.DeviceNo == item); if (setting != null) { result.Add(item, (setting.Start, setting.End)); } else { var logInfo = logInfos.FirstOrDefault(x => x.DeviceUniqueNo == item); result.Add(item, logInfo == null ? (DateTime.Now.AddMinutes(-10), commonEnd) : (logInfo.RaiseDateTime, commonEnd)); } } //更新为已处理 await _dbContext.Updateable() .SetColumns(x => x.IsFinished == true) .Where(x => manualSettings.Select(p => p.Id).Contains(x.Id)) .ExecuteCommandAsync(); return result; } private async Task HandlerTimePeriodAsync(List data) { try { var tasks = data.Where(x => x.Type is DeviceTaskEnumType.TimePeriodAdd or DeviceTaskEnumType.TimePeriodDelete).ToList(); if (tasks.IsEmpty()) return; var deviceUniqueNos = tasks.Select(x => x.DeviceUniqueNo).ToList(); var devices = await _dbContext.Queryable().Where(x => deviceUniqueNos.Contains(x.UniqueNo)).ToListAsync(); var templateCodes = devices.Select(x => new { x.TenantId, x.TemplateCode }).DistinctBy(x => new { x.TenantId, x.TemplateCode }).ToList(); var deviceCodes = devices.Select(x => x.Code).Distinct().ToList(); var where = LinqExpressionExtensions.False(); where = where.Or(x => x.AssignType == DeviceTimePeriodAssignEnumType.Device && deviceCodes.Contains(x.Value)); foreach (var template in templateCodes) { where = where.Or(x => x.AssignType == DeviceTimePeriodAssignEnumType.Template && x.Value == template.TemplateCode && x.TenantId == template.TenantId); } var settings = await _dbContext.Queryable() .Where(where) .Select() .ToListAsync(); var basicTypes = await _dbContext.Queryable().Where(x => x.Type == "timeperiod").ToListAsync(); settings.ForEach(x => x.CardName = basicTypes.FirstOrDefault(p => p.Code == x.CardType.ToString())?.Display); foreach (var item in data) { var device = devices.FirstOrDefault(x => x.UniqueNo == item.DeviceUniqueNo); if (device == null) { continue; } var templateCode = device.TemplateCode; var code = device.Code; var tSettings = settings.Where(x => x.AssignType == DeviceTimePeriodAssignEnumType.Template && x.TenantId == device.TenantId) .Where(x => x.Value == templateCode) .ToList(); var dSettings = settings.Where(x => x.AssignType == DeviceTimePeriodAssignEnumType.Device).Where(x => x.Value == code).ToList(); foreach (var setting in dSettings) { var s = tSettings.FirstOrDefault(x => x.CardType == setting.CardType); if (s != null) { tSettings.Remove(s); } tSettings.Add(setting); } item.TimePeriods = tSettings; } } catch (Exception e) { _logger.LogError(e, "处理时段码失败:{msg}", e.Message); } } }