From 4d4da8ac2b4f63f67ce5d7d89dc4869fd373206a Mon Sep 17 00:00:00 2001 From: guorui <774114798@qq.com> Date: Wed, 26 Nov 2025 15:21:40 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=B7=A1=E6=A3=80=E7=BB=93?= =?UTF-8?q?=E6=9E=9C=E7=9A=84=E8=8E=B7=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...econdaryCircuitInspectionItemAppService.cs | 16 +- .../TelemeteringConfigurationAppService.cs | 4 +- ...elesignalisationConfigurationAppServcie.cs | 5 +- .../BjYounuo/NetworkCableAppService.cs | 161 +++++++- .../BjYounuo/OpticalCableAppService.cs | 154 +++++++- .../BjYounuo/OpticalFiberAppService.cs | 154 +++++++- ...ondaryElectricalEquipmentInfoAppService.cs | 74 ++-- .../EquipmentLiveDataAppService.cs | 17 +- .../TransformInfomationAppService.cs | 234 ++++++++--- ...TransformSubstationExportDataAppService.cs | 232 +++++++++-- ...daryCircuitInspectionResultDetailOutput.cs | 60 +-- .../YunDa.SOMS.DataTransferObject.xml | 50 +-- .../InspectionItemResultAppService.cs | 139 ++++++- ...ondaryCircuitInspectionResultAppService.cs | 108 +++--- ...uitInspectionResultStatisticsAppService.cs | 90 ++++- .../MeasuresTemperatureResultAppService.cs | 362 ++++++++++++++---- src/YunDa.Util/ToolLibrary/HttpHelper.cs | 104 +++-- 17 files changed, 1581 insertions(+), 383 deletions(-) diff --git a/src/YunDa.Application/YunDa.SOMS.Application/DataMonitoring/SecondaryCircuitInspection/SecondaryCircuitInspectionItemAppService.cs b/src/YunDa.Application/YunDa.SOMS.Application/DataMonitoring/SecondaryCircuitInspection/SecondaryCircuitInspectionItemAppService.cs index 4da0c27..adf5b5d 100644 --- a/src/YunDa.Application/YunDa.SOMS.Application/DataMonitoring/SecondaryCircuitInspection/SecondaryCircuitInspectionItemAppService.cs +++ b/src/YunDa.Application/YunDa.SOMS.Application/DataMonitoring/SecondaryCircuitInspection/SecondaryCircuitInspectionItemAppService.cs @@ -1095,7 +1095,7 @@ namespace YunDa.SOMS.Application.DataMonitoring.SecondaryCircuitInspection }; if (ids.Count>0) { - var str = HttpHelper.HttpPostRequest(url, obj); + var str = HttpHelper.HttpPostRequestWithRetry(url, obj); if (str!=null) { if (bool.Parse(str["success"].ToString()) == true) @@ -1156,7 +1156,7 @@ namespace YunDa.SOMS.Application.DataMonitoring.SecondaryCircuitInspection /// /// /// - [HttpGet, Audited] + [HttpGet, DisableAuditing] [ShowApi] [AllowAnonymous] public RequestResult> GetTeleSignalData(Guid id) @@ -1208,7 +1208,7 @@ namespace YunDa.SOMS.Application.DataMonitoring.SecondaryCircuitInspection }; if (ids.Count>0) { - var str = HttpHelper.HttpPostRequest(url, obj); + var str = HttpHelper.HttpPostRequestWithRetry(url, obj); if (str!=null) { if (bool.Parse(str["success"].ToString()) == true) @@ -1311,7 +1311,7 @@ namespace YunDa.SOMS.Application.DataMonitoring.SecondaryCircuitInspection if (ids.Count > 0) { - var str = HttpHelper.HttpPostRequest(url, obj); + var str = HttpHelper.HttpPostRequestWithRetry(url, obj); if (str != null) { if (bool.Parse(str["success"].ToString()) == true) @@ -1358,7 +1358,7 @@ namespace YunDa.SOMS.Application.DataMonitoring.SecondaryCircuitInspection /// /// /// - [HttpGet, Audited] + [HttpGet, DisableAuditing] [ShowApi] [AllowAnonymous] public RequestResult> GetGatewayInfoData(Guid id) @@ -1402,7 +1402,7 @@ namespace YunDa.SOMS.Application.DataMonitoring.SecondaryCircuitInspection if (ids.Count > 0) { - var str = HttpHelper.HttpPostRequest(url, obj); + var str = HttpHelper.HttpPostRequestWithRetry(url, obj); if (str != null) { if (bool.Parse(str["success"].ToString()) == true) @@ -1453,6 +1453,7 @@ namespace YunDa.SOMS.Application.DataMonitoring.SecondaryCircuitInspection [HttpGet] [ShowApi] [AllowAnonymous] + [DisableAuditing] [UnitOfWork(isTransactional: false)] public async Task>> GetDeviceDzDataAsync(Guid id) { @@ -1615,6 +1616,7 @@ namespace YunDa.SOMS.Application.DataMonitoring.SecondaryCircuitInspection [HttpGet, Description("获取虚点信息选择框")] [ShowApi] [AllowAnonymous] + [DisableAuditing] public RequestResult> GetVariantForSelect() { RequestResult> rst = new RequestResult>(); @@ -1641,6 +1643,7 @@ namespace YunDa.SOMS.Application.DataMonitoring.SecondaryCircuitInspection [HttpGet, Description("获取网关数据选择框")] [ShowApi] [AllowAnonymous] + [DisableAuditing] public RequestResult> GetGateWayForSelect() { RequestResult> rst = new RequestResult>(); @@ -1667,6 +1670,7 @@ namespace YunDa.SOMS.Application.DataMonitoring.SecondaryCircuitInspection [HttpGet, Description("获取基础网关数据")] [ShowApi] [AllowAnonymous] + [DisableAuditing] public RequestResult> GetGateWayBaseInfo() { RequestResult> rst = new RequestResult>(); diff --git a/src/YunDa.Application/YunDa.SOMS.Application/DataMonitoring/TelemeteringConfiguration/TelemeteringConfigurationAppService.cs b/src/YunDa.Application/YunDa.SOMS.Application/DataMonitoring/TelemeteringConfiguration/TelemeteringConfigurationAppService.cs index ed15696..25437f9 100644 --- a/src/YunDa.Application/YunDa.SOMS.Application/DataMonitoring/TelemeteringConfiguration/TelemeteringConfigurationAppService.cs +++ b/src/YunDa.Application/YunDa.SOMS.Application/DataMonitoring/TelemeteringConfiguration/TelemeteringConfigurationAppService.cs @@ -532,7 +532,7 @@ namespace YunDa.SOMS.Application.DataMonitoring .WhereIf(searchCondition.SearchCondition.Id.HasValue, t => t.Id == searchCondition.SearchCondition.Id).WhereIf( searchCondition.SearchCondition.EquipmentInfoId.HasValue, - t => t.EquipmentInfoId == searchCondition.SearchCondition.EquipmentInfoId) + t => t.EquipmentInfoId == searchCondition.SearchCondition.EquipmentInfoId|| t.PrimaryEquipmentInfoId == searchCondition.SearchCondition.EquipmentInfoId) .Join(equipTypeG, f => f.EquipmentTypeId, s => s.Id, (f, s) => f); datas = datas.OrderBy(t => t.EquipmentType.SeqNo).ThenBy(t => t.EquipmentInfo.SeqNo).ThenBy(t => t.DispatcherAddress); var rstDatas = datas.ToList().AsQueryable(); @@ -687,7 +687,7 @@ namespace YunDa.SOMS.Application.DataMonitoring .WhereIf(searchCondition.IsVirtualDevice.HasValue, m => m.IsVirtualDevice == searchCondition.IsVirtualDevice) .WhereIf(searchCondition.TransformerSubstationId.HasValue, m => m.TransformerSubstationId == searchCondition.TransformerSubstationId); - datas = datas.OrderBy(t => t.SeqNo); + datas = datas.OrderBy(t => t.ismsbaseYCId); var dic = datas.AsEnumerable().GroupBy(t => t.EquipmentInfoId); var list = new List(); diff --git a/src/YunDa.Application/YunDa.SOMS.Application/DataMonitoring/TelesignalisationConfiguration/TelesignalisationConfigurationAppServcie.cs b/src/YunDa.Application/YunDa.SOMS.Application/DataMonitoring/TelesignalisationConfiguration/TelesignalisationConfigurationAppServcie.cs index 90523fb..de876d1 100644 --- a/src/YunDa.Application/YunDa.SOMS.Application/DataMonitoring/TelesignalisationConfiguration/TelesignalisationConfigurationAppServcie.cs +++ b/src/YunDa.Application/YunDa.SOMS.Application/DataMonitoring/TelesignalisationConfiguration/TelesignalisationConfigurationAppServcie.cs @@ -381,7 +381,7 @@ namespace YunDa.SOMS.Application.DataMonitoring .WhereIf(searchCondition.SearchCondition.Id.HasValue, t => t.Id == searchCondition.SearchCondition.Id).WhereIf( searchCondition.SearchCondition.EquipmentInfoId.HasValue, - t => t.EquipmentInfoId == searchCondition.SearchCondition.EquipmentInfoId) + t => t.EquipmentInfoId == searchCondition.SearchCondition.EquipmentInfoId || t.PrimaryEquipmentInfoId == searchCondition.SearchCondition.EquipmentInfoId) .Join(equipTypeG, f => f.EquipmentTypeId, s => s.Id, (f, s) => f); var rstDatas = datas.ToList().AsQueryable(); @@ -559,8 +559,7 @@ namespace YunDa.SOMS.Application.DataMonitoring .WhereIf(searchCondition.EquipmentInfoId.HasValue, m => m.EquipmentInfoId == searchCondition.EquipmentInfoId) .WhereIf(searchCondition.IsVirtualDevice.HasValue, m => m.IsVirtualDevice == searchCondition.IsVirtualDevice) .WhereIf(searchCondition.TransformerSubstationId.HasValue, m => m.TransformerSubstationId == searchCondition.TransformerSubstationId); - datas = datas.OrderBy(t => t.EquipmentInfoId); - datas = datas.OrderBy(t => t.SeqNo).ThenBy(t => t.InfoAddress); + datas = datas.OrderBy(t => t.ismsbaseYXId).ThenBy(t => t.SeqNo); rst.ResultData = datas.Select(m => new SelectModelOutput { diff --git a/src/YunDa.Application/YunDa.SOMS.Application/ExternalDataManager/BjYounuo/NetworkCableAppService.cs b/src/YunDa.Application/YunDa.SOMS.Application/ExternalDataManager/BjYounuo/NetworkCableAppService.cs index 69881b5..4b45871 100644 --- a/src/YunDa.Application/YunDa.SOMS.Application/ExternalDataManager/BjYounuo/NetworkCableAppService.cs +++ b/src/YunDa.Application/YunDa.SOMS.Application/ExternalDataManager/BjYounuo/NetworkCableAppService.cs @@ -13,6 +13,8 @@ using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Net.Http; +using System.Text; +using System.Text.Json; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -35,6 +37,8 @@ namespace YunDa.SOMS.Application.ExternalDataManager.BjYounuo [Description("网线管理服务")] public class NetworkCableAppService : SOMSAppServiceBase, INetworkCableAppService { + private const string AI_API_URL = "http://192.168.81.25:8002/chat/ai"; + private readonly IRepository _networkCableRepository; private readonly IRepository _equipmentInfoRepository; private readonly IRepository _telemeteringConfigurationRepository; @@ -499,23 +503,51 @@ namespace YunDa.SOMS.Application.ExternalDataManager.BjYounuo }; } - // 生成影响范围和处置意见 - var (impactScopeField, impactScopeDescription) = GenerateNetworkCableImpactScope(networkCable); - var disposalAdvice = GenerateNetworkCableDisposalAdvice(networkCable); + string responseData; - // 格式化响应数据 - var responseData = $"影响范围:\n{impactScopeDescription}\n{disposalAdvice}"; + // 尝试使用AI生成智能响应 + try + { + var prompt = BuildNetworkCableDisconnectionPrompt(networkCable); + var aiResponse = await CallAiApiAsync(prompt, cancellationToken).ConfigureAwait(false); + var aiContent = ParseAiStreamingResponse(aiResponse); + + if (!string.IsNullOrWhiteSpace(aiContent)) + { + responseData = aiContent; + Log4Helper.Info(GetType(), $"成功使用AI生成网线断线响应,孪生体ID: {twinId}"); + } + else + { + // AI返回空内容,使用静态生成方法 + Log4Helper.Warning(GetType(), $"AI返回空内容,使用静态生成方法,孪生体ID: {twinId}"); + var (impactScopeField, impactScopeDescription) = GenerateNetworkCableImpactScope(networkCable); + var disposalAdvice = GenerateNetworkCableDisposalAdvice(networkCable); + responseData = $"影响范围:\n{impactScopeDescription}\n{disposalAdvice}"; + } + } + catch (Exception aiEx) + { + // AI调用失败,使用静态生成方法作为后备 + Log4Helper.Warning(GetType(), $"AI API调用失败,使用静态生成方法,孪生体ID: {twinId}", aiEx); + var (impactScopeField, impactScopeDescription) = GenerateNetworkCableImpactScope(networkCable); + var disposalAdvice = GenerateNetworkCableDisposalAdvice(networkCable); + responseData = $"影响范围:\n{impactScopeDescription}\n{disposalAdvice}"; + } + + // 推送告警 YounuoAlert alert = new YounuoAlert { LastOccurrence = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), Severity = 5, - SourceAlertKey = "模拟光缆断线", + SourceAlertKey = "模拟网线断线", Status = 1, SourceIdentifier = networkCable.P1DeviceNumber, - Summary = "模拟光缆断线", + Summary = "模拟网线断线", TwinID = twinId, }; await _beijingYounuoApiAppService.PushYounuoAlertAsync(alert); + return new SimpleDisconnectionResponse { Code = 200, @@ -717,6 +749,119 @@ namespace YunDa.SOMS.Application.ExternalDataManager.BjYounuo } } - + #region AI Integration Helper Methods + + /// + /// 构建网线断线场景的AI提示词 + /// + private string BuildNetworkCableDisconnectionPrompt(NetworkCable networkCable) + { + var prompt = new StringBuilder(); + prompt.AppendLine("请作为一名专业的电力系统运维专家,分析以下网线断线场景并提供详细的影响范围分析和处置建议。"); + prompt.AppendLine(); + prompt.AppendLine("【断线信息】"); + prompt.AppendLine($"- 连接类型:网线"); + prompt.AppendLine($"- 孪生体ID:{networkCable.TwinId}"); + + if (!string.IsNullOrEmpty(networkCable.P1CabinetName)) + prompt.AppendLine($"- P1端屏柜:{networkCable.P1CabinetName}"); + if (!string.IsNullOrEmpty(networkCable.P1DeviceName)) + prompt.AppendLine($"- P1端设备:{networkCable.P1DeviceName}"); + if (!string.IsNullOrEmpty(networkCable.P1DeviceNumber)) + prompt.AppendLine($"- P1端设备编号:{networkCable.P1DeviceNumber}"); + if (!string.IsNullOrEmpty(networkCable.P1PortNumber)) + prompt.AppendLine($"- P1端端口:{networkCable.P1PortNumber}"); + + if (!string.IsNullOrEmpty(networkCable.P2CabinetName)) + prompt.AppendLine($"- P2端屏柜:{networkCable.P2CabinetName}"); + if (!string.IsNullOrEmpty(networkCable.P2DeviceName)) + prompt.AppendLine($"- P2端设备:{networkCable.P2DeviceName}"); + if (!string.IsNullOrEmpty(networkCable.P2DeviceNumber)) + prompt.AppendLine($"- P2端设备编号:{networkCable.P2DeviceNumber}"); + if (!string.IsNullOrEmpty(networkCable.P2PortNumber)) + prompt.AppendLine($"- P2端端口:{networkCable.P2PortNumber}"); + + if (!string.IsNullOrEmpty(networkCable.WiringDirection)) + prompt.AppendLine($"- 配线走向:{networkCable.WiringDirection}"); + + prompt.AppendLine(); + prompt.AppendLine("【要求】"); + prompt.AppendLine("请按照以下格式提供分析结果:"); + prompt.AppendLine(); + prompt.AppendLine("影响范围:"); + prompt.AppendLine("(请详细列出该网线断线可能影响的设备、系统和功能,每项单独一行)"); + prompt.AppendLine(); + prompt.AppendLine("处置意见:"); + prompt.AppendLine("(请提供具体的排查步骤和处置建议,按优先级排序,每项单独一行)"); + + return prompt.ToString(); + } + + /// + /// 调用AI API获取分析结果 + /// + private async Task CallAiApiAsync(string userMessage, CancellationToken cancellationToken) + { + var requestData = new + { + user_message = userMessage, + history = new List() + }; + + var jsonContent = JsonConvert.SerializeObject(requestData); + var content = new StringContent(jsonContent, Encoding.UTF8, "application/json"); + + Log4Helper.Info(GetType(), $"调用AI API: {AI_API_URL}"); + + var response = await _httpClient.PostAsync(AI_API_URL, content, cancellationToken).ConfigureAwait(false); + response.EnsureSuccessStatusCode(); + + var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + + Log4Helper.Debug(GetType(), $"AI API原始响应长度: {responseText.Length} 字符"); + + return responseText; + } + + /// + /// 解析AI流式响应,提取content内容 + /// + private string ParseAiStreamingResponse(string rawResponse) + { + if (string.IsNullOrWhiteSpace(rawResponse)) + return string.Empty; + + var result = new StringBuilder(); + + foreach (string line in rawResponse.Split('\n')) + { + var trimmed = line.Trim(); + if (string.IsNullOrWhiteSpace(trimmed)) + continue; + + try + { + using var doc = JsonDocument.Parse(trimmed); + var root = doc.RootElement; + + if (root.TryGetProperty("type", out var typeProp) && + typeProp.GetString() == "content" && + root.TryGetProperty("content", out var contentProp)) + { + result.Append(contentProp.GetString()); + } + } + catch + { + // 跳过非JSON行或解析失败的行 + continue; + } + } + + return result.ToString(); + } + + #endregion + } } diff --git a/src/YunDa.Application/YunDa.SOMS.Application/ExternalDataManager/BjYounuo/OpticalCableAppService.cs b/src/YunDa.Application/YunDa.SOMS.Application/ExternalDataManager/BjYounuo/OpticalCableAppService.cs index 00c2e76..bc2e318 100644 --- a/src/YunDa.Application/YunDa.SOMS.Application/ExternalDataManager/BjYounuo/OpticalCableAppService.cs +++ b/src/YunDa.Application/YunDa.SOMS.Application/ExternalDataManager/BjYounuo/OpticalCableAppService.cs @@ -14,6 +14,8 @@ using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Net.Http; +using System.Text; +using System.Text.Json; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -36,6 +38,8 @@ namespace YunDa.SOMS.Application.ExternalDataManager.BjYounuo [Description("光缆管理服务")] public class OpticalCableAppService : SOMSAppServiceBase, IOpticalCableAppService { + private const string AI_API_URL = "http://192.168.81.25:8002/chat/ai"; + private readonly IRepository _opticalCableRepository; private readonly IRepository _equipmentInfoRepository; private readonly HttpClient _httpClient; @@ -556,12 +560,39 @@ namespace YunDa.SOMS.Application.ExternalDataManager.BjYounuo }; } - // 生成影响范围和处置意见 - var (impactScopeField, impactScopeDescription) = GenerateOpticalCableImpactScope(opticalCable); - var disposalAdvice = GenerateOpticalCableDisposalAdvice(opticalCable); + string responseData; - // 格式化响应数据 - var responseData = $"影响范围:\n{impactScopeDescription}\n{disposalAdvice}"; + // 尝试使用AI生成智能响应 + try + { + var prompt = BuildOpticalCableDisconnectionPrompt(opticalCable); + var aiResponse = await CallAiApiAsync(prompt, cancellationToken).ConfigureAwait(false); + var aiContent = ParseAiStreamingResponse(aiResponse); + + if (!string.IsNullOrWhiteSpace(aiContent)) + { + responseData = aiContent; + Log4Helper.Info(GetType(), $"成功使用AI生成光缆断线响应,孪生体ID: {twinId}"); + } + else + { + // AI返回空内容,使用静态生成方法 + Log4Helper.Warning(GetType(), $"AI返回空内容,使用静态生成方法,孪生体ID: {twinId}"); + var (impactScopeField, impactScopeDescription) = GenerateOpticalCableImpactScope(opticalCable); + var disposalAdvice = GenerateOpticalCableDisposalAdvice(opticalCable); + responseData = $"影响范围:\n{impactScopeDescription}\n{disposalAdvice}"; + } + } + catch (Exception aiEx) + { + // AI调用失败,使用静态生成方法作为后备 + Log4Helper.Warning(GetType(), $"AI API调用失败,使用静态生成方法,孪生体ID: {twinId}", aiEx); + var (impactScopeField, impactScopeDescription) = GenerateOpticalCableImpactScope(opticalCable); + var disposalAdvice = GenerateOpticalCableDisposalAdvice(opticalCable); + responseData = $"影响范围:\n{impactScopeDescription}\n{disposalAdvice}"; + } + + // 推送告警 YounuoAlert alert = new YounuoAlert { LastOccurrence = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), @@ -573,6 +604,7 @@ namespace YunDa.SOMS.Application.ExternalDataManager.BjYounuo TwinID = twinId, }; await _beijingYounuoApiAppService.PushYounuoAlertAsync(alert); + return new SimpleDisconnectionResponse { Code = 200, @@ -768,5 +800,117 @@ namespace YunDa.SOMS.Application.ExternalDataManager.BjYounuo return RequestResult.CreateFailed($"未知错误: {ex.Message}"); } } + + #region AI Integration Helper Methods + + /// + /// 构建光缆断线场景的AI提示词 + /// + private string BuildOpticalCableDisconnectionPrompt(OpticalCable opticalCable) + { + var prompt = new StringBuilder(); + prompt.AppendLine("请作为一名专业的电力系统运维专家,分析以下光缆断线场景并提供详细的影响范围分析和处置建议。"); + prompt.AppendLine(); + prompt.AppendLine("【断线信息】"); + prompt.AppendLine($"- 连接类型:光缆"); + prompt.AppendLine($"- 孪生体ID:{opticalCable.TwinId}"); + + if (!string.IsNullOrEmpty(opticalCable.P1CabinetName)) + prompt.AppendLine($"- P1端屏柜:{opticalCable.P1CabinetName}"); + if (!string.IsNullOrEmpty(opticalCable.P1DeviceNumber)) + prompt.AppendLine($"- P1端设备编号:{opticalCable.P1DeviceNumber}"); + if (!string.IsNullOrEmpty(opticalCable.P1PortNumber)) + prompt.AppendLine($"- P1端端口:{opticalCable.P1PortNumber}"); + + if (!string.IsNullOrEmpty(opticalCable.P2CabinetName)) + prompt.AppendLine($"- P2端屏柜:{opticalCable.P2CabinetName}"); + if (!string.IsNullOrEmpty(opticalCable.P2DeviceNumber)) + prompt.AppendLine($"- P2端设备编号:{opticalCable.P2DeviceNumber}"); + if (!string.IsNullOrEmpty(opticalCable.P2PortNumber)) + prompt.AppendLine($"- P2端端口:{opticalCable.P2PortNumber}"); + + if (!string.IsNullOrEmpty(opticalCable.CableNumber)) + prompt.AppendLine($"- 光缆编号:{opticalCable.CableNumber}"); + if (!string.IsNullOrEmpty(opticalCable.WiringDirection)) + prompt.AppendLine($"- 配线走向:{opticalCable.WiringDirection}"); + + prompt.AppendLine(); + prompt.AppendLine("【要求】"); + prompt.AppendLine("请按照以下格式提供分析结果:"); + prompt.AppendLine(); + prompt.AppendLine("影响范围:"); + prompt.AppendLine("(请详细列出该光缆断线可能影响的设备、系统和功能,每项单独一行)"); + prompt.AppendLine(); + prompt.AppendLine("处置意见:"); + prompt.AppendLine("(请提供具体的排查步骤和处置建议,按优先级排序,每项单独一行)"); + + return prompt.ToString(); + } + + /// + /// 调用AI API获取分析结果 + /// + private async Task CallAiApiAsync(string userMessage, CancellationToken cancellationToken) + { + var requestData = new + { + user_message = userMessage, + history = new List() + }; + + var jsonContent = JsonConvert.SerializeObject(requestData); + var content = new StringContent(jsonContent, Encoding.UTF8, "application/json"); + + Log4Helper.Info(GetType(), $"调用AI API: {AI_API_URL}"); + + var response = await _httpClient.PostAsync(AI_API_URL, content, cancellationToken).ConfigureAwait(false); + response.EnsureSuccessStatusCode(); + + var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + + Log4Helper.Debug(GetType(), $"AI API原始响应长度: {responseText.Length} 字符"); + + return responseText; + } + + /// + /// 解析AI流式响应,提取content内容 + /// + private string ParseAiStreamingResponse(string rawResponse) + { + if (string.IsNullOrWhiteSpace(rawResponse)) + return string.Empty; + + var result = new StringBuilder(); + + foreach (string line in rawResponse.Split('\n')) + { + var trimmed = line.Trim(); + if (string.IsNullOrWhiteSpace(trimmed)) + continue; + + try + { + using var doc = JsonDocument.Parse(trimmed); + var root = doc.RootElement; + + if (root.TryGetProperty("type", out var typeProp) && + typeProp.GetString() == "content" && + root.TryGetProperty("content", out var contentProp)) + { + result.Append(contentProp.GetString()); + } + } + catch + { + // 跳过非JSON行或解析失败的行 + continue; + } + } + + return result.ToString(); + } + + #endregion } } diff --git a/src/YunDa.Application/YunDa.SOMS.Application/ExternalDataManager/BjYounuo/OpticalFiberAppService.cs b/src/YunDa.Application/YunDa.SOMS.Application/ExternalDataManager/BjYounuo/OpticalFiberAppService.cs index abd1231..c89c3ec 100644 --- a/src/YunDa.Application/YunDa.SOMS.Application/ExternalDataManager/BjYounuo/OpticalFiberAppService.cs +++ b/src/YunDa.Application/YunDa.SOMS.Application/ExternalDataManager/BjYounuo/OpticalFiberAppService.cs @@ -14,6 +14,8 @@ using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Net.Http; +using System.Text; +using System.Text.Json; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -36,6 +38,8 @@ namespace YunDa.SOMS.Application.ExternalDataManager.BjYounuo [Description("光纤管理服务")] public class OpticalFiberAppService : SOMSAppServiceBase, IOpticalFiberAppService { + private const string AI_API_URL = "http://192.168.81.25:8002/chat/ai"; + private readonly IRepository _opticalFiberRepository; private readonly IRepository _equipmentInfoRepository; private readonly HttpClient _httpClient; @@ -562,12 +566,39 @@ namespace YunDa.SOMS.Application.ExternalDataManager.BjYounuo }; } - // 生成影响范围和处置意见 - var (impactScopeField, impactScopeDescription) = GenerateOpticalFiberImpactScope(opticalFiber); - var disposalAdvice = GenerateOpticalFiberDisposalAdvice(opticalFiber); + string responseData; - // 格式化响应数据 - var responseData = $"影响范围:\n{impactScopeDescription}\n{disposalAdvice}"; + // 尝试使用AI生成智能响应 + try + { + var prompt = BuildOpticalFiberDisconnectionPrompt(opticalFiber); + var aiResponse = await CallAiApiAsync(prompt, cancellationToken).ConfigureAwait(false); + var aiContent = ParseAiStreamingResponse(aiResponse); + + if (!string.IsNullOrWhiteSpace(aiContent)) + { + responseData = aiContent; + Log4Helper.Info(GetType(), $"成功使用AI生成光纤断线响应,孪生体ID: {twinId}"); + } + else + { + // AI返回空内容,使用静态生成方法 + Log4Helper.Warning(GetType(), $"AI返回空内容,使用静态生成方法,孪生体ID: {twinId}"); + var (impactScopeField, impactScopeDescription) = GenerateOpticalFiberImpactScope(opticalFiber); + var disposalAdvice = GenerateOpticalFiberDisposalAdvice(opticalFiber); + responseData = $"影响范围:\n{impactScopeDescription}\n{disposalAdvice}"; + } + } + catch (Exception aiEx) + { + // AI调用失败,使用静态生成方法作为后备 + Log4Helper.Warning(GetType(), $"AI API调用失败,使用静态生成方法,孪生体ID: {twinId}", aiEx); + var (impactScopeField, impactScopeDescription) = GenerateOpticalFiberImpactScope(opticalFiber); + var disposalAdvice = GenerateOpticalFiberDisposalAdvice(opticalFiber); + responseData = $"影响范围:\n{impactScopeDescription}\n{disposalAdvice}"; + } + + // 推送告警 YounuoAlert alert = new YounuoAlert { LastOccurrence = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), @@ -579,6 +610,7 @@ namespace YunDa.SOMS.Application.ExternalDataManager.BjYounuo TwinID = twinId, }; await _beijingYounuoApiAppService.PushYounuoAlertAsync(alert); + return new SimpleDisconnectionResponse { Code = 200, @@ -774,5 +806,117 @@ namespace YunDa.SOMS.Application.ExternalDataManager.BjYounuo return RequestResult.CreateFailed($"未知错误: {ex.Message}"); } } + + #region AI Integration Helper Methods + + /// + /// 构建光纤断线场景的AI提示词 + /// + private string BuildOpticalFiberDisconnectionPrompt(OpticalFiber opticalFiber) + { + var prompt = new StringBuilder(); + prompt.AppendLine("请作为一名专业的电力系统运维专家,分析以下光纤断线场景并提供详细的影响范围分析和处置建议。"); + prompt.AppendLine(); + prompt.AppendLine("【断线信息】"); + prompt.AppendLine($"- 连接类型:光纤"); + prompt.AppendLine($"- 孪生体ID:{opticalFiber.TwinId}"); + + if (!string.IsNullOrEmpty(opticalFiber.P1CabinetName)) + prompt.AppendLine($"- P1端屏柜:{opticalFiber.P1CabinetName}"); + if (!string.IsNullOrEmpty(opticalFiber.P1DeviceNumber)) + prompt.AppendLine($"- P1端设备编号:{opticalFiber.P1DeviceNumber}"); + if (!string.IsNullOrEmpty(opticalFiber.P1PortNumber)) + prompt.AppendLine($"- P1端端口:{opticalFiber.P1PortNumber}"); + + if (!string.IsNullOrEmpty(opticalFiber.P2CabinetName)) + prompt.AppendLine($"- P2端屏柜:{opticalFiber.P2CabinetName}"); + if (!string.IsNullOrEmpty(opticalFiber.P2DeviceNumber)) + prompt.AppendLine($"- P2端设备编号:{opticalFiber.P2DeviceNumber}"); + if (!string.IsNullOrEmpty(opticalFiber.P2PortNumber)) + prompt.AppendLine($"- P2端端口:{opticalFiber.P2PortNumber}"); + + if (!string.IsNullOrEmpty(opticalFiber.VirtualPointCode)) + prompt.AppendLine($"- 虚拟点位代码:{opticalFiber.VirtualPointCode}"); + if (!string.IsNullOrEmpty(opticalFiber.WiringDirection)) + prompt.AppendLine($"- 配线走向:{opticalFiber.WiringDirection}"); + + prompt.AppendLine(); + prompt.AppendLine("【要求】"); + prompt.AppendLine("请按照以下格式提供分析结果:"); + prompt.AppendLine(); + prompt.AppendLine("影响范围:"); + prompt.AppendLine("(请详细列出该光纤断线可能影响的设备、系统和功能,每项单独一行)"); + prompt.AppendLine(); + prompt.AppendLine("处置意见:"); + prompt.AppendLine("(请提供具体的排查步骤和处置建议,按优先级排序,每项单独一行)"); + + return prompt.ToString(); + } + + /// + /// 调用AI API获取分析结果 + /// + private async Task CallAiApiAsync(string userMessage, CancellationToken cancellationToken) + { + var requestData = new + { + user_message = userMessage, + history = new List() + }; + + var jsonContent = JsonConvert.SerializeObject(requestData); + var content = new StringContent(jsonContent, Encoding.UTF8, "application/json"); + + Log4Helper.Info(GetType(), $"调用AI API: {AI_API_URL}"); + + var response = await _httpClient.PostAsync(AI_API_URL, content, cancellationToken).ConfigureAwait(false); + response.EnsureSuccessStatusCode(); + + var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + + Log4Helper.Debug(GetType(), $"AI API原始响应长度: {responseText.Length} 字符"); + + return responseText; + } + + /// + /// 解析AI流式响应,提取content内容 + /// + private string ParseAiStreamingResponse(string rawResponse) + { + if (string.IsNullOrWhiteSpace(rawResponse)) + return string.Empty; + + var result = new StringBuilder(); + + foreach (string line in rawResponse.Split('\n')) + { + var trimmed = line.Trim(); + if (string.IsNullOrWhiteSpace(trimmed)) + continue; + + try + { + using var doc = JsonDocument.Parse(trimmed); + var root = doc.RootElement; + + if (root.TryGetProperty("type", out var typeProp) && + typeProp.GetString() == "content" && + root.TryGetProperty("content", out var contentProp)) + { + result.Append(contentProp.GetString()); + } + } + catch + { + // 跳过非JSON行或解析失败的行 + continue; + } + } + + return result.ToString(); + } + + #endregion } } diff --git a/src/YunDa.Application/YunDa.SOMS.Application/GeneralInformation/EquipmentInfo/SecondaryElectricalEquipmentInfoAppService.cs b/src/YunDa.Application/YunDa.SOMS.Application/GeneralInformation/EquipmentInfo/SecondaryElectricalEquipmentInfoAppService.cs index bbdd21e..432739f 100644 --- a/src/YunDa.Application/YunDa.SOMS.Application/GeneralInformation/EquipmentInfo/SecondaryElectricalEquipmentInfoAppService.cs +++ b/src/YunDa.Application/YunDa.SOMS.Application/GeneralInformation/EquipmentInfo/SecondaryElectricalEquipmentInfoAppService.cs @@ -419,13 +419,12 @@ namespace YunDa.SOMS.Application.GeneralInformation var scopedRepository = scope.ServiceProvider.GetRequiredService>(); using var unitOfWork = scopedUnitOfWorkManager.Begin(); - foreach (var config in batch) - { - scopedRepository.Update(config); - updateCount++; - } + // 使用 UpdateRange 进行真正的批量更新操作 + scopedRepository.GetDbContext().UpdateRange(batch); await unitOfWork.CompleteAsync(); + + updateCount += batch.Length; } return updateCount; @@ -455,13 +454,11 @@ namespace YunDa.SOMS.Application.GeneralInformation using var unitOfWork = scopedUnitOfWorkManager.Begin(); - foreach (var config in batch) - { - scopedRepository.Update(config); - updateCount++; - } - + // 使用 UpdateRange 进行真正的批量更新操作 + scopedRepository.GetDbContext().UpdateRange(batch); await unitOfWork.CompleteAsync(); + + updateCount += batch.Length; } return updateCount; @@ -672,24 +669,6 @@ namespace YunDa.SOMS.Application.GeneralInformation try { using var unitOfWork = _unitOfWorkManager.Begin(); - - // 串行获取基础数据,避免 DbContext 并发访问 - Log4Helper.Info(GetType(), "开始获取设备数据..."); - var deviceDataList = await _imDeviceDatumRepository - .GetAllIncluding(t => t.Device) - .Where(d => d.DataName != "预留") // 提前过滤预留数据 - .ToListAsync(cancellationToken); - - - Log4Helper.Info(GetType(), "开始获取遥信数据..."); - var imDeviceYxList = await _imDeviceYxRepository.GetAll().ToListAsync(cancellationToken); - - Log4Helper.Info(GetType(), "开始获取告警分类数据..."); - var dmAlarmCategoriesList = await _dmalarmCategoryRepository.GetAll().ToListAsync(cancellationToken); - - Log4Helper.Info(GetType(), "开始获取设备信息数据..."); - var equipmentsList = await _equipmentInfoRepository.GetAll().ToListAsync(cancellationToken); - // 根据过滤条件获取保护装置信息 Log4Helper.Info(GetType(), "开始获取保护装置数据..."); var protectionDevicesQuery = _protectionDeviceInfoRepository.GetAll(); @@ -698,17 +677,46 @@ namespace YunDa.SOMS.Application.GeneralInformation protectionDevicesQuery = protectionDevicesQuery.Where(p => protectionDeviceIds.Contains(p.Id)); } var protectionDevicesList = await protectionDevicesQuery.ToListAsync(cancellationToken); + var protectionDeviceAddrList = protectionDevicesList.Select(t => t.ISMS_DeviceId); + var protectionDeviceEquipmentInfoIdList = protectionDevicesList.Select(t => t.EquipmentInfoId); + // 串行获取基础数据,避免 DbContext 并发访问 + Log4Helper.Info(GetType(), "开始获取设备数据..."); + var deviceDataList = await _imDeviceDatumRepository + .GetAllIncluding(t => t.Device) + .Where(t=> protectionDeviceAddrList.Contains(t.DeviceId)) + .Where(d => d.DataName != "预留"|| d.DataName != "未用") // 提前过滤预留数据 + .ToListAsync(cancellationToken); + + Log4Helper.Info(GetType(), "开始获取遥信数据..."); + var imDeviceYxList = await _imDeviceYxRepository.GetAll() + .ToListAsync(cancellationToken); + //imDeviceYxList = imDeviceYxList.Where(t=>) + imDeviceYxList = imDeviceYxList + .Where(device => protectionDeviceAddrList.Any(addr => device.Id.Contains(addr))) + .ToList(); + + Log4Helper.Info(GetType(), "开始获取告警分类数据..."); + var dmAlarmCategoriesList = await _dmalarmCategoryRepository.GetAll().ToListAsync(cancellationToken); + + Log4Helper.Info(GetType(), "开始获取设备信息数据..."); + var equipmentsList = await _equipmentInfoRepository.GetAll().ToListAsync(cancellationToken); + + // 获取配置数据 Log4Helper.Info(GetType(), "开始获取遥测配置数据..."); - var telemeteringConfigsList = await _telemeteringConfigurationRepository.GetAll() + var telemeteringConfigsList = _telemeteringConfigurationRepository.GetAll() .Where(t => t.EquipmentInfoId.HasValue) - .ToListAsync(cancellationToken); + .AsEnumerable() + .Where(t => protectionDeviceEquipmentInfoIdList.Any(x=>x==t.EquipmentInfoId)) + .ToList(); Log4Helper.Info(GetType(), "开始获取遥信配置数据..."); - var telesignalisationConfigsList = await _telesignalisationConfigurationRepository.GetAll() + var telesignalisationConfigsList = _telesignalisationConfigurationRepository.GetAll() .Where(t => t.EquipmentInfoId.HasValue) - .ToListAsync(cancellationToken); + .AsEnumerable() + .Where(t => protectionDeviceEquipmentInfoIdList.Any(x => x == t.EquipmentInfoId)) + .ToList(); // 构建字典(并行处理,安全因为操作的是内存数据) Log4Helper.Info(GetType(), "开始构建数据字典..."); diff --git a/src/YunDa.Application/YunDa.SOMS.Application/GeneralInformation/EquipmentLiveData/EquipmentLiveDataAppService.cs b/src/YunDa.Application/YunDa.SOMS.Application/GeneralInformation/EquipmentLiveData/EquipmentLiveDataAppService.cs index 4282e03..002cf47 100644 --- a/src/YunDa.Application/YunDa.SOMS.Application/GeneralInformation/EquipmentLiveData/EquipmentLiveDataAppService.cs +++ b/src/YunDa.Application/YunDa.SOMS.Application/GeneralInformation/EquipmentLiveData/EquipmentLiveDataAppService.cs @@ -421,7 +421,7 @@ namespace YunDa.SOMS.Application.GeneralInformation else { var datas = telemeterings - .Where(t => t.EquipmentInfoId == equipmentId && t.DataSourceCategory == (DataSourceCategoryEnum)dataSourceCategory) + .Where(t => (t.EquipmentInfoId == equipmentId || t.PrimaryEquipmentInfoId == equipmentId)) .Where(t => t.Name != "未用" || !t.Name.Contains("预留")) .OrderBy(t => t.DispatcherAddress) .ToList(); @@ -471,7 +471,7 @@ namespace YunDa.SOMS.Application.GeneralInformation else { var datas = telemeterings - .Where(t => t.EquipmentInfoId == equipmentId && t.DataSourceCategory == (DataSourceCategoryEnum)dataSourceCategory) + .Where(t => (t.EquipmentInfoId == equipmentId || t.PrimaryEquipmentInfoId == equipmentId) && t.DataSourceCategory == (DataSourceCategoryEnum)dataSourceCategory) .Where(t => t.Name != "未用" || !t.Name.Contains("预留")) .OrderBy(t => t.DispatcherAddress) .ToList(); @@ -1283,7 +1283,7 @@ namespace YunDa.SOMS.Application.GeneralInformation if (telemeterings != null && telemeterings.Count > 0) { var equipmentTelemeterings = telemeterings - .Where(t => t.EquipmentInfoId == equipmentId && t.DataSourceCategory == (DataSourceCategoryEnum)dataSourceCategory) + .Where(t =>( t.EquipmentInfoId == equipmentId||t.PrimaryEquipmentInfoId == equipmentId) && t.DataSourceCategory == (DataSourceCategoryEnum)dataSourceCategory) .Where(t => t.Name != "未用" && !t.Name.Contains("预留")) .ToList(); @@ -1311,7 +1311,7 @@ namespace YunDa.SOMS.Application.GeneralInformation if (telesignalisations != null && telesignalisations.Count > 0) { var equipmentTelesignalisations = telesignalisations - .Where(t => t.EquipmentInfoId == equipmentId && t.DataSourceCategory == (DataSourceCategoryEnum)dataSourceCategory) + .Where(t => (t.EquipmentInfoId == equipmentId||t.PrimaryEquipmentInfoId==equipmentId) && t.DataSourceCategory == (DataSourceCategoryEnum)dataSourceCategory) .Where(t => t.Name != "未用" && !t.Name.Contains("预留")) .ToList(); @@ -1386,7 +1386,7 @@ namespace YunDa.SOMS.Application.GeneralInformation if (telemeterings != null && telemeterings.Count > 0) { var equipmentTelemeterings = telemeterings - .Where(t => t.EquipmentInfoId == equipmentId && t.DataSourceCategory == (DataSourceCategoryEnum)dataSourceCategory) + .Where(t => (t.EquipmentInfoId == equipmentId || t.PrimaryEquipmentInfoId == equipmentId) && t.DataSourceCategory == (DataSourceCategoryEnum)dataSourceCategory) .Where(t => t.Name != "未用" && !t.Name.Contains("预留")) .Where(t => t.AlarmLevel > 0 && t.AlarmLevel != ConstantModel.DegaultValue) .ToList(); @@ -1468,7 +1468,8 @@ namespace YunDa.SOMS.Application.GeneralInformation var zxjckey = _telemeteringModelListRediskey + "ZXJC"; var zxjcdatas = await _telemeteringModelListRedis.HashSetGetAllAsync(zxjckey); - datas.AddRange(zzdatas.Where(t=>t.EquipmentInfoId == equipementInfo.Id)); + datas.AddRange(zzdatas.Where(t=>t.EquipmentInfoId == equipementInfo.Id + ||t.PrimaryEquipmentInfoId == equipementInfo.Id)); datas.AddRange(zxjcdatas.Where(t => t.EquipmentInfoId == equipementInfo.Id)); rst.ResultData = datas; rst.Flag = true; @@ -1482,7 +1483,7 @@ namespace YunDa.SOMS.Application.GeneralInformation } /// - /// 根据设备名称获取遥测 + /// 根据设备名称获取遥信 /// /// [HttpGet] @@ -1504,7 +1505,7 @@ namespace YunDa.SOMS.Application.GeneralInformation var zxjckey = _telesignalisationModelListRediskey + "ZXJC"; var zxjcdatas = await _telesignalisationModelListRedis.HashSetGetAllAsync(zxjckey); - datas.AddRange(zzdatas.Where(t => t.EquipmentInfoId == equipementInfo.Id)); + datas.AddRange(zzdatas.Where(t => t.EquipmentInfoId == equipementInfo.Id||t.PrimaryEquipmentInfoId == equipementInfo.Id)); datas.AddRange(zxjcdatas.Where(t => t.EquipmentInfoId == equipementInfo.Id)); rst.ResultData = datas; rst.Flag = true; diff --git a/src/YunDa.Application/YunDa.SOMS.Application/GeneralInformation/SettingAndFaultRpt/TransformInfomationAppService.cs b/src/YunDa.Application/YunDa.SOMS.Application/GeneralInformation/SettingAndFaultRpt/TransformInfomationAppService.cs index 2a06de6..e9f92d9 100644 --- a/src/YunDa.Application/YunDa.SOMS.Application/GeneralInformation/SettingAndFaultRpt/TransformInfomationAppService.cs +++ b/src/YunDa.Application/YunDa.SOMS.Application/GeneralInformation/SettingAndFaultRpt/TransformInfomationAppService.cs @@ -1,6 +1,7 @@ using Abp.Auditing; using Abp.Domain.Repositories; using Abp.Domain.Uow; +using Abp.EntityFrameworkCore; using Abp.Web.Mvc.Alerts; using Google.Protobuf.WellKnownTypes; using iText.Layout.Element; @@ -32,6 +33,7 @@ using YunDa.SOMS.Application.Core.Auditing; using YunDa.SOMS.Application.Core.Configuration; using YunDa.SOMS.Application.Core.Session; using YunDa.SOMS.Application.Core.SwaggerHelper; +using YunDa.SOMS.BASE.Entities; using YunDa.SOMS.BASE.Entities.Models; using YunDa.SOMS.DataTransferObject; using YunDa.SOMS.DataTransferObject.GeneralInformation.ProtectionDeviceInfoDto; @@ -56,6 +58,10 @@ namespace YunDa.SOMS.Application.GeneralInformation.SettingAndFaultRpt private readonly IRedisRepository _redisRepository; private readonly IRepository _protectionDeviceInfoRepository; private readonly IRepository _imProtectDevice; + private readonly IRepository _imDeviceDzDevice; + private readonly IRepository _imDztypeDevice; + private readonly IDbContextProvider _imDeviceDzenumProvider; + private readonly SOMSAuditingStore _SOMSAuditingStore; private string _ISMSGateWayIp = "http://127.0.0.1:38094"; private string _ISMSftpIp = "192.168.65.33"; @@ -67,8 +73,11 @@ namespace YunDa.SOMS.Application.GeneralInformation.SettingAndFaultRpt , IRedisRepository redisRepository , IAppServiceConfiguration appServiceConfiguration , IRepository imProtectDevice - , SOMSAuditingStore SOMSAuditingStore + , SOMSAuditingStore SOMSAuditingStore , IRepository protectionDeviceInfoRepository + , IRepository imDeviceDzDevice + , IRepository imDztypeDevice + , IDbContextProvider imDeviceDzenumProvider ) : base(sessionAppService) { _ISMSftpIp = appServiceConfiguration.ISMSFtpIp; @@ -82,6 +91,9 @@ namespace YunDa.SOMS.Application.GeneralInformation.SettingAndFaultRpt _protectionDeviceInfoRepository = protectionDeviceInfoRepository; _imProtectDevice = imProtectDevice; _SOMSAuditingStore = SOMSAuditingStore; + _imDeviceDzDevice = imDeviceDzDevice; + _imDztypeDevice = imDztypeDevice; + _imDeviceDzenumProvider = imDeviceDzenumProvider; } /// /// 转发定值行信息 @@ -91,7 +103,6 @@ namespace YunDa.SOMS.Application.GeneralInformation.SettingAndFaultRpt [HttpGet] [ShowApi] [AllowAnonymous] - [Audited] [Description("查询定值")] public async Task>> GetDZInfoAsync(string dz) { @@ -108,6 +119,8 @@ namespace YunDa.SOMS.Application.GeneralInformation.SettingAndFaultRpt List strings = dz.Split('|').ToList(); //CallDeviceDZ|B001|12|12|9|3|255 int dzAddr = int.Parse( strings[3]); + int dzCpuInex = int.Parse(strings[4]); + ImProtectDevice device = default; using (var unitOfWork = _unitOfWorkManager.Begin()) @@ -126,61 +139,91 @@ namespace YunDa.SOMS.Application.GeneralInformation.SettingAndFaultRpt UserName = string.IsNullOrEmpty(base.GetCurrentUser()?.UserName) ? "匿名用户" : base.GetCurrentUser()?.UserName, ExecutionTime = DateTime.Now, }); - // 先从Redis缓存中获取数据 + + // 定义缓存键 var cacheKey = "DZInfoHas"; - var cachedData = await _redisRepository.HashSetGetOneAsync(cacheKey, dz); + List dzList = null; + bool dataFromDevice = false; - if (cachedData != null) - { - // 处理 JSON 反序列化场景 - var json = JsonConvert.SerializeObject(cachedData); - var list = JsonConvert.DeserializeObject>(json); - rst.ResultData = list; - rst.Flag = true; - return rst; - } - - // 缓存中没有,从接口获取 + // 第一层:尝试从设备系统获取数据(主要数据源) + Log4Helper.Info(this.GetType(), $"尝试从设备系统获取定值,命令:{dz}"); string url = $"{_ISMSGateWayIp}/api"; - // 端口连通性检测(1秒超时) - if (!IsPortOpen(url, 5000)) + // 端口连通性检测 + if (IsPortOpen(url, 5000)) { - rst.Message = "目标服务不可访问,端口连接失败"; - Log4Helper.Warning(this.GetType(), $"端口连接失败,URL: {url}"); - return rst; + try + { + var str = HttpHelper.HttpGetRequest(url + $"?dz={dz}"); + JObject wrappedObject = JObject.Parse(str?.ToString()); + + if (wrappedObject != null) + { + var content = wrappedObject["content"]; + if (content != null) + { + dzList = JsonConvert.DeserializeObject>(content.ToString()); + if (dzList != null && dzList.Count > 0) + { + dataFromDevice = true; + Log4Helper.Info(this.GetType(), $"成功从设备系统获取定值,数量:{dzList.Count}"); + } + } + } + } + catch (Exception ex) + { + Log4Helper.Warning(this.GetType(), $"从设备系统获取定值失败,将尝试从缓存获取: {ex.Message}"); + } + } + else + { + Log4Helper.Warning(this.GetType(), $"设备系统端口连接失败,URL: {url},将尝试从缓存获取"); } - var str = HttpHelper.HttpGetRequest(url + $"?dz={dz}"); - JObject wrappedObject = JObject.Parse(str?.ToString()); - if (wrappedObject!=null) + // 第二层:如果设备系统获取失败,从Redis缓存获取(备用数据源) + if (dzList == null || dzList.Count == 0) { - var content = wrappedObject["content"]; - if (content != null) - { - var dzList = JsonConvert.DeserializeObject>(content.ToString()); - int zoneNo = int.Parse( dzList[0].ZoneNo.ToString()); + Log4Helper.Info(this.GetType(), $"尝试从Redis缓存获取定值,命令:{dz}"); + var cachedData = await _redisRepository.HashSetGetOneAsync(cacheKey, dz); - if (dzList!=null) + if (cachedData != null) + { + var json = JsonConvert.SerializeObject(cachedData); + dzList = JsonConvert.DeserializeObject>(json); + + if (dzList != null && dzList.Count > 0) { - // 写入Redis缓存,设置一个月过期时间 - await _redisRepository.HashSetUpdateOneAsync(cacheKey, dz, dzList); - await _redisRepository.KeyExpireAsync(cacheKey, TimeSpan.FromDays(30)); - rst.ResultData = dzList; - rst.Flag = true; - } - else - { - rst.Message = "未获取到定值"; - Log4Helper.Info(this.GetType(), $"定值查询命令:{dz},未获取到定值"); + Log4Helper.Info(this.GetType(), $"成功从缓存获取定值,数量:{dzList.Count}"); } } else { - rst.Message = "未获取到定值"; - Log4Helper.Info(this.GetType(), $"定值查询命令:{dz},未获取到定值"); + Log4Helper.Warning(this.GetType(), $"缓存中未找到定值数据,命令:{dz}"); } } + + // 处理获取到的数据 + if (dzList != null && dzList.Count > 0) + { + // 处理枚举值替换 + ProcessEnumerationValues(dzList, device.Id, dzCpuInex); + + // 如果数据来自设备系统,更新到Redis缓存(永久缓存,无过期时间) + if (dataFromDevice) + { + await _redisRepository.HashSetUpdateOneAsync(cacheKey, dz, dzList); + Log4Helper.Info(this.GetType(), $"已将设备系统数据缓存到Redis(永久缓存),命令:{dz}"); + } + + rst.ResultData = dzList; + rst.Flag = true; + } + else + { + rst.Message = "未获取到定值,设备系统和缓存均无数据"; + Log4Helper.Info(this.GetType(), $"定值查询失败,命令:{dz}"); + } } catch (Exception ex) @@ -192,6 +235,106 @@ namespace YunDa.SOMS.Application.GeneralInformation.SettingAndFaultRpt return rst; } + /// + /// 处理定值列表,将枚举类型的数值替换为中文描述 + /// + /// 定值信息列表 + /// 保护装置ID + private void ProcessEnumerationValues(List dzList, string deviceId, int dzCpuInex) + { + try + { + if (dzList == null || !dzList.Any() || string.IsNullOrEmpty(deviceId)) + { + return; + } + + // 获取该装置的所有定值配置 + List deviceDzList = null; + using (var unitOfWork = _unitOfWorkManager.Begin()) + { + deviceDzList = _imDeviceDzDevice.GetAll() + .Where(d => d.DeviceId == deviceId) + .Where(d=>d.CpuIndex == dzCpuInex) + .ToList(); + unitOfWork.Complete(); + } + + if (deviceDzList == null || !deviceDzList.Any()) + { + return; + } + + // 获取所有需要的枚举类型ID + var enumTypeIds = deviceDzList + .Where(d => d.EnumTypeId.HasValue && d.EnumTypeId.Value > 0) + .Select(d => d.EnumTypeId.Value) + .Distinct() + .ToList(); + + if (!enumTypeIds.Any()) + { + return; + } + + // 查询所有相关的枚举定义 + Dictionary> enumDefinitions = new Dictionary>(); + using (var unitOfWork = _unitOfWorkManager.Begin()) + { + var dbContext = _imDeviceDzenumProvider.GetDbContext(); + var allEnums = dbContext.Set() + .Where(e => enumTypeIds.Contains(e.EnumTypeId)) + .ToList(); + + // 按EnumTypeId分组 + foreach (var enumTypeId in enumTypeIds) + { + enumDefinitions[enumTypeId] = allEnums + .Where(e => e.EnumTypeId == enumTypeId) + .ToList(); + } + unitOfWork.Complete(); + } + + // 处理每个定值信息 + foreach (var dzInfo in dzList) + { + + // 查找对应的定值配置(通过DzIndex匹配SeqNo) + var deviceDz = deviceDzList.FirstOrDefault(d => d.DzComment == dzInfo.Name); + if (deviceDz == null || !deviceDz.EnumTypeId.HasValue || deviceDz.EnumTypeId.Value <= 0) + { + continue; + } + + // 解析当前值为数字 + if (!int.TryParse(dzInfo.Value, out int enumIndex)) + { + continue; + } + + // 查找对应的枚举定义 + if (!enumDefinitions.ContainsKey(deviceDz.EnumTypeId.Value)) + { + continue; + } + + var enumEntry = enumDefinitions[deviceDz.EnumTypeId.Value] + .FirstOrDefault(e => e.EnumIndex == enumIndex); + + if (enumEntry != null && !string.IsNullOrEmpty(enumEntry.EnumComment)) + { + // 替换数值为中文描述 + dzInfo.Value = enumEntry.EnumComment; + } + } + } + catch (Exception ex) + { + Log4Helper.Warning(this.GetType(), $"处理定值枚举值时发生错误: {ex.Message}", ex); + // 不抛出异常,保持原有数据返回 + } + } /// /// 根据设备id查询定值 /// @@ -231,11 +374,11 @@ namespace YunDa.SOMS.Application.GeneralInformation.SettingAndFaultRpt if (device!=null) { - string search = $"CallDeviceDZ|B001|{device.DeviceAddr}|{device.GateWay.PhyAddr}|9|3|{actualZoneNo}"; + string search = $"CallDeviceDZ|B001|{device.GateWay.PhyAddr}|{device.DeviceAddr}|9|3|{actualZoneNo}"; var data1 = await GetDZInfoAsync(search); rst.ResultData.SysDz = data1.ResultData; - search = $"CallDeviceDZ|B001|{device.DeviceAddr}|{device.GateWay.PhyAddr}|1|3|{actualZoneNo}"; + search = $"CallDeviceDZ|B001|{device.GateWay.PhyAddr}|{device.DeviceAddr}|1|3|{actualZoneNo}"; var data2 = await GetDZInfoAsync(search); rst.ResultData.UserDz = data2.ResultData; rst.ResultData.DeviceName = device.DeviceName; @@ -277,7 +420,7 @@ namespace YunDa.SOMS.Application.GeneralInformation.SettingAndFaultRpt unitOfWork.Complete(); } - if (cachedData != null) + if (cachedData != null&&cachedData.Count>0) { foreach (var item in cachedData.Keys) { @@ -325,7 +468,10 @@ namespace YunDa.SOMS.Application.GeneralInformation.SettingAndFaultRpt rst.Flag = true; return rst; } + else + { + } } diff --git a/src/YunDa.Application/YunDa.SOMS.Application/GeneralInformation/TransformerSubstation/TransformSubstationExportDataAppService.cs b/src/YunDa.Application/YunDa.SOMS.Application/GeneralInformation/TransformerSubstation/TransformSubstationExportDataAppService.cs index f016b1e..cf16e96 100644 --- a/src/YunDa.Application/YunDa.SOMS.Application/GeneralInformation/TransformerSubstation/TransformSubstationExportDataAppService.cs +++ b/src/YunDa.Application/YunDa.SOMS.Application/GeneralInformation/TransformerSubstation/TransformSubstationExportDataAppService.cs @@ -1,37 +1,39 @@ using Abp.Application.Services; using Abp.Auditing; using Abp.Domain.Repositories; +using Abp.EntityFrameworkCore.Repositories; +using Abp.Linq.Extensions; +using Abp.ObjectMapping; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using System; +using System.Collections.Generic; using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Linq.Dynamic.Core; +using System.Text; using System.Threading.Tasks; -using YunDa.SOMS.DataTransferObject.GeneralInformation.TransformerSubstationDto; +using ToolLibrary.LogHelper; +using YunDa.SOMS.Application.Core; +using YunDa.SOMS.Application.Core.Helper.PDFHelper; +using YunDa.SOMS.Application.Core.OfficeHelper; +using YunDa.SOMS.Application.Core.Session; +using YunDa.SOMS.Application.Core.SwaggerHelper; using YunDa.SOMS.DataTransferObject; +using YunDa.SOMS.DataTransferObject.DataMonitoring.TelecommandConfigurationDto; +using YunDa.SOMS.DataTransferObject.DataMonitoring.TelemeteringConfigurationDto; +using YunDa.SOMS.DataTransferObject.DataMonitoring.TelesignalisationConfigurationDto; +using YunDa.SOMS.DataTransferObject.GeneralInformation.EquipmentTypeDto; +using YunDa.SOMS.DataTransferObject.GeneralInformation.TransformerSubstationDto; using YunDa.SOMS.Entities.DataMonitoring; using YunDa.SOMS.Entities.GeneralInformation; using YunDa.SOMS.Entities.MySQL.DataMonitoring; using YunDa.SOMS.Entities.VideoSurveillance; -using YunDa.SOMS.DataTransferObject.GeneralInformation.EquipmentTypeDto; -using System.Collections.Generic; -using ToolLibrary.LogHelper; -using YunDa.SOMS.Application.Core.SwaggerHelper; -using YunDa.SOMS.Application.Core.Helper.PDFHelper; -using System.Linq.Dynamic.Core; -using System.Linq; -using System.IO; -using YunDa.SOMS.Application.Core.OfficeHelper; -using Microsoft.AspNetCore.Http; -using YunDa.SOMS.DataTransferObject.DataMonitoring.TelemeteringConfigurationDto; -using YunDa.SOMS.DataTransferObject.DataMonitoring.TelesignalisationConfigurationDto; -using YunDa.SOMS.DataTransferObject.DataMonitoring.TelecommandConfigurationDto; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Text; -using Microsoft.EntityFrameworkCore; -using Abp.ObjectMapping; -using YunDa.SOMS.Application.Core; -using YunDa.SOMS.Application.Core.Session; -using Abp.Linq.Extensions; +using static NetMQ.NetMQSelector; namespace YunDa.SOMS.Application.GeneralInformation { @@ -164,10 +166,11 @@ namespace YunDa.SOMS.Application.GeneralInformation ExcelHandler excelWrite = new DataConfigurationExcelHandle(); try { - var query = _telemeteringConfigurationRepository.GetAllIncluding(t => t.EquipmentType, t => t.TransformerSubstation, t => t.EquipmentInfo, t => t.PrimaryEquipmentInfo, t => t.PrimaryEquipmentInfo.EquipmentType) + var query = _telemeteringConfigurationRepository.GetAllIncluding(t => t.TransformerSubstation, t => t.EquipmentInfo, t => t.PrimaryEquipmentInfo) .Where(t => t.IsActive && t.TransformerSubstationId == input.TransformerSubstationId) - .WhereIf(input.DataSourceCategory.HasValue, t => t.DataSourceCategory == input.DataSourceCategory); - + .WhereIf(input.DataSourceCategory.HasValue, t => t.DataSourceCategory == input.DataSourceCategory) + .AsNoTracking(); + var equipmentTypes = _equipmentTypeRepository.GetAll().ToDictionary(t=>t.Id); // 根据 EquipmentInfoId 和 EquipmentTypeId 进行筛选 if (input.EquipmentInfoId.HasValue) { @@ -182,6 +185,7 @@ namespace YunDa.SOMS.Application.GeneralInformation } var telemeteringRepo = query.OrderBy(t => t.SeqNo).ToList(); + var telemeteringExcelList = ObjectMapper.Map>(telemeteringRepo); @@ -202,14 +206,35 @@ namespace YunDa.SOMS.Application.GeneralInformation foreach (var item in telemeteringExcelList) { + var PrimaryEquipmentInfoTypeName = ""; + var EquipmentTypeName = ""; + + if (item.PrimaryEquipmentInfo!=null) + { + if (equipmentTypes.ContainsKey(item.PrimaryEquipmentInfo.EquipmentTypeId.Value)) + { + var equipmentType = equipmentTypes[item.PrimaryEquipmentInfo.EquipmentTypeId.Value]; + PrimaryEquipmentInfoTypeName = equipmentType.Name; + } + + } + if (item.EquipmentInfo != null) + { + if (equipmentTypes.ContainsKey(item.EquipmentInfo.EquipmentTypeId.Value)) + { + var equipmentType = equipmentTypes[item.EquipmentInfo.EquipmentTypeId.Value]; + EquipmentTypeName = equipmentType.Name; + } + } + var row = new List { item.Id?.ToString() ?? "", item.SeqNo.ToString(), item.EquipmentInfo?.Name ?? "", - item.EquipmentType?.Name ?? "", + EquipmentTypeName, item.PrimaryEquipmentInfo?.Name ?? "", - item.PrimaryEquipmentInfoType?.Name ?? "", + PrimaryEquipmentInfoTypeName, item.Name ?? "", item.InfoAddress.ToString(), item.InfoDeviceAddress.ToString(), @@ -282,6 +307,10 @@ namespace YunDa.SOMS.Application.GeneralInformation var equipmentRepo = _equipmentInfoRepository.GetAll().ToList(); var equipmentTypeRepo = _equipmentTypeRepository.GetAll().ToList(); + // 使用批量操作优化性能 + var configsToUpdate = new List(); + var configsToInsert = new List(); + foreach (JToken result in results) { var item = result.ToObject(); @@ -309,7 +338,7 @@ namespace YunDa.SOMS.Application.GeneralInformation { // 更新现有配置 UpdateTelemeteringConfiguration(existingConfig, item, result, equipmentRepo, equipmentTypeRepo); - await _telemeteringConfigurationRepository.UpdateAsync(existingConfig); + configsToUpdate.Add(existingConfig); } else { @@ -317,12 +346,43 @@ namespace YunDa.SOMS.Application.GeneralInformation var newConfig = CreateTelemeteringConfiguration(item, result, id, equipmentRepo, equipmentTypeRepo); if (newConfig != null) { - await _telemeteringConfigurationRepository.InsertAsync(newConfig); + configsToInsert.Add(newConfig); } } } + // 批量处理更新和插入操作 + const int batchSize = 1000; + int totalProcessed = 0; + + // 批量更新 + if (configsToUpdate.Any()) + { + for (int i = 0; i < configsToUpdate.Count; i += batchSize) + { + var batch = configsToUpdate.Skip(i).Take(batchSize).ToList(); + _telemeteringConfigurationRepository.GetDbContext().UpdateRange(batch); + await CurrentUnitOfWork.SaveChangesAsync(); + totalProcessed += batch.Count; + } + Log4Helper.Info(this.GetType(), $"遥测数据导入服务 - 批量更新了 {configsToUpdate.Count} 条记录"); + } + + // 批量插入 + if (configsToInsert.Any()) + { + for (int i = 0; i < configsToInsert.Count; i += batchSize) + { + var batch = configsToInsert.Skip(i).Take(batchSize).ToList(); + await _telemeteringConfigurationRepository.GetDbContext().AddRangeAsync(batch); + await CurrentUnitOfWork.SaveChangesAsync(); + totalProcessed += batch.Count; + } + Log4Helper.Info(this.GetType(), $"遥测数据导入服务 - 批量插入了 {configsToInsert.Count} 条记录"); + } + requestEasyResult.Flag = true; + requestEasyResult.Message = $"成功处理 {totalProcessed} 条记录(更新: {configsToUpdate.Count}, 插入: {configsToInsert.Count})"; } catch (Exception ex) { @@ -353,6 +413,7 @@ namespace YunDa.SOMS.Application.GeneralInformation var query = _telesignalisationConfigurationRepository.GetAllIncluding(t => t.EquipmentType, t => t.TransformerSubstation, t => t.EquipmentInfo, t => t.PrimaryEquipmentInfo, t => t.PrimaryEquipmentInfo.EquipmentType) .Where(t => t.IsActive && t.TransformerSubstationId == input.TransformerSubstationId) .WhereIf(input.DataSourceCategory.HasValue, t => t.DataSourceCategory == input.DataSourceCategory); + var equipmentTypes = _equipmentTypeRepository.GetAll().ToDictionary(t => t.Id); // 根据 EquipmentInfoId 和 EquipmentTypeId 进行筛选 if (input.EquipmentInfoId.HasValue) @@ -385,9 +446,30 @@ namespace YunDa.SOMS.Application.GeneralInformation ClumnWidthList = new List { 36, 8, 15, 12, 15, 15, 15, 10, 10, 10, 12, 10, 10, 10, 10, 10, 10, 10, 8, 10, 10 }, AutoClumnWidths = false }; - + foreach (var item in telesignalisationExcelList) { + + var PrimaryEquipmentInfoTypeName = ""; + var EquipmentTypeName = ""; + + if (item.PrimaryEquipmentInfo != null) + { + if (equipmentTypes.ContainsKey(item.PrimaryEquipmentInfo.EquipmentTypeId.Value)) + { + var equipmentType = equipmentTypes[item.PrimaryEquipmentInfo.EquipmentTypeId.Value]; + PrimaryEquipmentInfoTypeName = equipmentType.Name; + } + + } + if (item.EquipmentInfo != null) + { + if (equipmentTypes.ContainsKey(item.EquipmentInfo.EquipmentTypeId.Value)) + { + var equipmentType = equipmentTypes[item.EquipmentInfo.EquipmentTypeId.Value]; + EquipmentTypeName = equipmentType.Name; + } + } var row = new List { item.Id?.ToString() ?? "", @@ -470,6 +552,10 @@ namespace YunDa.SOMS.Application.GeneralInformation var equipmentTypeRepo = _equipmentTypeRepository.GetAll().ToList(); var alarmCategoryRepo = _dmAlarmCategoryRepo.GetAll().Where(a => a.IsActive).ToList(); + // 使用批量操作优化性能 + var configsToUpdate = new List(); + var configsToInsert = new List(); + foreach (JToken result in results) { var item = result.ToObject(); @@ -497,7 +583,7 @@ namespace YunDa.SOMS.Application.GeneralInformation { // 更新现有配置 UpdateTelesignalisationConfiguration(existingConfig, item, result, equipmentRepo, equipmentTypeRepo, alarmCategoryRepo); - await _telesignalisationConfigurationRepository.UpdateAsync(existingConfig); + configsToUpdate.Add(existingConfig); } else { @@ -505,12 +591,43 @@ namespace YunDa.SOMS.Application.GeneralInformation var newConfig = CreateTelesignalisationConfiguration(item, result, id, equipmentRepo, equipmentTypeRepo, alarmCategoryRepo); if (newConfig != null) { - await _telesignalisationConfigurationRepository.InsertAsync(newConfig); + configsToInsert.Add(newConfig); } } } + // 批量处理更新和插入操作 + const int batchSize = 1000; + int totalProcessed = 0; + + // 批量更新 + if (configsToUpdate.Any()) + { + for (int i = 0; i < configsToUpdate.Count; i += batchSize) + { + var batch = configsToUpdate.Skip(i).Take(batchSize).ToList(); + _telesignalisationConfigurationRepository.GetDbContext().UpdateRange(batch); + await CurrentUnitOfWork.SaveChangesAsync(); + totalProcessed += batch.Count; + } + Log4Helper.Info(this.GetType(), $"遥信数据导入服务 - 批量更新了 {configsToUpdate.Count} 条记录"); + } + + // 批量插入 + if (configsToInsert.Any()) + { + for (int i = 0; i < configsToInsert.Count; i += batchSize) + { + var batch = configsToInsert.Skip(i).Take(batchSize).ToList(); + await _telesignalisationConfigurationRepository.GetDbContext().AddRangeAsync(batch); + await CurrentUnitOfWork.SaveChangesAsync(); + totalProcessed += batch.Count; + } + Log4Helper.Info(this.GetType(), $"遥信数据导入服务 - 批量插入了 {configsToInsert.Count} 条记录"); + } + requestEasyResult.Flag = true; + requestEasyResult.Message = $"成功处理 {totalProcessed} 条记录(更新: {configsToUpdate.Count}, 插入: {configsToInsert.Count})"; } catch (Exception ex) { @@ -639,6 +756,10 @@ namespace YunDa.SOMS.Application.GeneralInformation var equipmentRepo = _equipmentInfoRepository.GetAll().ToList(); var equipmentTypeRepo = _equipmentTypeRepository.GetAll().ToList(); + // 使用批量操作优化性能 + var configsToUpdate = new List(); + var configsToInsert = new List(); + foreach (JToken result in results) { var item = result.ToObject(); @@ -654,7 +775,7 @@ namespace YunDa.SOMS.Application.GeneralInformation { // 更新现有配置 UpdateTelecommandConfiguration(existingConfig, item, result, equipmentRepo, equipmentTypeRepo); - await _telecommandConfigurationRepository.UpdateAsync(existingConfig); + configsToUpdate.Add(existingConfig); } else { @@ -662,12 +783,43 @@ namespace YunDa.SOMS.Application.GeneralInformation var newConfig = CreateTelecommandConfiguration(item, result, id, equipmentRepo, equipmentTypeRepo); if (newConfig != null) { - await _telecommandConfigurationRepository.InsertAsync(newConfig); + configsToInsert.Add(newConfig); } } } + // 批量处理更新和插入操作 + const int batchSize = 1000; + int totalProcessed = 0; + + // 批量更新 + if (configsToUpdate.Any()) + { + for (int i = 0; i < configsToUpdate.Count; i += batchSize) + { + var batch = configsToUpdate.Skip(i).Take(batchSize).ToList(); + _telecommandConfigurationRepository.GetDbContext().UpdateRange(batch); + await CurrentUnitOfWork.SaveChangesAsync(); + totalProcessed += batch.Count; + } + Log4Helper.Info(this.GetType(), $"遥控数据导入服务 - 批量更新了 {configsToUpdate.Count} 条记录"); + } + + // 批量插入 + if (configsToInsert.Any()) + { + for (int i = 0; i < configsToInsert.Count; i += batchSize) + { + var batch = configsToInsert.Skip(i).Take(batchSize).ToList(); + await _telecommandConfigurationRepository.GetDbContext().AddRangeAsync(batch); + await CurrentUnitOfWork.SaveChangesAsync(); + totalProcessed += batch.Count; + } + Log4Helper.Info(this.GetType(), $"遥控数据导入服务 - 批量插入了 {configsToInsert.Count} 条记录"); + } + requestEasyResult.Flag = true; + requestEasyResult.Message = $"成功处理 {totalProcessed} 条记录(更新: {configsToUpdate.Count}, 插入: {configsToInsert.Count})"; } catch (Exception ex) { @@ -815,6 +967,10 @@ namespace YunDa.SOMS.Application.GeneralInformation config.PrimaryEquipmentInfoId = primaryEquipment.Id; } } + else + { + config.PrimaryEquipmentInfoId = null; + } // 设置数据来源 if (Enum.TryParse(item.DataSourceCategory.ToString(), out var dataSource)) @@ -859,7 +1015,7 @@ namespace YunDa.SOMS.Application.GeneralInformation // 设置设备1信息 var primaryEquipmentName = result["关联设备1"]?.ToString(); - var primaryEquipmentTypeName = result["关联类型设备1"]?.ToString(); + var primaryEquipmentTypeName = result["关联设备类型1"]?.ToString(); if (!string.IsNullOrEmpty(primaryEquipmentName)) { @@ -873,7 +1029,11 @@ namespace YunDa.SOMS.Application.GeneralInformation existingConfig.PrimaryEquipmentInfoId = primaryEquipment.Id; } } - + + } + else + { + existingConfig.PrimaryEquipmentInfoId = null; } // 设置数据来源 diff --git a/src/YunDa.Application/YunDa.SOMS.DataTransferObject/DataMonitoring/SecondaryCircuitInspection/Output/SecondaryCircuitInspectionResultDetailOutput.cs b/src/YunDa.Application/YunDa.SOMS.DataTransferObject/DataMonitoring/SecondaryCircuitInspection/Output/SecondaryCircuitInspectionResultDetailOutput.cs index bbe67da..1fbcd4a 100644 --- a/src/YunDa.Application/YunDa.SOMS.DataTransferObject/DataMonitoring/SecondaryCircuitInspection/Output/SecondaryCircuitInspectionResultDetailOutput.cs +++ b/src/YunDa.Application/YunDa.SOMS.DataTransferObject/DataMonitoring/SecondaryCircuitInspection/Output/SecondaryCircuitInspectionResultDetailOutput.cs @@ -94,7 +94,7 @@ namespace YunDa.SOMS.DataTransferObject.DataMonitoring.SecondaryCircuitInspectio /// - /// AI分析结果(支持层级/树形结构数据) + /// AI分析结果 正常|异常 /// public string AIAnalysisResult { get; set; } @@ -392,25 +392,18 @@ namespace YunDa.SOMS.DataTransferObject.DataMonitoring.SecondaryCircuitInspectio /// public Guid? InspectionItemId { get; set; } - /// - /// 模块类型ID - /// - public Guid? ModuleTypeId { get; set; } - /// - /// 结果状态 - /// - public SecondaryCircuitInspectionResultStatus? ResultStatus { get; set; } + public string ModuleTypeName { get; set; } + public string SecondaryCircuitInspectionItemName { get; set; } + + public string ResultStatusName { get; set; } + /// /// 触发方式 /// public SecondaryCircuitInspectionPlanType? TriggerType { get; set; } - /// - /// 是否异常 - /// - public bool? IsAbnormal { get; set; } } /// @@ -456,6 +449,11 @@ namespace YunDa.SOMS.DataTransferObject.DataMonitoring.SecondaryCircuitInspectio /// public class DailyReportOutput { + /// + /// 报告ID + /// + public string Id { get; set; } + /// /// 总巡检项数 /// @@ -475,6 +473,11 @@ namespace YunDa.SOMS.DataTransferObject.DataMonitoring.SecondaryCircuitInspectio /// 处置完成率 /// public double DisposalCompletionRate { get; set; } + /// + /// AI处理结果 + /// + public string ResultHandleDescription { get; set; } + /// /// 异常维度:按照父级类型进行统计 @@ -519,6 +522,11 @@ namespace YunDa.SOMS.DataTransferObject.DataMonitoring.SecondaryCircuitInspectio /// public class WeeklyReportOutput { + /// + /// 报告ID + /// + public string Id { get; set; } + /// /// 趋势维度 /// @@ -529,11 +537,11 @@ namespace YunDa.SOMS.DataTransferObject.DataMonitoring.SecondaryCircuitInspectio /// public List DistributionDimension { get; set; } + /// - /// 周例会决策建议 + /// AI处理结果 /// - public string WeeklyMeetingRecommendation { get; set; } - + public string ResultHandleDescription { get; set; } /// /// 高频重复异常点 /// @@ -543,7 +551,7 @@ namespace YunDa.SOMS.DataTransferObject.DataMonitoring.SecondaryCircuitInspectio { TrendDimension = new WeeklyTrendData(); DistributionDimension = new List(); - WeeklyMeetingRecommendation = "请调用AI分析"; + HighFrequencyRepeatAbnormalPoints = new List(); } } @@ -647,6 +655,11 @@ namespace YunDa.SOMS.DataTransferObject.DataMonitoring.SecondaryCircuitInspectio /// public class MonthlyReportOutput { + /// + /// 报告ID + /// + public string Id { get; set; } + /// /// 月度故障概率趋势:每月第一周到最后一周的每周故障概率 /// @@ -657,10 +670,7 @@ namespace YunDa.SOMS.DataTransferObject.DataMonitoring.SecondaryCircuitInspectio /// public List AbnormalTypeDistribution { get; set; } - /// - /// 月例会决策建议 - /// - public string MonthlyMeetingRecommendation { get; set; } + /// /// 高频重复异常点 @@ -668,9 +678,9 @@ namespace YunDa.SOMS.DataTransferObject.DataMonitoring.SecondaryCircuitInspectio public List HighFrequencyRepeatAbnormalPoints { get; set; } /// - /// AI预测建议 + /// AI处理结果 /// - public string AIPredictionRecommendation { get; set; } + public string ResultHandleDescription { get; set; } /// /// 月度关键指标对比 @@ -681,9 +691,9 @@ namespace YunDa.SOMS.DataTransferObject.DataMonitoring.SecondaryCircuitInspectio { MonthlyFaultProbabilityTrend = new List(); AbnormalTypeDistribution = new List(); - MonthlyMeetingRecommendation = "请调用AI分析"; + HighFrequencyRepeatAbnormalPoints = new List(); - AIPredictionRecommendation = "请调用AI分析"; + MonthlyKeyIndicatorComparison = new List(); } } diff --git a/src/YunDa.Application/YunDa.SOMS.DataTransferObject/YunDa.SOMS.DataTransferObject.xml b/src/YunDa.Application/YunDa.SOMS.DataTransferObject/YunDa.SOMS.DataTransferObject.xml index 8a96a79..3c2502b 100644 --- a/src/YunDa.Application/YunDa.SOMS.DataTransferObject/YunDa.SOMS.DataTransferObject.xml +++ b/src/YunDa.Application/YunDa.SOMS.DataTransferObject/YunDa.SOMS.DataTransferObject.xml @@ -6842,7 +6842,7 @@ - AI分析结果(支持层级/树形结构数据) + AI分析结果 正常|异常 @@ -7110,26 +7110,11 @@ 巡检项ID - - - 模块类型ID - - - - - 结果状态 - - 触发方式 - - - 是否异常 - - 创建处置过程记录输入DTO @@ -7170,6 +7155,11 @@ 日报输出DTO + + + 报告ID + + 总巡检项数 @@ -7190,6 +7180,11 @@ 处置完成率 + + + AI处理结果 + + 异常维度:按照父级类型进行统计 @@ -7225,6 +7220,11 @@ 周报输出DTO + + + 报告ID + + 趋势维度 @@ -7235,9 +7235,9 @@ 分布维度:按照父级类型统计 - + - 周例会决策建议 + AI处理结果 @@ -7335,6 +7335,11 @@ 月报输出DTO + + + 报告ID + + 月度故障概率趋势:每月第一周到最后一周的每周故障概率 @@ -7345,19 +7350,14 @@ 异常类型占比:按照父级类型统计 - - - 月例会决策建议 - - 高频重复异常点 - + - AI预测建议 + AI处理结果 diff --git a/src/YunDa.Application/YunDa.SOMS.MongoDB.Application/Inspection/InspectionItemResult/InspectionItemResultAppService.cs b/src/YunDa.Application/YunDa.SOMS.MongoDB.Application/Inspection/InspectionItemResult/InspectionItemResultAppService.cs index b17eacd..1184bab 100644 --- a/src/YunDa.Application/YunDa.SOMS.MongoDB.Application/Inspection/InspectionItemResult/InspectionItemResultAppService.cs +++ b/src/YunDa.Application/YunDa.SOMS.MongoDB.Application/Inspection/InspectionItemResult/InspectionItemResultAppService.cs @@ -71,6 +71,8 @@ namespace YunDa.SOMS.MongoDB.Application.Inspection private readonly IRepository _patternRecognitionConfigutrationRepository; private readonly IRepository _alarmCategoryRepository; private readonly IRepository _presetPointRepository; + private readonly IRepository _equipmentInfoRepository; + private readonly IRepository _telesignalisationConfigurationRepository; private readonly IRepository _mtpRepository; @@ -121,6 +123,7 @@ namespace YunDa.SOMS.MongoDB.Application.Inspection IMongoDbRepository bsonDocumentResultRepository, IRepository telesignalisationConfigurationRepository, IRepository videoDevRepository, + IRepository equipmentInfoRepository, ISessionAppService sessionAppService) : base(sessionAppService, appServiceConfiguration) { @@ -148,6 +151,7 @@ namespace YunDa.SOMS.MongoDB.Application.Inspection _telesignalisationConfigurationRepository = telesignalisationConfigurationRepository; _allTimeImageRepository = allTimeImageRepository; _videoDevRepository = videoDevRepository; + _equipmentInfoRepository = equipmentInfoRepository; _bsonDocumentResultRepository = bsonDocumentResultRepository; } #region 巡检结果编辑 @@ -492,13 +496,25 @@ namespace YunDa.SOMS.MongoDB.Application.Inspection var f = builder.Lte("AnalysisTime", date);//< filters.Add(f); } + else + { + DateTime date = DateTime.Now; + var f = builder.Lte("AnalysisTime", date);//< + filters.Add(f); + } if (searchCondition.MinTime.HasValue) { DateTime date = Convert.ToDateTime(searchCondition.MinTime); var f = builder.Gte("AnalysisTime", date);//< filters.Add(f); } - if (filters.Count>0) + else + { + DateTime date = DateTime.Now.Date - TimeSpan.FromDays(5); + var f = builder.Gte("AnalysisTime", date);//< + filters.Add(f); + } + if (filters.Count > 0) { var filter = builder.And(filters); var sorter = Builders.Sort; @@ -512,19 +528,19 @@ namespace YunDa.SOMS.MongoDB.Application.Inspection { foreach (var preset in presetRepo) { - if (data.VideoDevId== preset.VideoDevId&&data.PresetPointNumber == preset.Number) + if (data.VideoDevId == preset.VideoDevId && data.PresetPointNumber == preset.Number) { list.Add(data); } } } - + rst.ResultData = ObjectMapper.Map>(list); //获取条件下所有数量 rst.TotalCount = rst.ResultData.Count; rst.Flag = true; } - + } } @@ -551,6 +567,11 @@ namespace YunDa.SOMS.MongoDB.Application.Inspection try { var prserPoints = _presetPointRepository.GetAll().ToList(); + var equipment = _equipmentInfoRepository.GetAll().FirstOrDefault(t => t.Id == searchCondition.EquipmentInfoId); + if (equipment == null) + { + return rst; + } var cameras = _videoDevRepository.GetAll().ToList(); var builder = Builders.Filter; @@ -562,12 +583,24 @@ namespace YunDa.SOMS.MongoDB.Application.Inspection var f = builder.Lte("Time", date);//< filters.Add(f); } + else + { + DateTime date = DateTime.Now; + var f = builder.Lte("Time", date);//< + filters.Add(f); + } if (searchCondition.MinTime.HasValue) { DateTime date = Convert.ToDateTime(searchCondition.MinTime); var f = builder.Gte("Time", date);//< filters.Add(f); } + else + { + DateTime date = DateTime.Now.Date - TimeSpan.FromDays(5); + var f = builder.Gte("Time", date);//< + filters.Add(f); + } var sorter = Builders.Sort; SortDefinition sortDefinition = sorter.Descending("Time"); _bsonDocumentResultRepository.CollectionName = nameof(OriginalInspectionStoreResult); @@ -583,9 +616,9 @@ namespace YunDa.SOMS.MongoDB.Application.Inspection // 创建查找字典优化性能 var cameraDict = cameras.ToDictionary(c => c.DevName); var presetDict = prserPoints.GroupBy(p => p.VideoDevId) - .ToDictionary(g => g.Key, - g => g.ToDictionary(p => p.Number)); - + .ToDictionary(g => g.Key, + g => g.DistinctBy(p => p.Number) + .ToDictionary(p => p.Number)); foreach (var data in originalData) { // 1. 查找摄像头 @@ -600,25 +633,49 @@ namespace YunDa.SOMS.MongoDB.Application.Inspection if (!presetDict.TryGetValue(camera.Id, out var devicePresets) || !devicePresets.TryGetValue(presetCode, out var preset)) continue; - - // 4. 检查设备ID - if (preset.EquipmentInfoId != searchCondition.EquipmentInfoId) - continue; + if (preset.EquipmentInfoId == null) + { + // 5. 设备名称匹配检查(60%相似度) + bool isNameMatch = false; - // 5. 构建结果对象 + if (data.Result.Contains(equipment.Name)) + { + // 直接包含则通过 + isNameMatch = true; + } + else + { + // 计算相似度(60%阈值) + double similarity = CalculateSimilarity(data.Result, equipment.Name); + if (similarity >= 0.8) + { + isNameMatch = true; + } + } + + if (!isNameMatch) + continue; + } + else + { + // 4. 检查设备ID + if (preset.EquipmentInfoId != searchCondition.EquipmentInfoId) + continue; + } + + + + + // 6. 构建结果对象 var output = new InspectionItemResultOutput { VideoDevName = data.CamName, - PresetPointNumber = presetCode, AnalysisTime = DateTime.Parse(data.CaptureTime), AnalysisStatus = int.Parse(data.Status), AnalysisMessage = data.Message, AnalysisResult = data.Result, SeqNo = int.Parse(data.Seq), - VideoDevId = camera.Id, - PresetPointName = preset.Name, - //AliasPath = "" - RelativePath =string.IsNullOrEmpty( data.RelativePath)?"\\": data.RelativePath, + RelativePath = string.IsNullOrEmpty(data.RelativePath) ? "\\" : data.RelativePath, BasePath = string.IsNullOrEmpty(data.BasePath) ? "\\" : data.BasePath, InspectionItemFiles = data.PictureNames.Select(t => new AttachmentFile { @@ -627,10 +684,11 @@ namespace YunDa.SOMS.MongoDB.Application.Inspection FileType = 0 }) }; - rst.ResultData.Add(output); } - rst.Flag = true; + + + rst.Flag = true; } catch (Exception ex) @@ -641,7 +699,50 @@ namespace YunDa.SOMS.MongoDB.Application.Inspection } return rst; } + // 相似度计算方法(选择以下任一种) + private double CalculateSimilarity(string source, string target) + { + if (string.IsNullOrEmpty(source) || string.IsNullOrEmpty(target)) + return 0; + // 方法1: 简单的子串匹配比例 + int matchCount = 0; + foreach (char c in target) + { + if (source.Contains(c)) + matchCount++; + } + return (double)matchCount / target.Length; + + // 方法2: Levenshtein距离(更精确,推荐) + // int distance = LevenshteinDistance(source, target); + // int maxLength = Math.Max(source.Length, target.Length); + // return 1.0 - (double)distance / maxLength; + } + + // Levenshtein距离算法(可选,更精确) + private int LevenshteinDistance(string source, string target) + { + int n = source.Length; + int m = target.Length; + int[,] d = new int[n + 1, m + 1]; + + if (n == 0) return m; + if (m == 0) return n; + + for (int i = 0; i <= n; i++) d[i, 0] = i; + for (int j = 0; j <= m; j++) d[0, j] = j; + + for (int i = 1; i <= n; i++) + { + for (int j = 1; j <= m; j++) + { + int cost = (target[j - 1] == source[i - 1]) ? 0 : 1; + d[i, j] = Math.Min(Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1), d[i - 1, j - 1] + cost); + } + } + return d[n, m]; + } [HttpPost] [UnitOfWork(true)] [ShowApi] diff --git a/src/YunDa.Application/YunDa.SOMS.MongoDB.Application/Inspection/SecondaryCircuitInspectionResult/SecondaryCircuitInspectionResultAppService.cs b/src/YunDa.Application/YunDa.SOMS.MongoDB.Application/Inspection/SecondaryCircuitInspectionResult/SecondaryCircuitInspectionResultAppService.cs index 34bac75..46edae6 100644 --- a/src/YunDa.Application/YunDa.SOMS.MongoDB.Application/Inspection/SecondaryCircuitInspectionResult/SecondaryCircuitInspectionResultAppService.cs +++ b/src/YunDa.Application/YunDa.SOMS.MongoDB.Application/Inspection/SecondaryCircuitInspectionResult/SecondaryCircuitInspectionResultAppService.cs @@ -52,7 +52,10 @@ namespace YunDa.SOMS.MongoDB.Application.DataMonitoring.SecondaryCircuitInspecti private readonly IMongoDatabase _mongoDatabase; private readonly IRepository _inspectionItemRepository; private readonly IRepository _inspectionPlanRepository; + private readonly IRepository _planItemRepository; + private readonly IRepository _eventDrivenConfigRepository; + private readonly IRepository _eventItemRepository; public SecondaryCircuitInspectionResultAppService( IMongoDbRepository inspectionResultRepository, @@ -60,6 +63,8 @@ namespace YunDa.SOMS.MongoDB.Application.DataMonitoring.SecondaryCircuitInspecti IRepository inspectionItemRepository, IRepository inspectionPlanRepository, IRepository eventDrivenConfigRepository, + IRepository eventItemRepository, + IRepository planItemRepository, ISessionAppService sessionAppService) : base(sessionAppService) { _inspectionResultRepository = inspectionResultRepository; @@ -67,7 +72,8 @@ namespace YunDa.SOMS.MongoDB.Application.DataMonitoring.SecondaryCircuitInspecti _inspectionItemRepository = inspectionItemRepository; _inspectionPlanRepository = inspectionPlanRepository; _eventDrivenConfigRepository = eventDrivenConfigRepository; - + _eventItemRepository = eventItemRepository; + _planItemRepository = planItemRepository; // 初始化 MongoDB 数据库 _mongoDatabase = _inspectionResultRepository.Database; } @@ -113,15 +119,25 @@ namespace YunDa.SOMS.MongoDB.Application.DataMonitoring.SecondaryCircuitInspecti SecondaryCircuitInspectionPlan inspectionPlan = null; if (intput.InspectionPlanId.HasValue) { - inspectionPlan = await _inspectionPlanRepository.GetAll() - .FirstOrDefaultAsync(x => x.Id == intput.InspectionPlanId, cancellationToken); + var inspectionPlanItem = await _planItemRepository.GetAll().Include(t=>t.InspectionPlan) + .FirstOrDefaultAsync(x => x.InspectionItemId == intput.InspectionPlanId, cancellationToken); + + if (inspectionPlanItem != null) + { + inspectionPlan = inspectionPlanItem.InspectionPlan; + } } SecondaryCircuitEventDrivenConfig eventDrivenConfig = null; if (intput.TriggerEventId.HasValue) { - eventDrivenConfig = await _eventDrivenConfigRepository.GetAll() + var eventDrivenConfigitem = await _eventItemRepository.GetAll().Include(t=>t.SecondaryCircuitEventDrivenConfig) .FirstOrDefaultAsync(x => x.Id == intput.TriggerEventId, cancellationToken); + + if (eventDrivenConfigitem!=null) + { + eventDrivenConfig = eventDrivenConfigitem.SecondaryCircuitEventDrivenConfig; + } } // 创建实体并映射基本字段 @@ -150,7 +166,7 @@ namespace YunDa.SOMS.MongoDB.Application.DataMonitoring.SecondaryCircuitInspecti // 填充名称字段 entity.SecondaryCircuitInspectionItemName = inspectionItem.Name; entity.InspectionPlanName = inspectionPlan?.Name; - entity.TransformerSubstationName = inspectionPlan?.TransformerSubstation?.SubstationName; + entity.SubTypeName = inspectionItem.ModuleType?.Name; // 获取模块类型的父类型名称作为SubTypeName @@ -162,7 +178,6 @@ namespace YunDa.SOMS.MongoDB.Application.DataMonitoring.SecondaryCircuitInspecti entity.ConfigTypeName = eventDrivenConfig?.Name; entity.ResultStatusName = GetEnumDescription(entity.ResultStatus); - entity.TriggerTypeName = GetEnumDescription(entity.TriggerType); // 使用动态集合名称(按年月分片) var collectionName = entity.GetCollectionName(); @@ -541,7 +556,7 @@ namespace YunDa.SOMS.MongoDB.Application.DataMonitoring.SecondaryCircuitInspecti { Id = result.Id, IsDisposed = disposalRecords.Any(x => x.IsCompleted), - InspectionItemName = result.SecondaryCircuitInspectionItemName, + InspectionItemName = result.SecondaryCircuitInspectionItemName??result.ConfigTypeName, ModuleTypeName = result.ModuleTypeName, Status = result.Status, InspectionResult = result.InspectionResult, @@ -599,6 +614,8 @@ namespace YunDa.SOMS.MongoDB.Application.DataMonitoring.SecondaryCircuitInspecti PageSearchCondition searchCondition) { var rst = new RequestPageResult(); + //var plans = _inspectionPlanRepository.GetAll().ToDictionary(t=>t.Id); + //var enents = _eventDrivenConfigRepository.GetAll().ToDictionary(t => t.Id); try { @@ -636,23 +653,21 @@ namespace YunDa.SOMS.MongoDB.Application.DataMonitoring.SecondaryCircuitInspecti { filters.Add(filterBuilder.Eq(x => x.InspectionPlanId, condition.InspectionPlanId.Value)); } - - // 巡检项过滤 - if (condition.InspectionItemId.HasValue) + if (!string.IsNullOrEmpty(condition.ModuleTypeName)) { - filters.Add(filterBuilder.Eq(x => x.SecondaryCircuitInspectionItemId, condition.InspectionItemId.Value)); + filters.Add(filterBuilder.Eq(x => x.ModuleTypeName, condition.ModuleTypeName)); } - // 模块类型过滤 - if (condition.ModuleTypeId.HasValue) + if (!string.IsNullOrEmpty(condition.SecondaryCircuitInspectionItemName)) { - filters.Add(filterBuilder.Eq("ModuleTypeId", condition.ModuleTypeId.Value)); + filters.Add(filterBuilder.Regex(x => x.SecondaryCircuitInspectionItemName, + new BsonRegularExpression(condition.SecondaryCircuitInspectionItemName, "i"))); } // 结果状态过滤 - if (condition.ResultStatus.HasValue) + if (!string.IsNullOrEmpty(condition.ResultStatusName)) { - filters.Add(filterBuilder.Eq(x => x.ResultStatus, condition.ResultStatus.Value)); + filters.Add(filterBuilder.Eq(x => x.ResultStatusName, condition.ResultStatusName)); } // 触发方式过滤 @@ -661,12 +676,6 @@ namespace YunDa.SOMS.MongoDB.Application.DataMonitoring.SecondaryCircuitInspecti filters.Add(filterBuilder.Eq(x => x.TriggerType, condition.TriggerType.Value)); } - // 是否异常过滤 - if (condition.IsAbnormal.HasValue) - { - filters.Add(filterBuilder.Eq(x => x.IsAbnormal, condition.IsAbnormal.Value)); - } - var filter = filterBuilder.And(filters); // 查询数据 @@ -729,7 +738,8 @@ namespace YunDa.SOMS.MongoDB.Application.DataMonitoring.SecondaryCircuitInspecti { Id = result.Id, IsDisposed = disposalRecords.Where(t=>t.InspectionResultId == result.Id).Any(x => x.IsCompleted), - InspectionItemName = result.SecondaryCircuitInspectionItemName, + InspectionItemName = result.SecondaryCircuitInspectionItemName??"", + ModuleTypeName = result.ModuleTypeName, SubTypeName = result.SubTypeName, Status = result.Status, @@ -737,7 +747,7 @@ namespace YunDa.SOMS.MongoDB.Application.DataMonitoring.SecondaryCircuitInspecti //VerificationResult = result.VerificationResult, //CalculationProcess = result.CalculationProcess, ExecutionTime = result.ExecutionTime, - InspectionPlanName = result.InspectionPlanName, + InspectionPlanName = result.InspectionPlanName?? result.ConfigTypeName, TransformerSubstationName = result.TransformerSubstationName, IsAbnormal = result.IsAbnormal, //AbnormalDescription = result.AbnormalDescription, @@ -846,14 +856,8 @@ namespace YunDa.SOMS.MongoDB.Application.DataMonitoring.SecondaryCircuitInspecti } return current; } - #region 日报、周报、月报API - - - - - /// /// 更新报告AI分析结果和巡检结果处理措施 /// @@ -866,40 +870,56 @@ namespace YunDa.SOMS.MongoDB.Application.DataMonitoring.SecondaryCircuitInspecti { try { + // 处理日报/周报/月报的AI分析(如果提供了ReportId) if (!string.IsNullOrWhiteSpace(input.ReportId)) { - var reportCollectionName = "SecondaryCircuitInspectionReport"; + var reportCollectionName = "SecondaryCircuitInspectionAIReport"; + var reportCollection = _mongoDatabase.GetCollection(reportCollectionName); + var reportFilter = Builders.Filter.Eq("ReportId", ObjectId.Parse(input.ReportId)); - var reportFilter = Builders.Filter.Eq("_id", ObjectId.Parse(input.ReportId)); var reportUpdate = Builders.Update + .Set("ReportId", ObjectId.Parse(input.ReportId)) // 添加ReportId,确保插入时有该字段 .Set("AIAnalysisResult", input.AIAnalysisResult) - .Set("UpdatedAt", DateTime.Now); + .Set("ResultHandleDescription", input.ResultHandleDescription) + .Set("UpdatedAt", DateTime.Now) + .SetOnInsert("CreatedAt", DateTime.Now); // 仅在插入时设置CreatedAt - var reportResult = await reportCollection.UpdateOneAsync(reportFilter, reportUpdate, cancellationToken: cancellationToken); + // 设置upsert选项 + var options = new UpdateOptions { IsUpsert = true }; - if (reportResult.ModifiedCount == 0) - { - Log4Helper.Warning($"报告不存在或更新失败: ReportId={input.ReportId}"); - } - else + var reportResult = await reportCollection.UpdateOneAsync( + reportFilter, + reportUpdate, + options, + cancellationToken: cancellationToken + ); + + if (reportResult.ModifiedCount > 0) { Log4Helper.Info($"更新报告AI分析成功: ReportId={input.ReportId}"); } + else if (reportResult.UpsertedId != null) + { + Log4Helper.Info($"插入新报告AI分析成功: ReportId={input.ReportId}"); + } + else + { + Log4Helper.Warning($"报告处理异常: ReportId={input.ReportId}"); + } } - // 处理单个巡检项的AI分析(如果提供了InspectionResultId) - if (!string.IsNullOrWhiteSpace(input.InspectionResultId)) + else if (!string.IsNullOrWhiteSpace(input.InspectionResultId)) { var aiReportCollectionName = "SecondaryCircuitInspectionAIReport"; var aiReportCollection = _mongoDatabase.GetCollection(aiReportCollectionName); - + var filter = Builders.Filter.Eq("InspectionResultId", input.InspectionResultId); + var report = (await aiReportCollection.FindAsync(filter)).FirstOrDefault(); // 判断是更新还是插入操作 - if (!string.IsNullOrWhiteSpace(input.Id)) + if (report != null) { // 更新现有记录 - var filter = Builders.Filter.Eq("_id", ObjectId.Parse(input.Id)); var update = Builders.Update .Set("AIAnalysisResult", input.AIAnalysisResult) .Set("ResultHandleDescription", input.ResultHandleDescription) diff --git a/src/YunDa.Application/YunDa.SOMS.MongoDB.Application/Inspection/SecondaryCircuitInspectionResult/SecondaryCircuitInspectionResultStatisticsAppService.cs b/src/YunDa.Application/YunDa.SOMS.MongoDB.Application/Inspection/SecondaryCircuitInspectionResult/SecondaryCircuitInspectionResultStatisticsAppService.cs index 7bd8be4..3c44806 100644 --- a/src/YunDa.Application/YunDa.SOMS.MongoDB.Application/Inspection/SecondaryCircuitInspectionResult/SecondaryCircuitInspectionResultStatisticsAppService.cs +++ b/src/YunDa.Application/YunDa.SOMS.MongoDB.Application/Inspection/SecondaryCircuitInspectionResult/SecondaryCircuitInspectionResultStatisticsAppService.cs @@ -1187,6 +1187,7 @@ namespace YunDa.SOMS.MongoDB.Application.DataMonitoring.SecondaryCircuitInspecti /// [HttpGet] [ShowApi] + [AllowAnonymous] public async Task> GetDailyReportAsync( DateTime reportDate, CancellationToken cancellationToken = default) @@ -1203,7 +1204,18 @@ namespace YunDa.SOMS.MongoDB.Application.DataMonitoring.SecondaryCircuitInspecti if (cachedReport != null) { var cachedOutput = BsonSerializer.Deserialize(cachedReport.GetValue("ReportData").AsBsonDocument); - Log4Helper.Info($"从缓存获取日报成功: ReportDate={reportDate:yyyy-MM-dd}"); + + // 设置报告ID + cachedOutput.Id = cachedReport.GetValue("_id").AsObjectId.ToString(); + + // 查询AI分析结果 + var aiAnalysisResult = cachedReport.GetValue("AIAnalysisResult", BsonNull.Value); + if (aiAnalysisResult != BsonNull.Value) + { + cachedOutput.ResultHandleDescription = aiAnalysisResult.AsString; + } + + Log4Helper.Info($"从缓存获取日报成功: ReportDate={reportDate:yyyy-MM-dd}, ReportId={cachedOutput.Id}"); return RequestResult.CreateSuccess(cachedOutput); } } @@ -1217,8 +1229,12 @@ namespace YunDa.SOMS.MongoDB.Application.DataMonitoring.SecondaryCircuitInspecti // 只有历史日期才保存到缓存 if (!isToday) { - await SaveReportAsync("Daily", reportDate, reportDate, output.ToBsonDocument(), cancellationToken); - Log4Helper.Info($"历史日报已缓存: ReportDate={reportDate:yyyy-MM-dd}"); + var reportId = await SaveReportAsync("Daily", reportDate, reportDate, output.ToBsonDocument(), cancellationToken); + if (!string.IsNullOrEmpty(reportId)) + { + output.Id = reportId; + } + Log4Helper.Info($"历史日报已缓存: ReportDate={reportDate:yyyy-MM-dd}, ReportId={reportId}"); } else { @@ -1257,19 +1273,42 @@ namespace YunDa.SOMS.MongoDB.Application.DataMonitoring.SecondaryCircuitInspecti if (cachedReport != null) { var cachedOutput = BsonSerializer.Deserialize(cachedReport.GetValue("ReportData").AsBsonDocument); - Log4Helper.Info($"从缓存获取周报成功: StartDate={startDate:yyyy-MM-dd}, EndDate={endDate:yyyy-MM-dd}"); + + // 设置报告ID + cachedOutput.Id = cachedReport.GetValue("_id").AsObjectId.ToString(); + var reportCollectionName = "SecondaryCircuitInspectionAIReport"; + var reportCollection = _mongoDatabase.GetCollection(reportCollectionName); + + var reportFilter = Builders.Filter.Eq("ReportId", ObjectId.Parse(cachedOutput.Id)); + // 查询AI分析结果 + var reportResult =( await reportCollection.FindAsync(reportFilter)).FirstOrDefault(); + + if (reportResult !=null) + { + cachedOutput.ResultHandleDescription = reportResult["ResultHandleDescription"]?.AsString; + } + else + { + cachedOutput.ResultHandleDescription = "请点击按钮生成分析结果"; + } + + Log4Helper.Info($"从缓存获取周报成功: StartDate={startDate:yyyy-MM-dd}, EndDate={endDate:yyyy-MM-dd}, ReportId={cachedOutput.Id}"); return RequestResult.CreateSuccess(cachedOutput); } } // 生成报告 var output = await GenerateWeeklyReportAsync(startDate, endDate, cancellationToken); - + // 只有历史周才保存到缓存 if (!isCurrentWeek) { - await SaveReportAsync("Weekly", startDate, endDate, output.ToBsonDocument(), cancellationToken); - Log4Helper.Info($"历史周报已缓存: StartDate={startDate:yyyy-MM-dd}, EndDate={endDate:yyyy-MM-dd}"); + var reportId = await SaveReportAsync("Weekly", startDate, endDate, output.ToBsonDocument(), cancellationToken); + if (!string.IsNullOrEmpty(reportId)) + { + output.Id = reportId; + } + Log4Helper.Info($"历史周报已缓存: StartDate={startDate:yyyy-MM-dd}, EndDate={endDate:yyyy-MM-dd}, ReportId={reportId}"); } else { @@ -1311,7 +1350,25 @@ namespace YunDa.SOMS.MongoDB.Application.DataMonitoring.SecondaryCircuitInspecti if (cachedReport != null) { var cachedOutput = BsonSerializer.Deserialize(cachedReport.GetValue("ReportData").AsBsonDocument); - Log4Helper.Info($"从缓存获取月报成功: Year={year}, Month={month}"); + + // 设置报告ID + cachedOutput.Id = cachedReport.GetValue("_id").AsObjectId.ToString(); + var reportCollectionName = "SecondaryCircuitInspectionAIReport"; + var reportCollection = _mongoDatabase.GetCollection(reportCollectionName); + + var reportFilter = Builders.Filter.Eq("ReportId", ObjectId.Parse(cachedOutput.Id)); + // 查询AI分析结果 + var reportResult = (await reportCollection.FindAsync(reportFilter)).FirstOrDefault(); + + if (reportResult != null) + { + cachedOutput.ResultHandleDescription = reportResult["ResultHandleDescription"].AsString; + } + else + { + cachedOutput.ResultHandleDescription = "请点击按钮生成分析结果"; + } + Log4Helper.Info($"从缓存获取月报成功: Year={year}, Month={month}, ReportId={cachedOutput.Id}"); return RequestResult.CreateSuccess(cachedOutput); } } @@ -1322,8 +1379,12 @@ namespace YunDa.SOMS.MongoDB.Application.DataMonitoring.SecondaryCircuitInspecti // 只有历史月份才保存到缓存 if (!isCurrentMonth) { - await SaveReportAsync("Monthly", startDate, endDate, output.ToBsonDocument(), cancellationToken); - Log4Helper.Info($"历史月报已缓存: Year={year}, Month={month}"); + var reportId = await SaveReportAsync("Monthly", startDate, endDate, output.ToBsonDocument(), cancellationToken); + if (!string.IsNullOrEmpty(reportId)) + { + output.Id = reportId; + } + Log4Helper.Info($"历史月报已缓存: Year={year}, Month={month}, ReportId={reportId}"); } else { @@ -1417,7 +1478,7 @@ namespace YunDa.SOMS.MongoDB.Application.DataMonitoring.SecondaryCircuitInspecti /// /// 保存报告到缓存 /// - private async Task SaveReportAsync( + private async Task SaveReportAsync( string reportType, DateTime startDate, DateTime endDate, @@ -1441,11 +1502,14 @@ namespace YunDa.SOMS.MongoDB.Application.DataMonitoring.SecondaryCircuitInspecti }; await collection.InsertOneAsync(document, cancellationToken: cancellationToken); - Log4Helper.Info($"保存报告到缓存成功: ReportType={reportType}, StartDate={startDate:yyyy-MM-dd}"); + var reportId = document["_id"].AsObjectId.ToString(); + Log4Helper.Info($"保存报告到缓存成功: ReportType={reportType}, StartDate={startDate:yyyy-MM-dd}, ReportId={reportId}"); + return reportId; } catch (Exception ex) { Log4Helper.Error($"保存报告到缓存失败: {ex.Message}", ex); + return null; } } @@ -1552,6 +1616,8 @@ namespace YunDa.SOMS.MongoDB.Application.DataMonitoring.SecondaryCircuitInspecti else disposalProgress = "未处理"; + + output.HighFrequencyAbnormalItems.Add(new HighFrequencyAbnormalItem { InspectionItemName = group.Key, diff --git a/src/YunDa.Application/YunDa.SOMS.MongoDB.Application/MeasuresTemperature/MeasuresTemperatureResultAppService.cs b/src/YunDa.Application/YunDa.SOMS.MongoDB.Application/MeasuresTemperature/MeasuresTemperatureResultAppService.cs index 7574027..0069540 100644 --- a/src/YunDa.Application/YunDa.SOMS.MongoDB.Application/MeasuresTemperature/MeasuresTemperatureResultAppService.cs +++ b/src/YunDa.Application/YunDa.SOMS.MongoDB.Application/MeasuresTemperature/MeasuresTemperatureResultAppService.cs @@ -1,8 +1,10 @@ using Abp.Auditing; using Abp.Authorization; using Abp.Domain.Repositories; +using log4net.Filter; using Microsoft.AspNetCore.Mvc; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Driver; using Newtonsoft.Json.Linq; using System; @@ -11,6 +13,7 @@ using System.ComponentModel; using System.IO; using System.Linq; using System.Linq.Dynamic.Core; +using System.Text.RegularExpressions; using System.Threading.Tasks; using ToolLibrary.LogHelper; using Yunda.SOMS.MongoDB.Entities.MeasuresTemperature; @@ -20,8 +23,10 @@ using YunDa.SOMS.Application.Core.Session; using YunDa.SOMS.Application.Core.SwaggerHelper; using YunDa.SOMS.DataTransferObject; using YunDa.SOMS.DataTransferObject.Iec104; +using YunDa.SOMS.DataTransferObject.MainstationData; using YunDa.SOMS.DataTransferObject.VideoSurveillance.MeasureTemperatureResultDto; using YunDa.SOMS.DataTransferObject.VideoSurveillance.MeasureTemperatureResultDto.SearchCondition; +using YunDa.SOMS.Entities.GeneralInformation; using YunDa.SOMS.Entities.System; using YunDa.SOMS.Entities.VideoSurveillance; using YunDa.SOMS.MongoDB.Repositories; @@ -38,7 +43,8 @@ namespace YunDa.SOMS.MongoDB.Application.MeasuresTemperature /// 红外测温结果仓储 /// private readonly IMongoDbRepository _resultRepository; - + private readonly IRepository _equipmentInfoRepository; + private readonly IRepository _presetPointRepository; private readonly IMongoDbRepository _bsonDocumentResultRepository; /// /// 红外测温点仓储 @@ -50,6 +56,8 @@ namespace YunDa.SOMS.MongoDB.Application.MeasuresTemperature , IRepository mtpRepository, IMongoDbRepository bsonDocumentResultRepository , IRepository sysConfigurationRepository + , IRepository equipmentInfoRepository + , IRepository presetPointRepository , ISessionAppService sessionAppService) : base(sessionAppService) { @@ -57,6 +65,8 @@ namespace YunDa.SOMS.MongoDB.Application.MeasuresTemperature _mtpRepository = mtpRepository; _bsonDocumentResultRepository = bsonDocumentResultRepository; _sysConfigurationRepository = sysConfigurationRepository; + _equipmentInfoRepository = equipmentInfoRepository; + _presetPointRepository = presetPointRepository; } #region 测温结果编辑 @@ -340,76 +350,7 @@ namespace YunDa.SOMS.MongoDB.Application.MeasuresTemperature [Abp.Domain.Uow.UnitOfWork(true)] public RequestPageResult FindDatas(PageSearchCondition searchCondition) { - var search = searchCondition.SearchCondition; - RequestPageResult requestPageResult = new RequestPageResult(); - List outputs = new List(); - var builder = Builders.Filter; - var listfilters = new List>(); - if (!string.IsNullOrWhiteSpace(search.Name)) - { - var filterDefinition = builder.Regex("Name", search.Name); - listfilters.Add(filterDefinition); - } - if (search.EquipmentInfoId.HasValue) - { - var filterDefinition = builder.Eq("EquipmentInfoId", search.EquipmentInfoId); - listfilters.Add(filterDefinition); - } - if (search.StartTime.HasValue) - { - var filterDefinition = builder.Gte("MeasureTime", search.StartTime);//< - listfilters.Add(filterDefinition); - } - if (search.EndTime.HasValue) - { - var filterDefinition = builder.Lt("MeasureTime", search.EndTime);//< - listfilters.Add(filterDefinition); - } - - var filter = builder.And(listfilters); - if (listfilters.Count == 0) - { - filter = builder.Empty; - } - try - { - _bsonDocumentResultRepository.CollectionName = "MeasureTemperatureResult"; - - IFindFluent rst = _bsonDocumentResultRepository.GetAllIncludeToFindFluent(filter); - var sorter = Builders.Sort; - var sortDefine = sorter.Descending("MeasureTime"); - rst = rst.Sort(sortDefine); - requestPageResult.TotalCount = (int)rst.CountDocuments(); - var skipCount = searchCondition.PageIndex <= 0 - ? -1 - : (searchCondition.PageIndex - 1) * searchCondition.PageSize; - if (skipCount != -1) - rst = rst.Skip(skipCount).Limit(searchCondition.PageSize); - outputs = rst.ToEnumerable().Select(c => new MeasureTemperatureResultOutput - { - Id = c["_id"].AsGuid, - MeasureTemperaturePointId = c["MeasureTemperaturePointId"].AsGuid, - Number = c["Number"].ToInt32(), - Name = c["Name"]?.ToString(), - TemperatureValue = (float)(c["TemperatureValue"]?.AsNullableDouble == null ? 0.0 : c["TemperatureValue"]?.AsNullableDouble), - Message = c["Message"]?.ToString(), - MeasureTime = c["MeasureTime"]?.ToNullableLocalTime(), - Status = c["Status"].ToInt32(), - PresetPointId = c["PresetPointId"]?.AsNullableGuid, - PresetPointName = c["PresetPointName"]?.ToString(), - EquipmentTypeId = c["EquipmentTypeId"]?.AsNullableGuid, - EquipmentTypeName = c["EquipmentTypeName"]?.ToString(), - EquipmentInfoId = c["EquipmentInfoId"]?.AsNullableGuid, - EquipmentInfoName = c["EquipmentInfoName"]?.ToString(), - }).ToList(); - requestPageResult.ResultData = outputs; - requestPageResult.Flag = true; - } - catch (Exception ex) - { - Log4Helper.Error(this.GetType(), "测温结果服务", ex); - } - return requestPageResult; + return FindMeasureTempertureResults(searchCondition); } /// @@ -651,5 +592,284 @@ namespace YunDa.SOMS.MongoDB.Application.MeasuresTemperature } + /// + /// 查询温度测量结果(热成像设备) + /// + /// 查询条件 + /// + [HttpPost, Audited, Description("查询温度测量结果(热成像设备)")] + [Abp.Domain.Uow.UnitOfWork(true)] + public RequestPageResult FindMeasureTempertureResults(PageSearchCondition searchCondition) + { + var search = searchCondition.SearchCondition; + RequestPageResult requestPageResult = new RequestPageResult(); + List outputs = new List(); + + try + { + // 第一步:从预置点仓储中筛选出 DevType 为 12 或 22 的热成像预置点 + var thermalPresetPoints = _presetPointRepository + .GetAllIncluding(p => p.VideoDev, p => p.EquipmentInfo, p => p.EquipmentType) + .Where(p => p.VideoDev != null && + (p.VideoDev.DevType == VideoDevTypeEnum.热成像枪机_热成像 || + p.VideoDev.DevType == VideoDevTypeEnum.热成像球机_热成像)) + .ToList(); + + if (thermalPresetPoints.Count == 0) + { + // 如果没有符合条件的预置点,返回空结果 + requestPageResult.ResultData = outputs; + requestPageResult.TotalCount = 0; + requestPageResult.Flag = true; + return requestPageResult; + } + + // 获取设备信息(如果指定了设备ID) + EquipmentInfo equipment = null; + if (search.EquipmentInfoId.HasValue) + { + equipment = _equipmentInfoRepository.GetAll().FirstOrDefault(t => t.Id == search.EquipmentInfoId); + } + + // 第二步:构建 MongoDB 查询过滤器 + var builder = Builders.Filter; + var listfilters = new List>(); + + // 添加时间范围过滤条件 + if (search.StartTime.HasValue) + { + var filterDefinition = builder.Gte("Time", search.StartTime); + listfilters.Add(filterDefinition); + } + else + { + DateTime date = DateTime.Now - TimeSpan.FromDays(3); + var f = builder.Gte("Time", date); + listfilters.Add(f); + } + + if (search.EndTime.HasValue) + { + var filterDefinition = builder.Lt("Time", search.EndTime); + listfilters.Add(filterDefinition); + } + else + { + DateTime date = DateTime.Now; + var f = builder.Lte("Time", date); + listfilters.Add(f); + } + + var filter = builder.And(listfilters); + if (listfilters.Count == 0) + { + filter = builder.Empty; + } + + // 第三步:从 OriginalInspectionStoreResult 集合查询数据 + _bsonDocumentResultRepository.CollectionName = nameof(OriginalInspectionStoreResult); + + var sorter = Builders.Sort; + var sortDefine = sorter.Descending("Time"); + + var originalDataList = _bsonDocumentResultRepository.GetAllIncludeToFindFluent(filter, sort: sortDefine) + .ToList(); + + var originalData = originalDataList.Select(t => BsonSerializer.Deserialize(t)); + + // 第四步:创建查找字典优化性能 + var presetDict = thermalPresetPoints + .GroupBy(p => p.VideoDevId) + .ToDictionary(g => g.Key, + g => g.DistinctBy(p => p.Number) + .ToDictionary(p => p.Number)); + + var cameraDict = thermalPresetPoints + .Where(p => p.VideoDev != null) + .Select(p => p.VideoDev) + .DistinctBy(v => v.DevName) + .ToDictionary(v => v.DevName); + + // 第五步:处理每条原始巡检数据 + foreach (var data in originalData) + { + // 1. 查找摄像头 + if (!cameraDict.TryGetValue(data.CamName, out var camera)) + continue; + + // 2. 解析预置码 + if (!int.TryParse(data.PresetCode, out int presetCode)) + continue; + + // 3. 查找预置点 + if (!presetDict.TryGetValue(camera.Id, out var devicePresets) || + !devicePresets.TryGetValue(presetCode, out var preset)) + continue; + + // 4. 设备过滤(如果指定了设备ID) + if (search.EquipmentInfoId.HasValue) + { + if (preset.EquipmentInfoId == null) + { + // 如果预置点没有关联设备,通过设备名称匹配 + if (equipment != null && !string.IsNullOrEmpty(data.EquipmentInfoName)) + { + if (!data.EquipmentInfoName.Contains(equipment.Name) && + CalculateSimilarity(data.EquipmentInfoName, equipment.Name) < 0.8) + continue; + } + else + { + continue; + } + } + else if (preset.EquipmentInfoId != search.EquipmentInfoId) + { + continue; + } + } + + // 5. 解析温度数据 + var temperatureResults = ParseTemperatureFromResult(data, preset); + + // 6. 名称过滤(如果指定了名称) + if (!string.IsNullOrWhiteSpace(search.Name)) + { + temperatureResults = temperatureResults + .Where(t => t.Name != null && t.Name.Contains(search.Name)) + .ToList(); + } + + // 7. 添加到输出列表 + outputs.AddRange(temperatureResults); + } + + // 第六步:排序和分页 + outputs = outputs.OrderByDescending(o => o.MeasureTime).ToList(); + requestPageResult.TotalCount = outputs.Count; + + var skipCount = searchCondition.PageIndex <= 0 + ? 0 + : (searchCondition.PageIndex - 1) * searchCondition.PageSize; + + if (searchCondition.PageSize > 0) + { + outputs = outputs.Skip(skipCount).Take(searchCondition.PageSize).ToList(); + } + + requestPageResult.ResultData = outputs; + requestPageResult.Flag = true; + } + catch (Exception ex) + { + Log4Helper.Error(this.GetType(), "测温结果服务-查询热成像设备测温结果", ex); + requestPageResult.Flag = false; + requestPageResult.Message = ex.Message; + } + + return requestPageResult; + } + + /// + /// 从巡检结果数据中解析温度数据 + /// + /// 原始巡检结果数据 + /// 预置点信息 + /// 温度测量结果列表 + private List ParseTemperatureFromResult(OriginalInspectionStoreResult data, PresetPoint preset) + { + var results = new List(); + + if (string.IsNullOrWhiteSpace(data.Result)) + return results; + + try + { + // 解析测量时间 + DateTime measureTime = data.Time; + if (!string.IsNullOrWhiteSpace(data.CaptureTime)) + { + if (DateTime.TryParse(data.CaptureTime, out DateTime captureTime)) + { + measureTime = captureTime; + } + } + + // 解析状态 + int status = 0; + if (!string.IsNullOrWhiteSpace(data.Status)) + { + int.TryParse(data.Status, out status); + } + + // 分割多个测温点数据(支持中英文逗号分隔) + var analysisResultArr = data.Result.Split(new[] { ",", ",", "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries); + + int pointNumber = 1; + foreach (var item in analysisResultArr) + { + // 分割名称和温度值(支持中英文冒号) + var parts = item.Split(new[] { ":", ":" }, StringSplitOptions.RemoveEmptyEntries); + + if (parts.Length == 2) + { + string pointName = parts[0].Trim(); + string valueStr = parts[1].Trim(); + + // 使用正则表达式提取温度数值 + string pattern = @"\d+(?:\.\d+)?"; + Match match = Regex.Match(valueStr, pattern); + + if (match.Success && float.TryParse(match.Value, out float temperatureValue)) + { + var output = new MeasureTemperatureResultOutput + { + Id = Guid.NewGuid(), + MeasureTemperaturePointId = Guid.Empty, // 原始数据没有测温点ID + Number = pointNumber++, + Name = pointName, + TemperatureValue = temperatureValue, + Message = data.Message ?? "热成像测温", + MeasureTime = measureTime, + Status = status, + PresetPointId = preset.Id, + PresetPointName = preset.Name, + EquipmentTypeId = preset.EquipmentTypeId, + EquipmentTypeName = preset.EquipmentType?.Name, + EquipmentInfoId = preset.EquipmentInfoId, + EquipmentInfoName = preset.EquipmentInfo?.Name + }; + + results.Add(output); + } + } + } + } + catch (Exception ex) + { + Log4Helper.Error(this.GetType(), $"解析温度数据失败: {data.Result}", ex); + } + + return results; + } + + /// + /// 计算字符串相似度 + /// + private double CalculateSimilarity(string source, string target) + { + if (string.IsNullOrEmpty(source) || string.IsNullOrEmpty(target)) + return 0; + + // 简单的子串匹配比例 + int matchCount = 0; + foreach (char c in target) + { + if (source.Contains(c)) + matchCount++; + } + return (double)matchCount / target.Length; + } + } } \ No newline at end of file diff --git a/src/YunDa.Util/ToolLibrary/HttpHelper.cs b/src/YunDa.Util/ToolLibrary/HttpHelper.cs index ca1a50e..d448f82 100644 --- a/src/YunDa.Util/ToolLibrary/HttpHelper.cs +++ b/src/YunDa.Util/ToolLibrary/HttpHelper.cs @@ -159,62 +159,92 @@ namespace ToolLibrary /// /// 请求连接 /// 请求参数 + /// 总尝试次数(包括初始尝试),默认3次 + /// 重试延迟函数 + /// 访问令牌 /// 根据泛型返回响应值 public static T HttpPostRequestWithRetry(string url, object dicParams = null, - int retryCount = 10, + int retryCount = 3, Func func = null, string accessToken = null ) { - Stream requestStream = null; if (func == null) { - func = count => TimeSpan.FromSeconds(count * count); + // 使用更合理的延迟策略:2秒、4秒、6秒...最多10秒 + func = count => TimeSpan.FromSeconds(Math.Min(count * 2, 10)); } + + // 配置重试策略:retryCount-1 表示在初始尝试失败后的重试次数 + var policy = Policy + .Handle() + .WaitAndRetry( + retryCount - 1, + func, + (exception, timeSpan, retryAttempt, context) => + { + LogHelper.Log4Helper.Error(typeof(HttpClient), + $"HTTP请求失败,第{retryAttempt}次重试,url:{url},延迟{timeSpan.TotalSeconds}秒", + exception); + } + ); + try { - Encoding encoding = Encoding.UTF8; - HttpWebRequest request = GetHttpWebRequest(url); - request.UseDefaultCredentials = true; - //request.Timeout = 3000; - request.Method = "POST"; - request.Accept = "application/json, text/javascript, */*"; //"text/html, application/xhtml+xml, */*"; - request.ContentType = "application/json;charset=UTF-8"; - //request.Headers.Add("ContentType", "application/json; charset=utf-8"); - if (!string.IsNullOrEmpty(accessToken)) - request.Headers.Add("Authorization", "Bearer" + " " + accessToken); - if (dicParams != null) + // 在重试策略中执行HTTP请求,每次重试都创建新的连接 + var result = policy.Execute(() => { - var paramStr = ObjectToJsonStr(dicParams); - byte[] buffer = encoding.GetBytes(paramStr); - request.ContentLength = buffer.Length; - requestStream = request.GetRequestStream(); - requestStream.Write(buffer, 0, buffer.Length); - } - var policy = Policy - .Handle() - .WaitAndRetry( - retryCount, func, - (exception, timeSpan, retryCount, context) => + HttpWebRequest request = null; + Stream requestStream = null; + try + { + Encoding encoding = Encoding.UTF8; + request = GetHttpWebRequest(url); + request.UseDefaultCredentials = true; + request.Timeout = 120000; // 120秒超时 + request.Method = "POST"; + request.Accept = "application/json, text/javascript, */*"; + request.ContentType = "application/json;charset=UTF-8"; + + if (!string.IsNullOrEmpty(accessToken)) + request.Headers.Add("Authorization", "Bearer" + " " + accessToken); + + if (dicParams != null) { - LogHelper.Log4Helper.Error(typeof(HttpClient), $"第{retryCount}重试,url:{url}", exception.InnerException); + var paramStr = ObjectToJsonStr(dicParams); + byte[] buffer = encoding.GetBytes(paramStr); + request.ContentLength = buffer.Length; + requestStream = request.GetRequestStream(); + requestStream.Write(buffer, 0, buffer.Length); + requestStream.Close(); + requestStream = null; // 标记已关闭,避免finally中重复关闭 } - ); - var resulut = policy.Execute(() => GetResponseValue(request)); - return resulut; + + return GetResponseValue(request); + } + finally + { + // 确保资源被释放 + if (requestStream != null) + { + try { requestStream.Close(); } catch { } + } + if (request != null) + { + try { request.Abort(); } catch { } // 中止请求以释放连接 + } + } + }); + + return result; } catch (Exception ex) { - LogHelper.Log4Helper.Error(typeof(HttpWebRequest), "请求地址" + url, ex); + LogHelper.Log4Helper.Error(typeof(HttpWebRequest), + $"HTTP请求最终失败,已重试{retryCount - 1}次,请求地址:{url}", ex); + throw; // 重新抛出异常,让调用方知道请求失败 } - finally - { - if (requestStream != null) - requestStream.Close(); - } - return default(T); - } /// /// http请求,POST方式,文件断点续传