using LY.App.Common; using LY.App.Common.Redis; using LY.App.Common.WebSocket; using LY.App.Extensions.DI; using LY.App.Model; using Mapster; using NetTopologySuite.Geometries; using NetTopologySuite.IO; using SqlSugar; using System.ComponentModel.DataAnnotations; namespace LY.App.Service { /// /// 报警服务 /// [ServiceInjection(InjectionType.Transient)] public class AlarmService { private readonly SqlSugarClient _db; private readonly IConfiguration _config; private readonly RedisService _redisService; private readonly PushService _pushService; public AlarmService(SqlSugarClient db, IConfiguration config, RedisService redisService, PushService pushService) { _db = db; _config = config; _redisService = redisService; _pushService = pushService; } /// /// 新增报警信息 /// /// /// public async Task AddAlarm(RevData input) { await SetDeviceStataus(input); var key = RedisKeyList.DeviceInfo(input.product_ad_id); var deviceinfo = await _redisService.GetAsync(key); if (deviceinfo == null) { deviceinfo = await _db.CopyNew().Queryable().Where(s => s.DeviceSN == input.product_ad_id).FirstAsync(); if (deviceinfo == null) { return new ApiResult() { code = 1, msg = "设备不存在" }; } await _redisService.SetAsync(key, deviceinfo, TimeSpan.FromDays(1)); } var entity = input.data.Adapt>(); if (entity.Any()) { // 排除掉不在范围内的报警信息 var O4entity = entity.Where(s => s.device_type == "O4" || s.serial_number == "O4" || s.device_type == "UAV" || !string.IsNullOrEmpty(s.device_type)); if (O4entity.Any()) { foreach (var item in O4entity) { if (!entity.Exists(s => s.serial_number == item.serial_number)) { entity.Add(item); } } } if (entity.Any()) { foreach (var item in entity) { item.BatchId = await GetBatId(item.serial_number); item.DeviceId = deviceinfo.Id; item.DeviceName = deviceinfo.Name; item.positionId = deviceinfo.PositionId; item.PostionName = deviceinfo.PositionName; item.Time = input.time; item.distance = GisHelper.HaversineDistance(item.drone_lat, item.drone_lon, item.app_lat, item.app_lon); var temp = await Iswhitlist(item.serial_number, item.drone_lat, item.drone_lon); item.IsWhitelist = temp.Item1; item.WhiteListId = temp.Item2; item.alarmLevel = item.IsWhitelist == true ? 0 : await GetAlarmLevel(deviceinfo.PositionId, item.drone_lon, item.drone_lat); item.centerdistance = await GetCenterDistance(item.drone_lat, item.drone_lon, item.positionId); } await _db.CopyNew().Insertable(entity).SplitTable().ExecuteReturnSnowflakeIdListAsync(); //推送报警信息 await _pushService.SendMessageToAll(new { msgType = "event", data = entity }); } } return new ApiResult(); } /// /// 防区中心距离 /// /// /// /// /// private async Task GetCenterDistance(double lat, double lon, long positioonId) { try { var key = RedisKeyList.PositioinRegion(positioonId); var pontion = await _redisService.GetAsync(key); if (pontion == null) { pontion = await _db.CopyNew().Queryable() .Where(s => s.Id == positioonId) .FirstAsync(); await _redisService.SetAsync(key, pontion); } var distance = GisHelper.HaversineDistance(lat, lon, pontion.lat, pontion.lon); return distance; } catch (Exception ex) { Console.WriteLine(ex.Message); } return 0; } /// /// 判断 距离是否在范围内 /// /// /// /// /// /// private bool checkDistance(double lat, double lon, double d_lat, double d_lon) { if (lat == 0 || lon == 0) return false; var maxDistance = Convert.ToDouble(_config["MaxDistance"]); var distance = GisHelper.HaversineDistance(lat, lon, d_lat, d_lon); return distance < maxDistance; } /// /// 判断是否在白名单中 /// /// /// /// /// private async Task> Iswhitlist(string serial_number, double lat, double lon) { string key = RedisKeyList.white_list(serial_number); if (await _redisService.ExistsAsync(key)) { var entity = await _redisService.GetAsync(key); //判断时间是否在区在 if (entity.allDay) { return new Tuple(true, entity.Id); } else { var has = entity.startTime <= DateTime.Now && DateTime.Now <= entity.endTime; return new Tuple(has, has ? entity.Id : 0); } } return new Tuple(false, 0); ; } static bool IsPointInGeoJson(double latitude, double longitude, string geoJson) { // 使用 NetTopologySuite 解析 GeoJSON var reader = new GeoJsonReader(); Geometry geometry = reader.Read(geoJson); // 创建点 var point = new Point(longitude, latitude); // 判断点是否在几何图形内 return geometry.Contains(point); } /// /// 计算入侵级别 /// /// /// /// /// private async Task GetAlarmLevel(long positionId, double lon, double lat) { int result = 0; if (positionId > 0 && lon > 0 && lat > 0) { var key = RedisKeyList.PositioinRegion(positionId); var geodata = await _redisService.GetAsync(key); if (geodata == null) { geodata = await _db.CopyNew().Queryable() .Where(s => s.Id == positionId) .FirstAsync(); await _redisService.SetAsync(key, geodata); } WKTReader reader = new WKTReader(); Geometry point = reader.Read($"POINT ({lon} {lat})"); Geometry multipolygon = reader.Read(geodata.Region); if (multipolygon.Contains(point)) { return 1; } } return result; } /// /// 推送消息 /// /// /// public async Task PushMessage(object message) { //推送前端无人机数据 await _pushService.SendMessageToAll(message); } /// /// 设置设备在线状态,并更新数据 /// /// /// private async Task SetDeviceStataus(RevData input) { await _redisService.SetAsync(RedisKeyList.DeviceStatus(input.product_ad_id), true, TimeSpan.FromSeconds(15)); //更新 设备缓存 var key = RedisKeyList.DeviceInfo(input.product_ad_id); var deviceinfo = await _redisService.GetAsync(key); if (deviceinfo != null && deviceinfo.isMove) { deviceinfo.Lat = input.product_lat; deviceinfo.Lon = input.product_lon; await _redisService.SetAsync(key, deviceinfo); //更新位置 // await _db.Updateable(deviceinfo).ExecuteCommandAsync(); await _db.CopyNew().Updateable(deviceinfo).UpdateColumns(it => new { it.Lat, it.Lon }).ExecuteCommandAsync(); } } private async Task GetBatId(string droneId) { var timeSpan = Convert.ToDouble(_config["BatchId"]); var key = RedisKeyList.BatchIdBysn(droneId); //从redis取出batchid,如果没有,就新加一个,每次访问都重置一下过期时间来模拟滑动过期 var batchId = await _redisService.GetAsync(key); if (batchId == 0) { batchId = SnowFlakeSingle.Instance.NextId(); await _redisService.DeleteAsync(RedisKeyList.index_data()); } await _redisService.SetAsync(key, batchId, TimeSpan.FromSeconds(timeSpan)); return batchId; } /// /// //根据batchId获取报警信息 /// /// /// public async Task GetByBatchId(long batchId) { var items = await _db.Queryable().SplitTable() .Where(s => s.BatchId == batchId) .OrderBy(s => s.Id).Select(s => new { Lon = s.drone_lon, Lat = s.drone_lat, Alt = s.height, s.app_lat, s.app_lon, s.CreateTime, AlarmLevel = s.alarmLevel, }).ToListAsync(); return new ApiResult() { data = items }; } /// /// 首页统计 /// /// public async Task> IndexCount() { Dictionary result = new Dictionary(); // 获取当前日期和时间 DateTime currentDate = DateTime.Now; // 获取当天的开始时间 DateTime startDate = currentDate.Date; // 获取当天的结束时间(23:59:59) DateTime endDate = currentDate.Date.AddDays(1).AddTicks(-1); //计算当天 var todaywaring = await _db.Queryable().SplitTable() .Where(s => s.alarmLevel > 0 && s.IsWhitelist == false) .Where(s => s.CreateTime >= startDate && s.CreateTime <= endDate) .GroupBy(s => s.BatchId) .Select(s => s.BatchId).CountAsync(); //计算当天处理次数 //var todayhandle = _db.Queryable() // .Where(s => s.BatchId > 0) // .Where(s => s.CreateTime >= startDate && s.CreateTime <= endDate); //计算总数 var totalcount = _db.Queryable().SplitTable() .Where(s => s.alarmLevel > 0 && s.IsWhitelist == false) .GroupBy(s => s.BatchId) .Select(s => s.BatchId); //计算处理总数 //var totalhandle = _db.Queryable() // .Where(x => x.BatchId > 0); //组合查询结果 //return new //{ // todaywaring = await todaywaring.CountAsync(), // todayhandle =0, // totalcount = await totalcount.CountAsync(), // totalhandle = 0 //}; result.Add("todaywaring", todaywaring); result.Add("todayhandle", 0); result.Add("totalcount", await totalcount.CountAsync()); result.Add("totalhandle", 0); return result; } /// /// 分页 /// /// /// public async Task GetPage(AlarmReq input) { var result = await CreatePage(input); return new ApiResult() { code = 0, data = new { total = result.Item1, items = result.Item2 } }; } /// /// 热力图 /// /// public async Task hotmap() { var query = await _db.Queryable().SplitTable() .GroupBy(x => new { Lon = SqlFunc.Round(x.drone_lon, 4), Lat = SqlFunc.Round(x.drone_lat, 4) }) // 按经纬度分组 .Select(x => new { Lon = SqlFunc.Round(x.drone_lon, 4), Lat = SqlFunc.Round(x.drone_lat, 4), Count = SqlFunc.AggregateCount(x.BatchId), // 统计去重后的BatchId }) .ToListAsync(); return new ApiResult() { data = query }; } /// /// 列表 /// /// /// public async Task>> CreatePage(AlarmReq input) { RefAsync total = 0; var items = await _db.Queryable().SplitTable() .WhereIF(input.positionId.HasValue, st => st.positionId == input.positionId.Value) // .WhereIF(input.Frequency.HasValue, st => st.freq == input.Frequency.Value) .WhereIF(!string.IsNullOrEmpty(input.sn), s => s.serial_number.Contains(input.sn)) .WhereIF(!string.IsNullOrEmpty(input.model), st => st.device_type == input.model) .WhereIF(input.strartDate.HasValue, st => st.CreateTime >= input.strartDate.Value) .WhereIF(input.endDate.HasValue, st => st.CreateTime <= input.endDate.Value.AddDays(1)) .OrderBy(s => s.BatchId, OrderByType.Desc) .GroupBy(s => new { s.BatchId, s.serial_number, s.device_type }) .Select(st => new AlarmRepDto { batchId = st.BatchId.ToString(), startTime = SqlFunc.AggregateMin(st.CreateTime), endTime = SqlFunc.AggregateMax(st.CreateTime), sn = st.serial_number, Frequency = SqlFunc.AggregateMax(st.freq), duration = (SqlFunc.AggregateMax(st.CreateTime) - SqlFunc.AggregateMin(st.CreateTime)).TotalSeconds, model = st.device_type, positionId = SqlFunc.AggregateMax(st.positionId), }).MergeTable()//合并查询 .ToPageListAsync(input.pageNum, input.pageSize, total); var ids = items.Select(s => s.positionId).Distinct().ToList(); var positionNames = await _db.Queryable() .Where(s => ids.Contains(s.Id)) .Select(s => new { s.Id, s.Name }) .ToListAsync(); foreach (var item in items) { var positionName = positionNames.FirstOrDefault(s => s.Id == item.positionId)?.Name; if (!string.IsNullOrEmpty(positionName)) { item.positionName = positionName; } } return Tuple.Create(total.Value, items); } /// /// 快速分页 /// /// /// public async Task CreateHistoryPage(AlarmReq input) { var tables = _db.SplitHelper().GetTables(); string coutsql = @" SELECT COUNT(*) FROM ( SELECT batch_id FROM ( {0} ) AS unionTable GROUP BY batch_id ) AS CountTable;"; string page = @" SELECT * FROM ( SELECT batch_id FROM ( {0} ) AS unionTable GROUP BY batch_id ORDER BY batch_id desc LIMIT {1},{2} ) AS CountTable "; string tablesql = string.Join(" UNION ALL ", tables.Select(item => $"SELECT batch_id FROM {item.TableName} GROUP BY batch_id")); if (!string.IsNullOrEmpty(input.sn)) { tablesql = string.Join(" UNION ALL ", tables.Select(item => $"SELECT batch_id FROM {item.TableName} where serial_number like '%{input.sn}%' GROUP BY batch_id")); } var pageitem = await _db.Ado.SqlQueryAsync(string.Format(coutsql, tablesql) + string.Format(page, tablesql, (input.pageNum - 1) * input.pageSize, input.pageSize)); var temp = await _db.Queryable() .Where(s => pageitem.Item2.Contains(s.BatchId)).SplitTable().Select(s => new { s.BatchId, s.freq, s.Id, s.positionId, s.PostionName, s.alarmLevel, s.device_type }).ToListAsync(); var query = await _db.Queryable() .Where(s => pageitem.Item2.Contains(s.BatchId)).SplitTable() .GroupBy(s => new { s.BatchId, s.serial_number }) .Select(st => new AlarmRepDto { batchId = st.BatchId.ToString(), startTime = SqlFunc.AggregateMin(st.CreateTime), endTime = SqlFunc.AggregateMax(st.CreateTime), sn = st.serial_number, Frequency = 0, duration = (SqlFunc.AggregateMax(st.CreateTime) - SqlFunc.AggregateMin(st.CreateTime)).TotalSeconds, IsWhitelist = SqlFunc.AggregateMax(st.IsWhitelist), }).OrderByDescending(s => s.batchId).ToListAsync(); query.ForEach(s => { s.duration = s.duration == 0 ? 1 : s.duration; s.Frequency = temp.Where(m => m.BatchId == long.Parse(s.batchId)).OrderByDescending(o => o.Id).FirstOrDefault().freq; s.positionId = temp.Where(m => m.BatchId == long.Parse(s.batchId)).OrderByDescending(o => o.Id).FirstOrDefault().positionId; s.positionName = temp.Where(m => m.BatchId == long.Parse(s.batchId)).OrderByDescending(o => o.Id).FirstOrDefault().PostionName; s.alarmLevel = temp.Where(m => m.BatchId == long.Parse(s.batchId)).Any(o => o.alarmLevel == 1) ? 1 : 0; s.model = temp.Where(m => m.BatchId == long.Parse(s.batchId)).OrderByDescending(o => o.Id).FirstOrDefault().device_type; }); return new ApiResult() { code = 0, data = new { total = pageitem.Item1.First(), items = query } }; } /// /// 报表统计 /// /// public async Task GetReport(DateTime? start, DateTime? end) { start = start.HasValue ? start.Value : DateTime.Now.AddMonths(-1); end = end.HasValue ? end.Value.AddDays(1) : DateTime.Now.Date.AddDays(1); var query = await _db.Queryable().SplitTable() .Where(s => s.alarmLevel > 0) .WhereIF(start.HasValue, st => st.CreateTime >= start.Value) .WhereIF(end.HasValue, st => st.CreateTime <= end.Value.AddDays(1)) .GroupBy(s => new { s.BatchId, s.serial_number, s.device_type, s.positionId, s.PostionName, s.DeviceId, s.DeviceName }) .Select(st => new { batchId = st.BatchId, startTime = SqlFunc.AggregateMin(st.CreateTime), endTime = SqlFunc.AggregateMax(st.CreateTime), sn = st.serial_number, Frequency = SqlFunc.AggregateMax(st.freq), duration = (SqlFunc.AggregateMax(st.CreateTime) - SqlFunc.AggregateMin(st.CreateTime)).TotalSeconds, model = st.device_type, positionName = st.PostionName, deviceId = st.DeviceId, deviceName = st.DeviceName }).MergeTable()//合并查询 .ToListAsync(); return new ApiResult() { data = new { freq = query.GroupBy(s => new { freq = FreqConvert.CoverFreq(s.Frequency) }) .Select(b => new { b.Key.freq, count = b.Count() }), model = query.GroupBy(s => new { s.model }) .Select(b => new { b.Key.model, count = b.Count() }), position = query.GroupBy(s => new { s.positionName }) .Select(s => new { s.Key.positionName, count = s.Count() }), time = query.GroupBy(s => s.startTime.ToString("HH")) .Select(it => new { it.Key, count = it.Count() }), date = query.GroupBy(s => s.startTime.ToString("yyyy-MM-dd")) .Select(it => new { it.Key, count = it.Count() }), device = query.GroupBy(s => new { s.deviceName }) .Select(b => new { b.Key.deviceName, count = b.Count() }) } }; } /// /// 热力图,取经纬度4位数 /// /// /// private async Task GenerateHotMap(List batchIds) { return await _db.Queryable().SplitTable() .Where(s => s.drone_lat > 0 && s.drone_lon > 0 && batchIds.Contains(s.BatchId)) .Select(s => new { drone_lon = SqlFunc.Round(s.drone_lon, 4), drone_lat = SqlFunc.Round(s.drone_lat, 4), }) .GroupBy(s => new { s.drone_lat, s.drone_lon }) .Select(s => new { Key = new { Lon = SqlFunc.Round(s.drone_lat, 4), Lat = SqlFunc.Round(s.drone_lon, 4) }, val = SqlFunc.AggregateCount(s) }) .ToListAsync(); } } }