using NCC.Common.Core.Manager; using NCC.Common.Enum; using NCC.Common.Extension; using NCC.Common.Filter; using NCC.Dependency; using NCC.DynamicApiController; using NCC.FriendlyException; using NCC.Extend.Interfaces.UavDevice; using Mapster; using Microsoft.AspNetCore.Mvc; using SqlSugar; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using NCC.Extend.Entitys; using NCC.Extend.Entitys.Dto.UavDevice; using Yitter.IdGenerator; using NCC.Common.Helper; using NCC.JsonSerialization; using NCC.Extend.Entitys.Dto.UavDeviceCell; using NCC.Extend.Entitys.Enums; using System.ComponentModel; using System.Reflection; using NCC.Code; using Microsoft.AspNetCore.Authorization; using NCC.Extend.Interfaces.MqttPublisher; using Serilog; using NCC.System.Entitys.Permission; namespace NCC.Extend.UavDevice { /// /// 无人机机柜管理服务 /// [ApiDescriptionSettings(Tag = "无人机机柜管理服务", Name = "UavDevice", Order = 200)] [Route("api/Extend/[controller]")] public class UavDeviceService : IUavDeviceService, IDynamicApiController, ITransient { /// /// MQTT配置信息类 /// private class MqttConfig { public string ServerUri { get; set; } public int Port { get; set; } public string Username { get; set; } public string Password { get; set; } public string Qos { get; set; } } /// /// 设备配置信息类 /// private class DeviceConfig { public MqttConfig Mqtt { get; set; } public string MaxRetryCount { get; set; } public string DevicePortPath { get; set; } public string RfidPortPath { get; set; } public int DeleteVideo { get; set; } } private readonly ISqlSugarRepository _uavDeviceRepository; private readonly SqlSugarScope _db; private readonly IUserManager _userManager; private readonly IMqttPublisherService _mqttService; /// /// 初始化一个类型的新实例 /// public UavDeviceService( ISqlSugarRepository uavDeviceRepository, IUserManager userManager, IMqttPublisherService mqttService) { _uavDeviceRepository = uavDeviceRepository; _db = _uavDeviceRepository.Context; _userManager = userManager; _mqttService = mqttService; } #region 获取无人机机柜管理信息 /// /// 获取无人机机柜管理 /// /// 参数 /// [HttpGet("{id}")] public async Task GetInfo(string id) { var entity = await _db.Queryable().FirstAsync(p => p.Id == id); var output = entity.Adapt(); output.uavDeviceCellEntities = _db.Queryable().Where(p => p.DeviceId == output.id) .Select(it => new UavDeviceCellListOutput { id = it.Id, deviceId = it.DeviceId, cellCode = it.CellCode, status = it.Status, rfid1 = it.Rfid1, rfid2 = it.Rfid2, uavCode = it.UavCode, }).ToList(); output.isOnline = await _mqttService.IsClientOnlineAsync(output.deviceCode); return output; } #endregion #region 获取无人机机柜管理列表 /// /// 获取无人机机柜管理列表 /// /// 请求参数 /// [HttpGet("")] public async Task GetList([FromQuery] UavDeviceListQueryInput input) { var sidx = input.sidx == null ? "id" : input.sidx; var data = await _db.Queryable() .WhereIF(!string.IsNullOrEmpty(input.deviceName), p => p.DeviceName.Contains(input.deviceName)) .WhereIF(!string.IsNullOrEmpty(input.deviceCode), p => p.DeviceCode.Contains(input.deviceCode)) .WhereIF(!string.IsNullOrEmpty(input.belongUserId), p => p.BelongUserId.Contains(input.belongUserId)) .Select(it => new UavDeviceListOutput { id = it.Id, deviceName = it.DeviceName, deviceCode = it.DeviceCode, siteId = it.SiteId, belongUserId = it.BelongUserId, lng = it.Lng, lat = it.Lat, status = it.Status, remark = it.Remark, appVersion = it.AppVersion, cellnumber = SqlFunc.Subqueryable().Where(u => u.DeviceId == it.Id).Count(), belongUserName = SqlFunc.Subqueryable().Where(u => u.Id == it.BelongUserId).Select(u => u.RealName), }).MergeTable().Mapper(async res => { res.uavDeviceCells = _db.Queryable().Where(p => p.DeviceId == res.id).Select(it => new UavDeviceCellListOutput { id = it.Id, cellCode = it.CellCode, status = it.Status, rfid1 = it.Rfid1, rfid2 = it.Rfid2, uavCode = it.UavCode, }).ToList(); res.isOnline = await _mqttService.IsClientOnlineAsync(res.deviceCode); }).OrderBy(sidx + " " + input.sort).ToPagedListAsync(input.currentPage, input.pageSize); return PageResult.SqlSugarPageResult(data); } #endregion #region 获取代理商获取自己的无人机机柜管理列表 /// /// 获取代理商获取自己的无人机机柜管理列表 /// /// 请求参数 /// [HttpGet("GetListByCurrent")] public async Task GetListByCurrent([FromQuery] UavDeviceListQueryInput input) { var sidx = input.sidx == null ? "id" : input.sidx; var data = await _db.Queryable() .WhereIF(!string.IsNullOrEmpty(input.deviceName), p => p.DeviceName.Contains(input.deviceName)) .WhereIF(!string.IsNullOrEmpty(input.deviceCode), p => p.DeviceCode.Contains(input.deviceCode)) .WhereIF(!string.IsNullOrEmpty(input.siteId), p => p.SiteId == input.siteId) .Where(p => p.BelongUserId == _userManager.UserId) .Select(it => new UavDeviceListOutput { id = it.Id, deviceName = it.DeviceName, deviceCode = it.DeviceCode, siteId = it.SiteId, belongUserId = it.BelongUserId, lng = it.Lng, lat = it.Lat, status = it.Status, remark = it.Remark, siteName = SqlFunc.Subqueryable().Where(u => u.Id == it.SiteId).Select(u => u.SiteName), cellnumber = SqlFunc.Subqueryable().Where(u => u.DeviceId == it.Id).Count(), }).MergeTable().Mapper(async it => { it.isOnline = await _mqttService.IsClientOnlineAsync(it.deviceCode); }).OrderBy(sidx + " " + input.sort).ToPagedListAsync(input.currentPage, input.pageSize); return PageResult.SqlSugarPageResult(data); } #endregion #region 获取根据代理商id获取的无人机机柜管理列表 /// /// 获取根据代理商id获取的无人机机柜管理列表 /// /// 请求参数 /// [HttpGet("GetListByUserId")] public async Task GetListByUserId([FromQuery] UavDeviceListQueryInput input) { var sidx = input.sidx == null ? "id" : input.sidx; var data = await _db.Queryable() .WhereIF(!string.IsNullOrEmpty(input.deviceName), p => p.DeviceName.Contains(input.deviceName)) .WhereIF(!string.IsNullOrEmpty(input.deviceCode), p => p.DeviceCode.Contains(input.deviceCode)) .WhereIF(!string.IsNullOrEmpty(input.siteId), p => p.SiteId == input.siteId) .Where(p => p.BelongUserId == input.belongUserId) .Select(it => new UavDeviceListOutput { id = it.Id, deviceName = it.DeviceName, deviceCode = it.DeviceCode, siteId = it.SiteId, belongUserId = it.BelongUserId, lng = it.Lng, lat = it.Lat, status = it.Status, remark = it.Remark, siteName = SqlFunc.Subqueryable().Where(u => u.Id == it.SiteId).Select(u => u.SiteName), cellnumber = SqlFunc.Subqueryable().Where(u => u.DeviceId == it.Id).Count(), }).MergeTable().Mapper(async res => { res.isOnline = await _mqttService.IsClientOnlineAsync(res.deviceCode); }).OrderBy(sidx + " " + input.sort).ToPagedListAsync(input.currentPage, input.pageSize); return PageResult.SqlSugarPageResult(data); } #endregion #region 新建无人机机柜管理 /// /// 新建无人机机柜管理 /// /// 参数 /// [HttpPost("")] public async Task Create([FromBody] UavDeviceCrInput input) { var entity = input.Adapt(); entity.Id = YitIdHelper.NextId().ToString(); entity.BelongUserId = _userManager.UserId; if (input.deviceCode.IsNullOrEmpty()) { entity.DeviceCode = "SA" + YitIdHelper.NextId().ToString(); } var isOk = await _db.Insertable(entity).IgnoreColumns(ignoreNullColumn: true).ExecuteCommandAsync(); //新建完毕后,添加格位的信息,也就是货道信息 List cellEntities = new List(); //初始化的时候就提供6个货道 for (int i = 1; i <= 6; i++) { UavDeviceCellEntity uavDeviceCellEntitycellEntity = new UavDeviceCellEntity { Id = YitIdHelper.NextId().ToString(), DeviceId = entity.Id, CellCode = i.ToString(), Status = DeviceCellStatusEnum.空闲.GetHashCode(), CreateTime = DateTime.Now, }; cellEntities.Add(uavDeviceCellEntitycellEntity); } isOk = await _db.Insertable(cellEntities).ExecuteCommandAsync(); if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1000); } #endregion #region 更新无人机机柜管理 /// /// 更新无人机机柜管理 /// /// 主键 /// 参数 /// [HttpPut("{id}")] public async Task Update(string id, [FromBody] UavDeviceUpInput input) { var entity = input.Adapt(); var isOk = await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync(); if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1001); } #endregion #region 小程序端更新无人机机柜信息 /// /// 更新无人机机柜管理 /// /// 主键 /// 参数 /// [HttpPut("UpdateByWechat")] public async Task UpdateByWechat(string id, [FromBody] UavDeviceUpInput input) { var entity = input.Adapt(); var isOk = await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync(); //更新完基本信息后,需要去更新机柜格子信息 foreach (var item in input.uavDeviceCellEntities) { var cellEntity = await _db.Queryable().FirstAsync(p => p.Id == item.id); //需要判断是否有此机柜格子 if (cellEntity != null) { cellEntity.DeviceId = input.id; cellEntity.CellCode = item.cellCode; cellEntity.Status = item.status; cellEntity.Remark = item.remark; await _db.Updateable(cellEntity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync(); } else { //如果没有这个机柜格子,则创建 UavDeviceCellEntity uavDeviceCellEntity = new UavDeviceCellEntity { DeviceId = input.id, CellCode = item.cellCode, Status = item.status, Remark = item.remark, Id = YitIdHelper.NextId().ToString(), CreateTime = DateTime.Now, UpdateTime = DateTime.Now, CreatorId = _userManager.UserId }; await _db.Insertable(uavDeviceCellEntity).ExecuteCommandAsync(); } } if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1001); } #endregion #region 获取设备状态枚举列表 /// /// 获取设备状态枚举列表 /// [HttpGet("GetDeviceStatusEnum")] public async Task GetDeviceStatusEnum() { var list = Enum.GetValues(typeof(DeviceStatusEnum)) .Cast() .Select(e => new { Value = (int)e, Label = GetDescription(e) }).ToList(); return list; } private string GetDescription(Enum enumValue) { var fieldInfo = enumValue.GetType().GetField(enumValue.ToString()); var descriptionAttribute = fieldInfo?.GetCustomAttribute(); return descriptionAttribute?.Description ?? enumValue.ToString(); } #endregion #region 删除无人机机柜管理 /// /// 删除无人机机柜管理 /// /// [HttpDelete("{id}")] public async Task Delete(string id) { var entity = await _db.Queryable().FirstAsync(p => p.Id == id); _ = entity ?? throw NCCException.Oh(ErrorCode.COM1005); var isOk = await _db.Deleteable().Where(d => d.Id == id).ExecuteCommandAsync(); if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1002); } #endregion #region 解绑设备 /// /// 解绑设备 /// /// /// [HttpPut("UnbindDevice/{id}")] public async Task UnbindDevice(string id) { var entity = await _db.Queryable().FirstAsync(p => p.Id == id); entity.BelongUserId = ""; var isOk = await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync(); if (!(isOk > 0)) throw NCCException.Oh(ErrorCode.COM1001); } #endregion #region 绑定设备 /// /// 绑定设备 /// /// /// [HttpPut("BindDevice")] public async Task BindDevice(UavDeviceUpInput input) { var entity = await _db.Queryable().FirstAsync(p => p.DeviceCode == input.deviceCode); if (entity.IsNull()) { return new { code = -1, message = "设备不存在!", }; } if (entity.BelongUserId.IsNotEmptyOrNull()) { return new { code = -1, message = "该设备已绑定,请勿重复绑定!", }; } entity.DeviceName = input.deviceCode; entity.BelongUserId = _userManager.UserId; entity.SiteId = input.siteId; entity.Remark = input.remark; entity.Status = input.status; var isOk = await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync(); if (!(isOk > 0)) { return NCCException.Oh(ErrorCode.COM1001); } return new { code = 0, message = "绑定设备成功!", }; } #endregion #region 获取代理商设备数量信息 /// /// 获取代理商设备数量信息 /// /// [HttpGet("GetAgentDeviceCount")] public async Task GetAgentDeviceCount() { var DeviceCount = await _db.Queryable().Where(p => p.BelongUserId == _userManager.UserId).CountAsync(); return new { DeviceCount = DeviceCount }; } #endregion #region 柜子服务端进行绑定 /// /// 柜子服务端进行绑定 /// /// /// [HttpPut("CabinetBind")] [AllowAnonymous] public async Task CabinetBind(UavDeviceBindCrInput input) { var entity = await _db.Queryable().FirstAsync(p => p.Id == input.id); if (entity.MacAddress.IsNotEmptyOrNull()) { return new { code = -1, message = "设备已绑定!", }; } entity.MacAddress = input.macAddress; var isOk = await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync(); return new { code = 0, message = "绑定成功!", }; } #endregion #region 测试发布订阅 /// /// 测试发布订阅 /// /// /// [HttpPost("testPublish")] public async Task testPublish(UavDeviceOprnNotifyCrInput input) { string topic = $"device/{input.id}/response"; string message = input.ToJson(); await _mqttService.PublishAsync(topic, message); return new { code = 200, msg = "测试开门指令已发送" }; } #endregion #region 检测开门 /// /// 检测开门 /// /// /// [HttpPost("DetectionCheckOpenDoor")] public async Task DetectionCheckOpenDoor(UavDeviceOpenCrInput input) { var entity = await _db.Queryable().FirstAsync(p => p.Id == input.id); string topic = $"device/{entity.DeviceCode}/uav"; string message = new MqttContent { className = "DetectionOpen", action = "open", lane = input.lane, orderNo = "", instructionTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), tokenUrl = "" }.ToJson(); await _mqttService.PublishAsync(topic, message); return new { code = 200, msg = "测试开门指令已发送" }; } #endregion #region 同步设备信息(补货) /// /// 同步设备信息(补货) /// /// /// [HttpPost("SyncDeviceInfo")] [AllowAnonymous] public async Task SyncDeviceInfo(UavDeviceOprnNotifyCrInput input) { //获取设备信息 var DeviceInfo = await _db.Queryable().FirstAsync(p => p.Id == input.id); //通过设备编号以及货道code获取信息 var DeviceCellInfo = await _db.Queryable().FirstAsync(p => p.DeviceId == input.id && p.CellCode == input.lane); if (input.rfid_1.IsNotEmptyOrNull()) { //如果传入的无人机FRID不为空,就需要去修改无人机的信息 //通过传入的无人机FRID以及电池FRID去查询信息 var UavInfo = await _db.Queryable().FirstAsync(p => p.UavFrid == input.rfid_1); UavInfo.Status = UavAircraftStatusEnum.空闲.GetHashCode(); UavInfo.UavFrid = input.rfid_1; UavInfo.DeviceId = input.id; UavInfo.CellId = DeviceCellInfo.Id; UavInfo.BatteryCapacity = input.batteryCapacity; _db.Updateable(UavInfo).ExecuteCommand(); } else { //如果传入的无人机FRID为空,就通过货道id去找到之前绑定的无人机,然后去把绑定取消,并且标注无人机为 var UavInfo = _db.Queryable().Where(x => x.CellId == DeviceCellInfo.Id).First(); UavInfo.Status = UavAircraftStatusEnum.维修.GetHashCode(); UavInfo.DeviceId = string.Empty; UavInfo.CellId = string.Empty; UavInfo.BatteryCapacity = input.batteryCapacity; _db.Updateable(UavInfo).ExecuteCommand(); } return new { code = 0, message = "同步成功!", }; } #endregion #region 注册设备 /// /// 注册设备 /// /// /// [HttpPost("RegisterDevice/{DeviceCOde}")] [AllowAnonymous] public async Task RegisterDevice(string DeviceCOde) { Log.Information($"设备更新信息1111: {DeviceCOde}"); // 查询是否存在该设备 // 查询是否存在该设备 var entity = await _db.Queryable().Where(x => x.DeviceCode == DeviceCOde).FirstAsync(); if (!entity.IsNullOrEmpty()) { return new { success = true, message = "设备已存在,无需重复注册。" }; } // 开启事务 try { await _db.Ado.UseTranAsync(async () => { // 新建设备 var info = new UavDeviceEntity { Id = YitIdHelper.NextId().ToString(), DeviceCode = DeviceCOde, DeviceName = DeviceCOde, Status = DeviceStatusEnum.启用.GetHashCode(), CreateTime = DateTime.Now, }; await _db.Insertable(info).IgnoreColumns(ignoreNullColumn: true).ExecuteCommandAsync(); // 新建货道 var cellEntities = new List(); for (int i = 1; i <= 6; i++) { cellEntities.Add(new UavDeviceCellEntity { Id = YitIdHelper.NextId().ToString(), DeviceId = info.Id, CellCode = i.ToString(), Status = DeviceCellStatusEnum.空闲.GetHashCode(), CreateTime = DateTime.Now }); } await _db.Insertable(cellEntities).ExecuteCommandAsync(); }); return new { success = true, message = "设备注册成功。" }; } catch (Exception ex) { // 日志记录异常(如有日志组件) Log.Error($"设备注册异常: {ex.Message}"); return new { success = false, message = "设备注册失败,请联系系统管理员。" }; } } #endregion #region 注册设备(用于APP更新) /// /// 注册设备(用于APP更新) /// /// /// [HttpPost("RegisterDeviceForAppUpdate")] [AllowAnonymous] public async Task RegisterDeviceForAppUpdate(UavDeviceBindInput DeviceCodeInfo) { Log.Information($"设备更新信息: {DeviceCodeInfo.ToJson()}"); // 查询是否存在该设备 var entity = await _db.Queryable().Where(x => x.DeviceCode == DeviceCodeInfo.newDeviceCode || x.DeviceCode == DeviceCodeInfo.deviceCode).FirstAsync(); if (!entity.IsNullOrEmpty()) { entity.AppVersion = DeviceCodeInfo.appVersion; //判断两个设备编号 if (entity.DeviceCode == DeviceCodeInfo.newDeviceCode) { await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync(); return new { success = true, message = "设备已存在,无需重复注册。" }; } //如果两个设备编号不一致,则需要修改设备编号 entity.DeviceCode = DeviceCodeInfo.newDeviceCode; await _db.Updateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync(); return new { success = true, message = "设备信息更新成功" }; } // 开启事务 try { await _db.Ado.UseTranAsync(async () => { // 新建设备 var info = new UavDeviceEntity { Id = YitIdHelper.NextId().ToString(), DeviceCode = DeviceCodeInfo.newDeviceCode, DeviceName = DeviceCodeInfo.newDeviceCode, Status = DeviceStatusEnum.启用.GetHashCode(), CreateTime = DateTime.Now, AppVersion = DeviceCodeInfo.appVersion, }; await _db.Insertable(info).IgnoreColumns(ignoreNullColumn: true).ExecuteCommandAsync(); // 新建货道 var cellEntities = new List(); for (int i = 1; i <= 6; i++) { cellEntities.Add(new UavDeviceCellEntity { Id = YitIdHelper.NextId().ToString(), DeviceId = info.Id, CellCode = i.ToString(), Status = DeviceCellStatusEnum.空闲.GetHashCode(), CreateTime = DateTime.Now }); } await _db.Insertable(cellEntities).ExecuteCommandAsync(); }); return new { success = true, message = "设备注册成功。" }; } catch (Exception ex) { // 日志记录异常(如有日志组件) Log.Error($"设备注册异常: {ex.Message}"); return new { success = false, message = "设备注册失败,请联系系统管理员。" }; } } #endregion #region 机柜获取MQTT配置 /// /// 机柜获取MQTT配置 /// /// 设备编码 /// MQTT配置信息 [HttpGet("GetMqttConfig/{deviceCode}")] [AllowAnonymous] public async Task GetMqttConfig(string deviceCode = "") { var mqttBaseConfig = new { serverUri = "mqtt.cqjiangzhichao.cn", port = 1883, username = "wrjservice", password = "P@ssw0rd", qos = "0" }; var baseConfig = new { mqtt = mqttBaseConfig, maxRetryCount = "3", devicePortPath = "/dev/ttyS2", rfidPortPath = "/dev/ttyS4", downloadUrl = "https://wrj.cqjiangzhichao.cn/app.apk", deleteVideo = deviceCode.IsNullOrEmpty() ? 168 : (await _db.Queryable().FirstAsync(p => p.DeviceCode == deviceCode))?.DelVideoTime ?? 168, openLogActions = "close" }; return baseConfig; } #endregion #region 设备转让 /// /// 设备转让 /// /// /// [HttpPost("TransferDevice")] public async Task TransferDevice([FromBody] UavDeviceTransferInput input) { var entity = await _db.Queryable().FirstAsync(x => x.Id == input.id); if (entity.IsNullOrEmpty()) { return NCCException.Oh("设备不存在!"); } var userBool = await _db.Queryable().Where(x => x.Id == entity.BelongUserId).AnyAsync(); if (!userBool) { return NCCException.Oh("用户不存在!"); } entity.BelongUserId = input.belongUserId; var isOk = await _db.Updateable(entity).ExecuteCommandAsync(); if (isOk > 0) { return new { code = 0, message = "转让成功!", }; } else { return NCCException.Oh("转让失败!"); } } #endregion #region 推送APP更新消息 /// /// 推送APP更新消息给设备 /// /// 设备编码 /// APP下载地址 /// [HttpPost("PushAppUpdate")] public async Task PushAppUpdate(string deviceCode, string downloadUrl = "") { try { // 验证设备是否存在 var device = await _db.Queryable().FirstAsync(x => x.DeviceCode == deviceCode); if (device == null) { return new { code = 400, msg = "设备不存在" }; } // 如果没有提供下载地址,使用默认地址 if (string.IsNullOrEmpty(downloadUrl)) { downloadUrl = "https://wrj.cqjiangzhichao.cn/app.apk"; } // 构建MQTT消息 string topic = $"device/{deviceCode}/uav"; string message = new MqttContent { className = "updateapp", action = "open", lane = "1", // APP更新通常使用固定货道 orderNo = downloadUrl, // 将下载地址放在orderNo字段中 instructionTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), tokenUrl = "" }.ToJson(); // 发送MQTT消息 await _mqttService.PublishAsync(topic, message); Log.Information($"已推送APP更新消息到设备 {deviceCode},下载地址:{downloadUrl}"); return new { code = 200, msg = "APP更新消息推送成功", data = new { deviceCode = deviceCode, downloadUrl = downloadUrl, messageId = Guid.NewGuid().ToString() } }; } catch (Exception ex) { Log.Error($"推送APP更新消息失败:{ex.Message}"); return new { code = 500, msg = $"推送APP更新消息失败:{ex.Message}" }; } } /// /// 批量推送APP更新消息给多个设备 /// /// 设备编码列表 /// APP下载地址 /// [HttpPost("PushAppUpdateBatch")] public async Task PushAppUpdateBatch(string downloadUrl = "") { try { var deviceCodes = await _db.Queryable().ToListAsync(); // 如果没有提供下载地址,使用默认地址 if (string.IsNullOrEmpty(downloadUrl)) { downloadUrl = "https://wrj.cqjiangzhichao.cn/app.apk"; } var results = new List(); var successCount = 0; var failCount = 0; foreach (var item in deviceCodes) { try { // 构建MQTT消息 string topic = $"device/{item.DeviceCode}/uav"; string message = new MqttContent { className = "updateapp", action = "open", lane = "1", orderNo = downloadUrl, instructionTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), tokenUrl = "" }.ToJson(); // 发送MQTT消息 await _mqttService.PublishAsync(topic, message); results.Add(new { item.DeviceCode, success = true, message = "推送成功" }); successCount++; } catch (Exception ex) { results.Add(new { item.DeviceCode, success = false, message = ex.Message }); failCount++; } } Log.Information($"批量推送APP更新消息完成,成功:{successCount},失败:{failCount}"); return new { code = 200, msg = $"批量推送完成,成功:{successCount},失败:{failCount}", data = new { totalCount = deviceCodes.Count, successCount = successCount, failCount = failCount, results = results, downloadUrl = downloadUrl } }; } catch (Exception ex) { Log.Error($"批量推送APP更新消息失败:{ex.Message}"); return new { code = 500, msg = $"批量推送APP更新消息失败:{ex.Message}" }; } } #endregion #region 查询设备是否在线 /// /// 查询设备是否在线 /// /// /// [HttpGet("IsDeviceOnline/{deviceCode}")] public async Task IsDeviceOnline(string deviceCode) { //return new { deviceId = deviceCode, isOnline = true }; try { bool isOnline = await _mqttService.IsClientOnlineAsync(deviceCode); return new { deviceId = deviceCode, isOnline = isOnline }; } catch (Exception ex) { Log.Error($"查询设备 {deviceCode} 在线状态失败: {ex.Message}"); return new { deviceId = deviceCode, isOnline = false }; } } #endregion #region 批量查询设备在线状态 /// /// 批量查询设备在线状态(优化版本) /// /// 设备ID列表 /// [HttpPost("BatchCheckOnlineStatus")] public async Task BatchCheckOnlineStatus([FromBody] BatchCheckOnlineStatusInput input) { if (input?.DeviceIds == null || input.DeviceIds.Count == 0) { return new { success = false, message = "设备ID列表不能为空", data = new List() }; } try { // 使用新的批量查询方法,提高效率 var onlineStatusDict = await _mqttService.BatchCheckOnlineStatusAsync(input.DeviceIds); // 转换为前端期望的格式 var result = input.DeviceIds.Select(deviceId => new { deviceId = deviceId, isOnline = onlineStatusDict.ContainsKey(deviceId) ? onlineStatusDict[deviceId] : false }).ToList(); return new { success = true, message = $"成功查询 {result.Count} 个设备的在线状态", data = result }; } catch (Exception ex) { Log.Error($"批量查询设备在线状态失败: {ex.Message}"); return new { success = false, message = $"批量查询失败: {ex.Message}", data = new List() }; } } /// /// 获取设备列表(用于App更新推送,不包含在线状态查询) /// /// 查询参数 /// [HttpGet("GetDeviceListForUpdate")] public async Task GetDeviceListForUpdate([FromQuery] UavDeviceListQueryInput input) { var sidx = input.sidx == null ? "id" : input.sidx; var data = await _db.Queryable() .WhereIF(!string.IsNullOrEmpty(input.deviceName), p => p.DeviceName.Contains(input.deviceName)) .WhereIF(!string.IsNullOrEmpty(input.deviceCode), p => p.DeviceCode.Contains(input.deviceCode)) .WhereIF(!string.IsNullOrEmpty(input.belongUserId), p => p.BelongUserId.Contains(input.belongUserId)) .Select(it => new UavDeviceListOutput { id = it.Id, deviceName = it.DeviceName, deviceCode = it.DeviceCode, siteId = it.SiteId, belongUserId = it.BelongUserId, lng = it.Lng, lat = it.Lat, status = it.Status, remark = it.Remark, appVersion = it.AppVersion, cellnumber = SqlFunc.Subqueryable().Where(u => u.DeviceId == it.Id).Count(), }).MergeTable().OrderBy(sidx + " " + input.sort).ToPagedListAsync(input.currentPage, input.pageSize); return PageResult.SqlSugarPageResult(data); } #endregion } }