Files
jinko.yikatong/Src/Dlhis.ScheduledTask/TaskAssignment/Services/Implements/AssignmentService.cs
2026-05-22 11:26:05 +08:00

553 lines
25 KiB
C#

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<AssignmentService> _logger;
private readonly FaceUrlOptions _faceUrlOptions;
public AssignmentService(IDbContext dbContext,
IDataProtectionProvider dataProtectionProvider,
IOptions<FaceUrlOptions> options,
IMemoryCache memoryCache,
IGainfoPermissionApi gainfoPermissionApi,
ILogger<AssignmentService> logger)
{
_dbContext = dbContext;
_dataProtectionProvider = dataProtectionProvider;
_memoryCache = memoryCache;
_gainfoPermissionApi = gainfoPermissionApi;
_logger = logger;
_faceUrlOptions = options.Value;
}
public async Task<List<DeviceTaskToDeviceByDeviceResponse>> DeviceGetTasksAsync(DeviceTaskGetRequest request)
{
var data = await GetDataAsync(request);
if (data.IsEmpty()) return new List<DeviceTaskToDeviceByDeviceResponse>();
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<int> { item.CardType },
Doors = new List<int> { 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<ScheduleDto>
{
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<List<string>> ElevatorAcsDeviceGetTasksGetAsync(string deviceUniqueNo)
{
//梯控
var elevatorAcsFloors = await _dbContext.Queryable<ElevatorAcsFloorEntity>().Where(x => x.RealDeviceUniqueNo == deviceUniqueNo).ToListAsync();
if (elevatorAcsFloors.IsPresent())
{
var virtualDeviceUniqueNos = elevatorAcsFloors.Select(x => x.VirtualDeviceUniqueNo).ToList();
var allUserNos = new List<string>();
foreach (var virtualDeviceUniqueNo in virtualDeviceUniqueNos)
{
var userNos = await _dbContext.Queryable<DeviceTaskEntity>().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<string>();
}
var userCodes = await _dbContext.Queryable<GainfoEntity>().Where(x => allUserNos.Contains(x.No)).Select(x => x.Code).ToListAsync();
var leaveUserCodes = await _dbContext.Queryable<GainfoLeaveEntity>().Where(x => allUserNos.Contains(x.No)).Select(x => x.Code).ToListAsync();
return userCodes.Concat(leaveUserCodes).Distinct().ToList();
}
return new List<string>();
}
public async Task<ElevatorAcsDeviceTaskToDeviceByDeviceResponse> GetElevatorAcsTaskAsync(string deviceUniqueNo, string userCode)
{
var userInfo = await _dbContext.Queryable<GainfoEntity>()
.Where(x => x.Code == userCode)
.Select(x => new { UserId = x.Id, UserNo = x.No, UserCode = x.Code, UserName = x.Name, x.CardNo })
.FirstAsync();
var isCurrent = userInfo != null;
userInfo ??= await _dbContext.Queryable<GainfoLeaveEntity>()
.Where(x => x.Code == userCode)
.Select(x => new { UserId = x.Id, UserNo = x.No, UserCode = x.Code, UserName = x.Name, x.CardNo })
.FirstAsync();
userInfo.ThrowIfNull("用户信息不存在");
//获取用户的设备权限码列表,可以精确到楼层权限(虚拟电梯层级设备)
var deviceCodes = isCurrent ? await _gainfoPermissionApi.GetPermissionsDeviceCodesAsync(userCode) : new List<Guid>();
//通过真实设备号获取对应的虚拟设备号和权限值
var elevatorAcsFloors = await _dbContext.Queryable<ElevatorAcsFloorEntity>()
.LeftJoin<DeviceEntity>((x, d) => x.VirtualDeviceUniqueNo == d.UniqueNo)
.Where((x, d) => x.RealDeviceUniqueNo == deviceUniqueNo)
.WhereIF(isCurrent, (x, d) => deviceCodes.Contains(d.Code))
.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<DeviceTaskEntity>()
.LeftJoin<DeviceEntity>((deviceTask, device) => deviceTask.DeviceUniqueNo == device.UniqueNo)
.Where((deviceTask, device) => deviceTask.UserNo == userInfo.UserNo)
.Where((deviceTask, device) => virtualDeviceUniqueNos.Contains(deviceTask.DeviceUniqueNo) || deviceTask.Type == DeviceTaskEnumType.Delete)
.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<DeviceTaskEntity>()
.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<GainfoCardRecordEntity>()
.Where(x => x.UserNo == userInfo.UserCode)
.Where(x => x.CardNo != userInfo.CardNo)
.Where(x => SqlFunc.Subqueryable<GainfoEntity>().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<UpdateDeviceTaskStatusRequest> requests)
{
var taskIds = requests.Select(x => x.TaskId).ToList();
var deviceTasks = await _dbContext.Queryable<DeviceTaskEntity>()
.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<List<TaskDto>> GetDataAsync(DeviceTaskGetRequest request)
{
var startId = await GetStartIdAsync();
var data = await CommonHelper.RetryAsync(async () =>
{
var rows = await _dbContext.Queryable<DeviceTaskEntity>()
.LeftJoin<DeviceEntity>((deviceTask, device) => deviceTask.DeviceUniqueNo == device.UniqueNo)
.LeftJoin<GainfoEntity>((deviceTask, device, ga) => deviceTask.UserNo == ga.No)
.LeftJoin<GainfoLeaveEntity>((deviceTask, device, ga, gaLeave) => deviceTask.UserNo == gaLeave.No)
.LeftJoin<DepartmentEntity>((deviceTask, device, ga, gaLeave, department) => department.No == ga.DepartmentNo)
.LeftJoin<DeviceRegistryEntity>((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<DeviceTaskEntity>();
taskIdVs.ForEach(p => { where = where.Or(x => x.Id == p.TaskId && x.Version == p.Version && x.Status == p.Status); });
var effect = await _dbContext.Updateable<DeviceTaskEntity>()
.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<long> GetStartIdAsync()
{
const string cacheKey = "DeviceTaskStartId";
if (_memoryCache.TryGetValue<long>(cacheKey, out var cachedStartId))
{
return cachedStartId;
}
var limitTime = DateTime.Now.AddDays(-3);
var limitData = await _dbContext.Queryable<DeviceTaskEntity>()
.Where(x => x.ModifiedTime >= limitTime)
.OrderBy(x => x.ModifiedTime)
.Select(x => x.Id)
.FirstAsync();
_memoryCache.Set(cacheKey, limitData, TimeSpan.FromMinutes(30));
return limitData;
}
/// <summary>
/// 处理获取上传记录的任务
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private async Task<Dictionary<string, (DateTime, DateTime)>> GetDeviceUploadRecordInfosAsync(List<TaskDto> data)
{
if (data.All(x => x.Type != DeviceTaskEnumType.UploadRecord))
{
return new Dictionary<string, (DateTime, DateTime)>();
}
var deviceNos = data.Select(x => x.DeviceUniqueNo).Distinct().ToList();
var result = new Dictionary<string, (DateTime, DateTime)>(deviceNos.Count);
var manualSettings = await _dbContext.Queryable<DeviceUploadLogManuallyEntity>().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<DeviceUploadLogEntity>()
.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<DeviceUploadLogManuallyEntity>()
.SetColumns(x => x.IsFinished == true)
.Where(x => manualSettings.Select(p => p.Id).Contains(x.Id))
.ExecuteCommandAsync();
return result;
}
private async Task HandlerTimePeriodAsync(List<TaskDto> 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<DeviceEntity>().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<DeviceTimePeriodEntity>();
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<DeviceTimePeriodEntity>()
.Where(where)
.Select<DeviceTimePeriodDto>()
.ToListAsync();
var basicTypes = await _dbContext.Queryable<BasicTypeEntity>().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);
}
}
}