using Dlhis.Basic.Device.Request; using Dlhis.Basic.Device.Response; using Dlhis.Entity.Basic.Entity; using Dlhis.Entity.Canteen.Entities; using Dlhis.Entity.Canteen.Enums; using Dlhis.Entity.Device.Entities; using Dlhis.Entity.Device.Enums; using Kurisu.AspNetCore.Scope; using Kurisu.AspNetCore.UnifyResultAndValidation; using Kurisu.AspNetCore.Utils.Extensions; using Kurisu.Core.DataAccess.Dto; using Kurisu.SqlSugar.Extensions; using Kurisu.SqlSugar.Services; using Mapster; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace Dlhis.Basic.Device.Services.Implements; public class DeviceService : IDeviceService { private readonly IDbContext _dbContext; public DeviceService(IDbContext dbContext) { _dbContext = dbContext; } public async Task> GetPageAsync(DevicePageRequest request) { var page = await _dbContext.Queryable() .LeftJoin((d, dt) => d.TemplateCode == dt.Code) .WhereIF(request.Type.HasValue, (d, dt) => d.Type == request.Type) .Select((d, dt) => new DevicePageResponse { FuncsStr = d.Funcs.ToString(), Brand = dt.Brand, Type = d.Type, }, true) .ToPageListAsync(request); var now = DateTime.Now; var deviceUniqueNos = page.Data.Select(x => x.UniqueNo).ToList(); var balanceRecords = await _dbContext.Queryable().Where(x => x.Type == BalanceRecordTypeEnumType.Out) .Where(x => deviceUniqueNos.Contains(x.DeviceInfo)) .ToListAsync(); var deviceRegistries = await _dbContext.Queryable() .LeftJoin((x, d) => x.DeviceUniqueNo == d.UniqueNo) .Where(x => deviceUniqueNos.Contains(x.DeviceUniqueNo)) .Select((x, d) => new { d.HeartbeatInterval, x.LastHeartbeatTime, x.DeviceUniqueNo, }) .ToListAsync(); page.Data.ForEach(device => { device.Amount = balanceRecords.Sum(x => x.Amount); device.UserCount = balanceRecords.DistinctBy(x => x.UserCode).Count(); var r = deviceRegistries.FirstOrDefault(x => x.DeviceUniqueNo == device.UniqueNo); if (r == null) { device.IsOnline = false; } else { if (r.HeartbeatInterval <= 0) { device.IsOnline = true; } else { device.IsOnline = r.LastHeartbeatTime.AddSeconds(r.HeartbeatInterval * 3) >= now; } } }); return page; } public async Task SaveAsync(DeviceSaveRequest request) { await CheckUniqueNoExistsAsync(request); var template = await CheckTemplateExistsAsync(request.TemplateCode); await CheckTemplateFieldAsync(request.TemplateCode, request.ConnectionInfo); if (!request.Id.HasValue) { var entity = request.Adapt(); entity.Code = Guid.NewGuid(); await _dbContext.InsertAsync(entity); //同时保存注册信息 var registry = await _dbContext.Queryable().Where(x => x.DeviceUniqueNo == request.UniqueNo).FirstAsync(); if (registry == null) { registry = new DeviceRegistryEntity() { DeviceUniqueNo = request.UniqueNo, LastHeartbeatTime = DateTime.Now, RegisterTime = DateTime.Now, BusinessPlaceCode = null, BusinessPlaceCustomCode = null, }; await _dbContext.InsertAsync(registry); } } else { var entity = await _dbContext.Queryable().Where(x => x.Id == request.Id).FirstAsync(); entity.ThrowIfNull("设备不存在,无法更新"); //分类发生变化,检查是否存在分组 if (request.Type != entity.Type) { var existsG = await _dbContext.Queryable() .LeftJoin((dgr, dg) => dgr.GroupCode == dg.Code) .Where((dgr, dg) => dgr.DeviceCode == entity.Code) .Where((dgr, dg) => dg.Type != null) .AnyAsync(); existsG.ThrowIfTrue("存在设备分组,不可修改设备分类"); } var uniqueNo = entity.UniqueNo; request.Adapt(entity).UniqueNo = uniqueNo; await _dbContext.UpdateAsync(entity); } } //******************************************************** help methods ********************************************************** /// /// 检查设备模板是否存在 /// /// /// private async Task CheckTemplateExistsAsync(Guid templateCode) { var entity = await _dbContext.Queryable().Where(x => x.Code == templateCode).FirstAsync(); entity.ThrowIfNull("设备模板不存在"); return entity; } /// /// 检查设备连接参数是否与模板设定一致 /// /// /// /// private async Task CheckTemplateFieldAsync(Guid templateCode, string jsonContent) { var template = await _dbContext.Queryable().Where(x => x.Code == templateCode).FirstAsync(); template.ThrowIfNull("设备模板不存在"); try { var templateJobj = JObject.Parse(template.Template); var jobj = JObject.Parse(jsonContent); (templateJobj.Count == jobj.Count).ThrowIfFalse("连接参数字段数不一致"); foreach (var item in templateJobj) { jobj.ContainsKey(item.Key).ThrowIfFalse($"设备连接参数需要和模板一致.缺失{item.Key}"); } } catch (Exception ex) when (ex is JsonReaderException) { throw new UserFriendlyException("连接信息参数格式不正确"); } catch (Exception) { throw; } } /// /// 检查唯一号是否存在 /// /// /// private async Task CheckUniqueNoExistsAsync(DeviceSaveRequest request) { var exists = await _dbContext.Queryable().Where(x => x.UniqueNo == request.UniqueNo) .WhereIF(request.Id.HasValue, x => x.Id != request.Id) .AnyAsync(); exists.ThrowIfTrue("唯一号已经存在"); } public async Task CheckExistsAsync(string deviceUniqueNo) { return await _dbContext.Queryable() .Where(x => x.UniqueNo == deviceUniqueNo && x.Enable) .AnyAsync(); } /// /// 获取设备 /// /// /// public async Task GetDeviceByNoAsync(DeviceByNoRequest request) { var device = await _dbContext.Queryable() .Where(x => x.UniqueNo == request.UniqueNo) .Select(device => new DeviceResponse { FuncsStr = device.Funcs.ToString(), ConnectionInfoStr = device.ConnectionInfo }, true) .SingleAsync(); return device; } public async Task> GetAttendanceDevicesAsync() { var devices = await _dbContext.Queryable() .Where(x => x.Enable) .Select(x => new DeviceResponse { FuncsStr = x.Funcs.ToString() }, true) .ToListAsync(); return devices.Where(x => x.Type == DeviceTypeEnumType.Attendance).ToList(); } public async Task GetDeviceNumberAsync() { var devices = await _dbContext.Queryable() .Where(x => x.Type == DeviceTypeEnumType.Attendance && x.Enable) .ToListAsync(); var offlineNumber = await GetOfflineDevicesCount(devices); var res = new DeviceNumberResponse { OfflineNumber = offlineNumber.Count, TotalNumber = devices.Count, OnlineNumber = devices.Count- offlineNumber.Count }; return res; } /// /// 获取离线设备 /// /// /// private async Task> GetOfflineDevicesCount(List devices) { var nos = devices.Select(x => x.UniqueNo).ToList(); var deviceRegistrys = await _dbContext.Queryable() .Where(x => nos.Contains(x.DeviceUniqueNo)) .ToListAsync(); var list = new List(); foreach (var device in devices) { var regis = deviceRegistrys.FirstOrDefault(x => x.DeviceUniqueNo == device.UniqueNo); if (regis == null) { continue; } TimeSpan timeDifference = DateTime.Now - regis.LastHeartbeatTime; if (timeDifference.TotalSeconds > (device.HeartbeatInterval * 3)) { list.Add(device); } } return list; } public async Task> GetAttendanceTotalAsync(AttendanceTotalRequest request) { var page = await _dbContext.Queryable() .LeftJoin((d, dt) => d.TemplateCode == dt.Code) .Where(d => d.Type == DeviceTypeEnumType.Attendance && d.Enable) .Select((d, dt) => new DevicePageResponse { FuncsStr = d.Funcs.ToString(), Brand = dt.Brand, Type = d.Type, }, true) .ToPageListAsync(request); return page; } public async Task> GetAttendanceOfflineAsync(DeviceOnlineAndofflineRequest request) { var devices = await _dbContext.Queryable() .Where(x => x.Type == DeviceTypeEnumType.Attendance && x.Enable) .ToListAsync(); //离线 var offDevices =await GetOfflineDevicesCount(devices); var offids = offDevices.Select(x => x.Id).ToList(); if (request.IsOnAndOff) { //离线 var offpage = await _dbContext.Queryable() .LeftJoin((d, dt) => d.TemplateCode == dt.Code) .Where(d => d.Type == DeviceTypeEnumType.Attendance && d.Enable) .Where(d => offids.Contains(d.Id)) .Select((d, dt) => new DevicePageResponse { FuncsStr = d.Funcs.ToString(), Brand = dt.Brand, Type = d.Type, }, true) .ToPageListAsync(request); return offpage; } //在线 var ids = devices.Select(x => x.Id).ToList(); List onids = ids.Except(offids).ToList(); var page = await _dbContext.Queryable() .LeftJoin((d, dt) => d.TemplateCode == dt.Code) .Where(d => d.Type == DeviceTypeEnumType.Attendance && d.Enable) .Where(d => onids.Contains(d.Id)) .Select((d, dt) => new DevicePageResponse { FuncsStr = d.Funcs.ToString(), Brand = dt.Brand, Type = d.Type, }, true) .ToPageListAsync(request); return page; } public async Task> GetRecentNumbersAsync() { //获取近30天并转化为"yyyy-MM-dd"集合 DateTime currentDate = DateTime.Now; List times = new List(); for (int i = 0; i < 30; i++) { DateTime date = currentDate.AddDays(-i); string dateString = date.ToString("yyyy-MM-dd"); times.Add(dateString); } var devices = await _dbContext.Queryable().ToListAsync(); var list = new List(); times.ForEach(time => { var count = devices.Where(x => x.CreateTime.ToString("yyyy-MM-dd") == time).Count(); var res = new RecentNumberResponse { Date = time, Num = count, }; list.Add(res); }); return list; } public async Task> GetLogsAsync(ApiLogRequest request) { var level = request.Type.ToString(); return await Scoped.Request.Value.CreateAsync(async (sp) => { var configuration = sp.GetService(); var logConnectionString = configuration.GetSection("SqlSugarOptions:LogConnectionString").Value; //todo 切换连接字符串 var dbContext = sp.GetService(); dbContext.Client.CurrentConnectionConfig.ConnectionString = logConnectionString; var logs = await dbContext.Queryable() .WhereIF(!request.message.IsEmpty(), x => x.Message.Contains(request.message)) .WhereIF(!level.IsEmpty(), x => x.Level == level) .Select() .ToPageListAsync(request); return logs; }); } }