Files
jinko.yikatong/Src/Dlhis.ScheduledTask/TaskAssignment/Services/Implements/AssignmentService.cs
2025-07-05 13:15:49 +08:00

508 lines
23 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Dlhis.Entity.Basic.Entity;
using Dlhis.Entity.Device.Entities;
using Dlhis.Entity.Device.Enums;
using Dlhis.Entity.Device.Models;
using Dlhis.ScheduledTask.TaskAssignment.Request;
using Dlhis.ScheduledTask.TaskAssignment.Response;
using Kurisu.AspNetCore.Utils.Extensions;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.Options;
using SqlSugar;
using Newtonsoft.Json.Linq;
using Kurisu.AspNetCore.Utils;
using Dlhis.ScheduledTask.TaskAssignment.Dto;
using Kurisu.AspNetCore.CustomClass;
using Kurisu.AspNetCore.DataAccess.SqlSugar.Services;
namespace Dlhis.ScheduledTask.TaskAssignment.Services.Implements;
public class AssignmentService : IAssignmentService
{
private readonly IDbContext _dbContext;
private readonly IDataProtectionProvider _dataProtectionProvider;
private readonly ILogger<AssignmentService> _logger;
private readonly FaceUrlOptions _faceUrlOptions;
public AssignmentService(IDbContext dbContext,
IDataProtectionProvider dataProtectionProvider,
IOptions<FaceUrlOptions> options,
ILogger<AssignmentService> logger)
{
_dbContext = dbContext;
_dataProtectionProvider = dataProtectionProvider;
_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 HandlerOldDeviceGainfoAsync(data);
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 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;
});
if (await _dbContext.Queryable<OldDeviceGainfoEntity>().Where(x => x.Status != OldDeviceGainfoEnumType.Deleted).AnyAsync())
{
var dCodes = await _dbContext.Queryable<DeviceTaskEntity>()
.LeftJoin<GainfoEntity>((x, ga) => x.UserNo == ga.No)
.Where(x => taskIds.Contains(x.Id))
.Where(x => x.Status == DeviceTaskStatusEnumType.Process)
.Select((x, ga) => new
{
x.DeviceUniqueNo,
ga.Code,
ga.No
})
.ToListAsync();
var oldDeviceGainfoWhere = LinqExpressionExtensions.False<OldDeviceGainfoEntity>();
dCodes.ForEach(item => { oldDeviceGainfoWhere = oldDeviceGainfoWhere.Or(x => x.Code == item.Code && x.DeviceUniqueNo == item.DeviceUniqueNo); });
var oldData = await _dbContext.Queryable<OldDeviceGainfoEntity>().Where(x => x.Status == OldDeviceGainfoEnumType.Wait)
.Where(oldDeviceGainfoWhere)
.ToListAsync();
oldData.ForEach(d =>
{
var current = dCodes.FirstOrDefault(x => x.Code == d.Code);
if (current != null)
{
var task = deviceTasks.FirstOrDefault(x => x.UserNo == current.No && x.DeviceUniqueNo == current.DeviceUniqueNo);
if (task != null)
{
d.Status = task.Status == DeviceTaskStatusEnumType.Success
? OldDeviceGainfoEnumType.Deleted
: OldDeviceGainfoEnumType.Error;
d.Remark = task.ResultMessage;
}
}
});
await _dbContext.UpdateAsync(oldData);
}
await _dbContext.UpdateAsync(deviceTasks);
}
//**************************************************** help method **************************************************
private async Task<List<TaskDto>> GetDataAsync(DeviceTaskGetRequest request)
{
(long? startId, List<long> otherIds) = await GetStartIdAndOtherIdsAsync(request.DeviceUniqueNo);
if (!startId.HasValue && otherIds.IsEmpty())
{
return new List<TaskDto>();
}
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)
.WhereIF(startId.HasValue, (deviceTask, device) =>
(deviceTask.Id >= startId || otherIds.Contains(deviceTask.Id))
&& deviceTask.DeviceUniqueNo == request.DeviceUniqueNo
).WhereIF(!startId.HasValue, (deviceTask, device) =>
otherIds.Contains(deviceTask.Id)
&& 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? StartId, List<long> OtherIds)> GetStartIdAndOtherIdsAsync(string deviceUniqueNo)
{
var limitTime = DateTime.Now.AddDays(-3);
var types = new List<DeviceTaskEnumType>
{
DeviceTaskEnumType.UploadRecord,
DeviceTaskEnumType.SyncDateTime
};
//排除UploadRecord,SyncDateTime的任务,获取最近的任务ID
var limitData = await _dbContext.Queryable<DeviceTaskEntity>()
.Where(x => x.ModifiedTime >= limitTime)
.Where(x => x.DeviceUniqueNo == deviceUniqueNo)
.Where(x => !types.Contains(x.Type))
.OrderBy(x => x.Id)
.Select(x => new { x.Id })
.FirstAsync();
var startId = limitData?.Id;
//获取其他任务ID
var otherIds = await _dbContext.Queryable<DeviceTaskEntity>()
.Where(x => x.DeviceUniqueNo == deviceUniqueNo)
.Where(x => types.Contains(x.Type))
.Where(x => x.Status == DeviceTaskStatusEnumType.Wait)
.Select(x => x.Id)
.ToListAsync();
return (startId, otherIds);
}
/// <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;
}
/// <summary>
/// 处理老档案数据
/// </summary>
/// <param name="data"></param>
private async Task HandlerOldDeviceGainfoAsync(List<TaskDto> data)
{
if (await _dbContext.Queryable<OldDeviceGainfoEntity>().Where(x => x.Status != OldDeviceGainfoEnumType.Deleted).AnyAsync())
{
var oldDeviceGainfoWhere = LinqExpressionExtensions.False<OldDeviceGainfoEntity>();
data.ForEach(item => { oldDeviceGainfoWhere = oldDeviceGainfoWhere.Or(x => x.Code == item.UserCode && x.DeviceUniqueNo == item.DeviceUniqueNo); });
var oldData = await _dbContext.Queryable<OldDeviceGainfoEntity>()
.Where(x => x.Status == OldDeviceGainfoEnumType.Wait || x.Status == OldDeviceGainfoEnumType.Error)
.Where(oldDeviceGainfoWhere)
.ToListAsync();
data.ForEach(item =>
{
var o = oldData.FirstOrDefault(x => x.Code == item.UserCode && x.DeviceUniqueNo == item.DeviceUniqueNo);
if (o != null)
item.OldUserNo = o.No;
});
}
}
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);
}
}
}