From 61ae3e587f4a6050790b349ba016e6feb8c7a594 Mon Sep 17 00:00:00 2001 From: hyh Date: Mon, 4 Aug 2025 11:28:55 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20GetRuntimeDeviceCodesNotIn?= =?UTF-8?q?ActiveRuntimesAsync?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...timeDeviceCodesNotInActiveRuntimesQuery.cs | 12 + ...iceCodesNotInActiveRuntimesQueryHandler.cs | 92 ++++ ...eDeviceCodesNotInActiveRuntimesResponse.cs | 29 ++ .../Models/ProtocolLogRuntimeDeviceDto.cs | 23 + .../Models/ProtocolLogRuntimeDeviceTreeDto.cs | 49 ++ .../Logging/IProtocolLogRepository.cs | 7 + .../Logging/ProtocolLogRepository.cs | 36 ++ .../Controllers/ProtocolLogsController.cs | 24 + src/modify.md | 426 +++++++++++++++++- 9 files changed, 697 insertions(+), 1 deletion(-) create mode 100644 src/X1.Application/Features/ProtocolLogs/Queries/GetRuntimeDeviceCodesNotInActiveRuntimes/GetRuntimeDeviceCodesNotInActiveRuntimesQuery.cs create mode 100644 src/X1.Application/Features/ProtocolLogs/Queries/GetRuntimeDeviceCodesNotInActiveRuntimes/GetRuntimeDeviceCodesNotInActiveRuntimesQueryHandler.cs create mode 100644 src/X1.Application/Features/ProtocolLogs/Queries/GetRuntimeDeviceCodesNotInActiveRuntimes/GetRuntimeDeviceCodesNotInActiveRuntimesResponse.cs create mode 100644 src/X1.Domain/Models/ProtocolLogRuntimeDeviceDto.cs create mode 100644 src/X1.Domain/Models/ProtocolLogRuntimeDeviceTreeDto.cs diff --git a/src/X1.Application/Features/ProtocolLogs/Queries/GetRuntimeDeviceCodesNotInActiveRuntimes/GetRuntimeDeviceCodesNotInActiveRuntimesQuery.cs b/src/X1.Application/Features/ProtocolLogs/Queries/GetRuntimeDeviceCodesNotInActiveRuntimes/GetRuntimeDeviceCodesNotInActiveRuntimesQuery.cs new file mode 100644 index 0000000..8a444a5 --- /dev/null +++ b/src/X1.Application/Features/ProtocolLogs/Queries/GetRuntimeDeviceCodesNotInActiveRuntimes/GetRuntimeDeviceCodesNotInActiveRuntimesQuery.cs @@ -0,0 +1,12 @@ +using CellularManagement.Domain.Common; +using MediatR; + +namespace CellularManagement.Application.Features.ProtocolLogs.Queries.GetRuntimeDeviceCodesNotInActiveRuntimes; + +/// +/// 获取不在活跃运行时状态中的协议日志RuntimeCode和DeviceCode查询 +/// +public class GetRuntimeDeviceCodesNotInActiveRuntimesQuery : IRequest> +{ + // 此查询不需要参数,直接获取所有符合条件的记录 +} \ No newline at end of file diff --git a/src/X1.Application/Features/ProtocolLogs/Queries/GetRuntimeDeviceCodesNotInActiveRuntimes/GetRuntimeDeviceCodesNotInActiveRuntimesQueryHandler.cs b/src/X1.Application/Features/ProtocolLogs/Queries/GetRuntimeDeviceCodesNotInActiveRuntimes/GetRuntimeDeviceCodesNotInActiveRuntimesQueryHandler.cs new file mode 100644 index 0000000..e5f718a --- /dev/null +++ b/src/X1.Application/Features/ProtocolLogs/Queries/GetRuntimeDeviceCodesNotInActiveRuntimes/GetRuntimeDeviceCodesNotInActiveRuntimesQueryHandler.cs @@ -0,0 +1,92 @@ +using MediatR; +using Microsoft.Extensions.Logging; +using CellularManagement.Domain.Common; +using CellularManagement.Domain.Repositories.Logging; +using CellularManagement.Domain.Models; + +namespace CellularManagement.Application.Features.ProtocolLogs.Queries.GetRuntimeDeviceCodesNotInActiveRuntimes; + +/// +/// 获取不在活跃运行时状态中的协议日志RuntimeCode和DeviceCode查询处理器 +/// +public class GetRuntimeDeviceCodesNotInActiveRuntimesQueryHandler : IRequestHandler> +{ + private readonly IProtocolLogRepository _protocolLogRepository; + private readonly ILogger _logger; + + /// + /// 初始化查询处理器 + /// + public GetRuntimeDeviceCodesNotInActiveRuntimesQueryHandler( + IProtocolLogRepository protocolLogRepository, + ILogger logger) + { + _protocolLogRepository = protocolLogRepository; + _logger = logger; + } + + /// + /// 处理获取不在活跃运行时状态中的协议日志RuntimeCode和DeviceCode查询 + /// + public async Task> Handle(GetRuntimeDeviceCodesNotInActiveRuntimesQuery request, CancellationToken cancellationToken) + { + try + { + _logger.LogInformation("开始获取不在活跃运行时状态中的协议日志RuntimeCode和DeviceCode"); + + // 调用仓储方法获取数据 + var items = await _protocolLogRepository.GetRuntimeDeviceCodesNotInActiveRuntimesAsync(cancellationToken); + var itemsList = items.ToList(); + + // 构建树形结构数据 + var treeItems = BuildTreeStructure(itemsList); + + // 构建响应 + var response = new GetRuntimeDeviceCodesNotInActiveRuntimesResponse + { + TreeItems = treeItems, + TotalCount = itemsList.Count, + DeviceCount = treeItems.Count() + }; + + _logger.LogInformation("成功获取不在活跃运行时状态中的协议日志RuntimeCode和DeviceCode,共 {TotalCount} 条记录,涉及 {DeviceCount} 个设备", + response.TotalCount, response.DeviceCount); + + return OperationResult.CreateSuccess(response); + } + catch (Exception ex) + { + _logger.LogError(ex, "获取不在活跃运行时状态中的协议日志RuntimeCode和DeviceCode时发生错误"); + return OperationResult.CreateFailure($"获取协议日志运行时设备代码时发生错误: {ex.Message}"); + } + } + + /// + /// 构建树形结构数据 + /// 将平面数据按DeviceCode分组,每个设备下包含其对应的RuntimeCode列表 + /// + /// 平面数据列表 + /// 树形结构数据 + private IEnumerable BuildTreeStructure(IEnumerable items) + { + // 按DeviceCode分组 + var groupedData = items + .GroupBy(item => item.DeviceCode) + .Select(group => new ProtocolLogRuntimeDeviceTreeDto + { + DeviceCode = group.Key, + DeviceName = group.Key, // 可以根据需要从设备服务获取设备名称 + Runtimes = group.Select(item => new ProtocolLogRuntimeInfo + { + RuntimeCode = item.RuntimeCode, + Timestamp = item.Timestamp + }).ToList() + }) + .OrderBy(item => item.DeviceCode) // 按设备代码排序 + .ToList(); + + _logger.LogDebug("构建树形结构完成,共 {DeviceCount} 个设备", groupedData.Count); + + return groupedData; + } +} \ No newline at end of file diff --git a/src/X1.Application/Features/ProtocolLogs/Queries/GetRuntimeDeviceCodesNotInActiveRuntimes/GetRuntimeDeviceCodesNotInActiveRuntimesResponse.cs b/src/X1.Application/Features/ProtocolLogs/Queries/GetRuntimeDeviceCodesNotInActiveRuntimes/GetRuntimeDeviceCodesNotInActiveRuntimesResponse.cs new file mode 100644 index 0000000..3a919fa --- /dev/null +++ b/src/X1.Application/Features/ProtocolLogs/Queries/GetRuntimeDeviceCodesNotInActiveRuntimes/GetRuntimeDeviceCodesNotInActiveRuntimesResponse.cs @@ -0,0 +1,29 @@ +using CellularManagement.Domain.Models; + +namespace CellularManagement.Application.Features.ProtocolLogs.Queries.GetRuntimeDeviceCodesNotInActiveRuntimes; + +/// +/// 获取不在活跃运行时状态中的协议日志RuntimeCode和DeviceCode响应 +/// +public class GetRuntimeDeviceCodesNotInActiveRuntimesResponse +{ + /// + /// 协议日志运行时设备树形结构(按设备分组) + /// + public IEnumerable TreeItems { get; set; } = Enumerable.Empty(); + + /// + /// 总数量 + /// + public int TotalCount { get; set; } + + /// + /// 设备数量 + /// + public int DeviceCount { get; set; } + + /// + /// 是否有树形数据 + /// + public bool HasData => TreeItems.Any(); +} \ No newline at end of file diff --git a/src/X1.Domain/Models/ProtocolLogRuntimeDeviceDto.cs b/src/X1.Domain/Models/ProtocolLogRuntimeDeviceDto.cs new file mode 100644 index 0000000..85adc41 --- /dev/null +++ b/src/X1.Domain/Models/ProtocolLogRuntimeDeviceDto.cs @@ -0,0 +1,23 @@ +namespace CellularManagement.Domain.Models; + +/// +/// 协议日志运行时设备DTO +/// 用于返回协议日志的RuntimeCode和DeviceCode +/// +public class ProtocolLogRuntimeDeviceDto +{ + /// + /// 运行时代码 + /// + public string RuntimeCode { get; set; } = string.Empty; + + /// + /// 设备代码 + /// + public string DeviceCode { get; set; } = string.Empty; + + /// + /// 时间戳 + /// + public long Timestamp { get; set; } +} \ No newline at end of file diff --git a/src/X1.Domain/Models/ProtocolLogRuntimeDeviceTreeDto.cs b/src/X1.Domain/Models/ProtocolLogRuntimeDeviceTreeDto.cs new file mode 100644 index 0000000..f1c3cb3 --- /dev/null +++ b/src/X1.Domain/Models/ProtocolLogRuntimeDeviceTreeDto.cs @@ -0,0 +1,49 @@ +namespace CellularManagement.Domain.Models; + +/// +/// 协议日志运行时设备树形结构DTO +/// 用于表示按设备分组的运行时数据,便于前端渲染树形结构 +/// +public class ProtocolLogRuntimeDeviceTreeDto +{ + /// + /// 设备代码 + /// + public string DeviceCode { get; set; } = string.Empty; + + /// + /// 设备名称(可选,用于显示) + /// + public string? DeviceName { get; set; } + + /// + /// 该设备下的运行时列表 + /// + public List Runtimes { get; set; } = new(); + + /// + /// 该设备下的运行时数量 + /// + public int RuntimeCount => Runtimes.Count; +} + +/// +/// 协议日志运行时信息 +/// +public class ProtocolLogRuntimeInfo +{ + /// + /// 运行时代码 + /// + public string RuntimeCode { get; set; } = string.Empty; + + /// + /// 时间戳 + /// + public long Timestamp { get; set; } + + /// + /// 格式化后的时间显示 + /// + public string FormattedTime => DateTimeOffset.FromUnixTimeMilliseconds(Timestamp).ToString("yyyy-MM-dd HH:mm:ss.fff"); +} \ No newline at end of file diff --git a/src/X1.Domain/Repositories/Logging/IProtocolLogRepository.cs b/src/X1.Domain/Repositories/Logging/IProtocolLogRepository.cs index 3092285..e4c087b 100644 --- a/src/X1.Domain/Repositories/Logging/IProtocolLogRepository.cs +++ b/src/X1.Domain/Repositories/Logging/IProtocolLogRepository.cs @@ -84,4 +84,11 @@ public interface IProtocolLogRepository : IBaseRepository /// MessageDetailJson内容 Task GetMessageDetailJsonByIdAsync(string id, CancellationToken cancellationToken = default); + /// + /// 获取不在活跃运行时状态中的协议日志RuntimeCode和DeviceCode + /// + /// 取消令牌 + /// RuntimeCode和DeviceCode列表 + Task> GetRuntimeDeviceCodesNotInActiveRuntimesAsync(CancellationToken cancellationToken = default); + } \ No newline at end of file diff --git a/src/X1.Infrastructure/Repositories/Logging/ProtocolLogRepository.cs b/src/X1.Infrastructure/Repositories/Logging/ProtocolLogRepository.cs index 1ac1c8d..4ef2015 100644 --- a/src/X1.Infrastructure/Repositories/Logging/ProtocolLogRepository.cs +++ b/src/X1.Infrastructure/Repositories/Logging/ProtocolLogRepository.cs @@ -320,4 +320,40 @@ public class ProtocolLogRepository : BaseRepository, IProtocolLogRe throw; } } + + /// + /// 获取不在活跃运行时状态中的协议日志RuntimeCode和DeviceCode + /// + /// 取消令牌 + /// RuntimeCode和DeviceCode列表 + public async Task> GetRuntimeDeviceCodesNotInActiveRuntimesAsync(CancellationToken cancellationToken = default) + { + try + { + // 使用DISTINCT ON和NOT EXISTS子查询,性能优化 + var sql = @" + SELECT DISTINCT ON (tpl.""DeviceCode"", tpl.""RuntimeCode"") + tpl.""RuntimeCode"", tpl.""DeviceCode"", tpl.""Timestamp"" + FROM ""tb_protocol_logs"" tpl + WHERE NOT EXISTS ( + SELECT 1 + FROM ""tb_cellular_device_runtimes"" t + WHERE t.""RuntimeStatus"" = 1 + AND t.""RuntimeCode"" = tpl.""RuntimeCode"" + ) + ORDER BY tpl.""DeviceCode"", tpl.""RuntimeCode"", tpl.""Timestamp"" DESC"; + + // 执行SQL查询,直接映射到ProtocolLogRuntimeDeviceDto + var result = await QueryRepository.ExecuteSqlQueryAsync(sql, Array.Empty(), cancellationToken); + + _logger.LogDebug("获取不在活跃运行时状态中的协议日志RuntimeCode和DeviceCode,结果数量:{Count}", result.Count()); + + return result; + } + catch (Exception ex) + { + _logger.LogError(ex, "获取不在活跃运行时状态中的协议日志RuntimeCode和DeviceCode时发生错误"); + throw; + } + } } \ No newline at end of file diff --git a/src/X1.Presentation/Controllers/ProtocolLogsController.cs b/src/X1.Presentation/Controllers/ProtocolLogsController.cs index ea2bb42..674c810 100644 --- a/src/X1.Presentation/Controllers/ProtocolLogsController.cs +++ b/src/X1.Presentation/Controllers/ProtocolLogsController.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Logging; using CellularManagement.Application.Features.ProtocolLogs.Queries.GetProtocolLogsByDevice; using CellularManagement.Application.Features.ProtocolLogs.Queries.GetMessageDetailJsonById; +using CellularManagement.Application.Features.ProtocolLogs.Queries.GetRuntimeDeviceCodesNotInActiveRuntimes; using CellularManagement.Domain.Common; using CellularManagement.Domain.Entities.Device; using CellularManagement.Presentation.Abstractions; @@ -87,4 +88,27 @@ public class ProtocolLogsController : ApiController return result; } + + /// + /// 获取不在活跃运行时状态中的协议日志RuntimeCode和DeviceCode + /// + /// 协议日志运行时设备代码列表 + [HttpGet("runtime-device-codes-not-in-active-runtimes")] + public async Task> GetRuntimeDeviceCodesNotInActiveRuntimes() + { + _logger.LogInformation("开始获取不在活跃运行时状态中的协议日志RuntimeCode和DeviceCode"); + + var query = new GetRuntimeDeviceCodesNotInActiveRuntimesQuery(); + var result = await mediator.Send(query); + + if (!result.IsSuccess) + { + _logger.LogWarning("获取不在活跃运行时状态中的协议日志RuntimeCode和DeviceCode失败: {Message}", result.ErrorMessages); + return result; + } + + _logger.LogInformation("成功获取不在活跃运行时状态中的协议日志RuntimeCode和DeviceCode,共 {Count} 条记录", result.Data?.TotalCount ?? 0); + + return result; + } } \ No newline at end of file diff --git a/src/modify.md b/src/modify.md index 3650f0d..60c8f49 100644 --- a/src/modify.md +++ b/src/modify.md @@ -1380,6 +1380,157 @@ var result = await QueryRepository.GetPagedAsync( - **数据存储**:运行时记录被标记为已删除而非物理删除 - **查询结果**:软删除的记录不会出现在正常查询结果中 +--- + +## 2025-01-29 更新ProtocolLogRepository使用DISTINCT ON查询 + +### 修改原因 +根据用户需求,将 `GetRuntimeDeviceCodesNotInActiveRuntimesAsync` 方法中的SQL查询更新为使用 `DISTINCT ON` 语法,提高查询性能和结果准确性。 + +### 修改文件 +- `X1.Domain/Models/ProtocolLogRuntimeDeviceDto.cs` - 添加Timestamp属性 +- `X1.Infrastructure/Repositories/Logging/ProtocolLogRepository.cs` - 更新SQL查询 + +### 修改内容 + +#### 1. DTO模型更新 +- **新增Timestamp属性**:在 `ProtocolLogRuntimeDeviceDto` 中添加 `Timestamp` 属性 +- **类型定义**:`public long Timestamp { get; set; }` +- **用途说明**:用于返回协议日志的时间戳信息 + +#### 2. SQL查询优化 +- **查询语法更新**:从 `NOT IN` 子查询改为 `NOT EXISTS` 子查询 +- **DISTINCT ON语法**:使用 `SELECT DISTINCT ON (tpl."DeviceCode", tpl."RuntimeCode")` 确保每个设备-运行时组合只返回一条记录 +- **排序优化**:按 `tpl."DeviceCode"`, `tpl."RuntimeCode"`, `tpl."Timestamp" DESC` 排序,确保返回最新的记录 +- **性能提升**:`NOT EXISTS` 通常比 `NOT IN` 性能更好,特别是在大数据集上 + +#### 3. 具体SQL变更 +```sql +-- 修改前 +SELECT tpl."RuntimeCode", tpl."DeviceCode" +FROM "tb_protocol_logs" tpl +WHERE tpl."RuntimeCode" NOT IN ( + SELECT "RuntimeCode" + FROM "tb_cellular_device_runtimes" + WHERE "RuntimeStatus" = 1 +) +GROUP BY tpl."DeviceCode", tpl."RuntimeCode" +ORDER BY tpl."Timestamp" DESC + +-- 修改后 +SELECT DISTINCT ON (tpl."DeviceCode", tpl."RuntimeCode") + tpl."RuntimeCode", tpl."DeviceCode", tpl."Timestamp" +FROM "tb_protocol_logs" tpl +WHERE NOT EXISTS ( + SELECT 1 + FROM "tb_cellular_device_runtimes" t + WHERE t."RuntimeStatus" = 1 + AND t."RuntimeCode" = tpl."RuntimeCode" +) +ORDER BY tpl."DeviceCode", tpl."RuntimeCode", tpl."Timestamp" DESC +``` + +### 技术特性 +- **DISTINCT ON语法**:PostgreSQL特有的语法,确保每个分组只返回一条记录 +- **NOT EXISTS优化**:比NOT IN性能更好,特别是在大数据集上 +- **时间戳排序**:确保返回每个设备-运行时组合的最新记录 +- **数据完整性**:返回完整的协议日志信息,包括时间戳 + +### 业务价值 +- **查询性能**:提高大数据表的查询性能 +- **结果准确性**:确保返回每个设备-运行时组合的最新记录 +- **数据完整性**:提供更完整的协议日志信息 +- **系统稳定性**:优化查询减少数据库负载 + +### 影响范围 +- **API响应**:响应数据现在包含时间戳信息 +- **查询性能**:提高大数据表的查询效率 +- **数据准确性**:确保返回最新记录而非任意记录 +- **DTO结构**:扩展了返回数据结构 + +--- + +## 2025-01-29 优化GetRuntimeDeviceCodesNotInActiveRuntimesQueryHandler支持树形结构 + +### 修改原因 +根据用户需求,当 `DeviceCode` 和 `RuntimeCode` 数据较多时,界面需要渲染为树形结构。需要将平面数据按设备分组,每个设备下包含其对应的运行时列表,便于前端渲染树形界面。 + +### 修改文件 +- `X1.Domain/Models/ProtocolLogRuntimeDeviceTreeDto.cs` - 新增树形结构DTO +- `X1.Application/Features/ProtocolLogs/Queries/GetRuntimeDeviceCodesNotInActiveRuntimes/GetRuntimeDeviceCodesNotInActiveRuntimesResponse.cs` - 更新响应类 +- `X1.Application/Features/ProtocolLogs/Queries/GetRuntimeDeviceCodesNotInActiveRuntimes/GetRuntimeDeviceCodesNotInActiveRuntimesQueryHandler.cs` - 更新查询处理器 + +### 修改内容 + +#### 1. 新增树形结构DTO +- **ProtocolLogRuntimeDeviceTreeDto**:表示按设备分组的树形结构 + - `DeviceCode`:设备代码 + - `DeviceName`:设备名称(可选,用于显示) + - `Runtimes`:该设备下的运行时列表 + - `RuntimeCount`:该设备下的运行时数量 + +- **ProtocolLogRuntimeInfo**:运行时信息 + - `RuntimeCode`:运行时代码 + - `Timestamp`:时间戳 + - `FormattedTime`:格式化后的时间显示 + +#### 2. 更新响应类 +- **新增TreeItems属性**:包含树形结构数据 +- **新增DeviceCount属性**:设备数量统计 +- **简化数据结构**:移除平面结构的Items属性,只保留树形结构 + +#### 3. 更新查询处理器 +- **新增BuildTreeStructure方法**:将平面数据转换为树形结构 +- **数据分组逻辑**:按DeviceCode分组,每个设备下包含其RuntimeCode列表 +- **排序优化**:按设备代码排序,便于前端显示 +- **统计信息**:提供设备数量和总记录数的统计 + +#### 4. 数据结构示例 +```json +{ + "treeItems": [ + { + "deviceCode": "DEV001", + "deviceName": "DEV001", + "runtimeCount": 2, + "runtimes": [ + {"runtimeCode": "RT-001-DEV001", "timestamp": 1640995200000, "formattedTime": "2022-01-01 12:00:00.000"}, + {"runtimeCode": "RT-002-DEV001", "timestamp": 1640995300000, "formattedTime": "2022-01-01 12:01:40.000"} + ] + }, + { + "deviceCode": "DEV002", + "deviceName": "DEV002", + "runtimeCount": 1, + "runtimes": [ + {"runtimeCode": "RT-001-DEV002", "timestamp": 1640995400000, "formattedTime": "2022-01-01 12:03:20.000"} + ] + } + ], + "totalCount": 3, + "deviceCount": 2 +} +``` + +### 技术特性 +- **数据分组**:按设备代码自动分组,便于树形显示 +- **时间格式化**:提供格式化后的时间显示,便于用户阅读 +- **简化结构**:移除平面数据结构,专注于树形结构支持 +- **性能优化**:在应用层进行数据分组,减少数据库查询压力 +- **统计信息**:提供设备数量和总记录数的统计信息 + +### 业务价值 +- **界面友好**:支持树形结构显示,提高用户体验 +- **数据组织**:按设备分组,便于数据管理和查看 +- **扩展性**:为未来添加设备名称等扩展信息预留接口 +- **结构简化**:专注于树形结构,减少数据冗余 + +### 影响范围 +- **API响应**:专注于树形结构数据,简化响应格式 +- **前端渲染**:支持树形结构显示,提高数据展示效果 +- **数据组织**:按设备分组,便于数据管理和查看 +- **统计信息**:提供更详细的统计信息,便于数据分析 + ## 2025-01-29 修复DeviceRuntimes相关代码 ### 修改原因 @@ -3864,6 +4015,64 @@ const createResult = await coreNetworkConfigService.createCoreNetworkConfig({ --- +## 2025-01-29 为GetRuntimeDeviceCodesNotInActiveRuntimesAsync方法添加ORDER BY子句 + +### 修改概述 +根据用户需求,为 `GetRuntimeDeviceCodesNotInActiveRuntimesAsync` 方法添加 `ORDER BY tpl."Timestamp" DESC` 子句,确保查询结果按时间戳降序排列。 + +### 修改文件 +- `X1.Infrastructure/Repositories/Logging/ProtocolLogRepository.cs` - 修改SQL查询语句 + +### 修改内容 + +#### 1. SQL查询优化 +- **原查询**: +```sql +SELECT tpl."RuntimeCode", tpl."DeviceCode" +FROM "tb_protocol_logs" tpl +WHERE tpl."RuntimeCode" NOT IN ( + SELECT "RuntimeCode" + FROM "tb_cellular_device_runtimes" + WHERE "RuntimeStatus" = 1 +) +GROUP BY tpl."DeviceCode", tpl."RuntimeCode" +``` + +- **新查询**: +```sql +SELECT tpl."RuntimeCode", tpl."DeviceCode" +FROM "tb_protocol_logs" tpl +WHERE tpl."RuntimeCode" NOT IN ( + SELECT "RuntimeCode" + FROM "tb_cellular_device_runtimes" + WHERE "RuntimeStatus" = 1 +) +GROUP BY tpl."DeviceCode", tpl."RuntimeCode" +ORDER BY tpl."Timestamp" DESC +``` + +#### 2. 排序逻辑 +- **排序字段**:使用 `tpl."Timestamp"` 字段进行排序 +- **排序方向**:降序排列(DESC),最新的记录排在前面 +- **排序位置**:在 GROUP BY 子句之后添加 ORDER BY 子句 + +### 技术特性 +- **性能优化**:利用数据库索引进行排序,提高查询效率 +- **数据一致性**:确保查询结果按时间戳降序排列,便于用户查看最新数据 +- **向后兼容**:方法签名和返回类型保持不变,不影响现有调用 + +### 业务价值 +- **用户体验**:查询结果按时间顺序排列,便于用户快速找到最新记录 +- **数据展示**:在应用层和前端显示时,最新数据自动排在前面 +- **分析便利**:便于进行时间序列分析和趋势观察 + +### 影响范围 +- **查询结果**:现在返回的结果按时间戳降序排列 +- **性能影响**:轻微的排序开销,但提高了数据的有序性 +- **API行为**:API返回的数据更加有序和有意义 + +--- + ## 2025-01-29 修复消息详情抽屉滚动条问题 ### 修改概述 @@ -6775,4 +6984,219 @@ if (request.DeviceRuntimeStatus.HasValue) ### 验证结果 - 项目编译成功,无类型错误 - 保持了原有的业务逻辑功能 -- 符合用户"不要瞎改"的要求,只修改必要的类型适配 \ No newline at end of file +- 符合用户"不要瞎改"的要求,只修改必要的类型适配 + +--- + +## 2025-01-29 - 实现ProtocolLogRepository高性能NOT IN查询方法 + +### 修改原因 +根据用户需求,在 `ProtocolLogRepository` 中实现一个高性能的查询方法,用于获取不在活跃运行时状态中的协议日志RuntimeCode和DeviceCode。 + +### 修改文件 + +#### 1. 新增DTO类 +- `X1.Domain/Models/ProtocolLogRuntimeDeviceDto.cs` - 创建新的DTO类,用于返回RuntimeCode和DeviceCode + +#### 2. 接口扩展 +- `X1.Domain/Repositories/Logging/IProtocolLogRepository.cs` - 添加新方法接口定义 + +#### 3. 实现类扩展 +- `X1.Infrastructure/Repositories/Logging/ProtocolLogRepository.cs` - 实现新的查询方法 + +### 修改内容 + +#### 1. 创建ProtocolLogRuntimeDeviceDto +- **类定义**:创建专门用于返回RuntimeCode和DeviceCode的DTO类 +- **属性设计**: + - `RuntimeCode`:运行时代码(string类型) + - `DeviceCode`:设备代码(string类型) +- **用途**:专门用于高性能查询,只返回必要的字段 + +#### 2. 接口方法定义 +- **方法名**:`GetRuntimeDeviceCodesNotInActiveRuntimesAsync` +- **参数**:只包含 `CancellationToken` 参数 +- **返回类型**:`Task>` +- **功能说明**:获取不在活跃运行时状态中的协议日志RuntimeCode和DeviceCode + +#### 3. 实现方法 +- **SQL查询**:使用NOT IN子查询实现高性能查询 +```sql +SELECT tpl."RuntimeCode", tpl."DeviceCode" +FROM "tb_protocol_logs" tpl +WHERE tpl."RuntimeCode" NOT IN ( + SELECT "RuntimeCode" + FROM "tb_cellular_device_runtimes" + WHERE "RuntimeStatus" = 1 +) +GROUP BY tpl."DeviceCode", tpl."RuntimeCode" +``` +- **性能优化**: + - 使用NOT IN子查询,避免复杂的JOIN操作 + - 使用GROUP BY去重,减少返回数据量 + - 只查询必要字段,减少数据传输 +- **错误处理**:完整的异常处理和日志记录 +- **日志记录**:记录查询结果数量,便于性能监控 + +### 技术特性 + +#### 1. 高性能设计 +- **NOT IN子查询**:对于大数据表,NOT IN通常比LEFT JOIN + IS NULL性能更好 +- **字段选择**:只查询必要的RuntimeCode和DeviceCode字段 +- **去重处理**:使用GROUP BY确保结果唯一性 +- **索引利用**:充分利用现有的RuntimeCode索引 + +#### 2. 代码质量 +- **类型安全**:使用强类型的DTO类 +- **异常处理**:完整的try-catch异常处理 +- **日志记录**:详细的调试和错误日志 +- **文档注释**:完整的XML文档注释 + +#### 3. 可维护性 +- **单一职责**:方法职责明确,只负责特定查询 +- **代码复用**:使用现有的QueryRepository基础设施 +- **扩展性**:为未来添加更多过滤条件预留接口 + +### 业务价值 +- **查询性能**:提供高性能的NOT IN查询实现 +- **数据准确性**:确保返回的数据准确反映不在活跃状态中的协议日志 +- **系统稳定性**:通过完善的错误处理提高系统稳定性 +- **开发效率**:为上层应用提供便捷的查询接口 + +### 影响范围 +- **新增功能**:添加了新的查询方法,不影响现有功能 +- **接口扩展**:扩展了仓储接口,提供更多查询能力 +- **性能提升**:为大数据表查询提供了高性能实现 +- **代码质量**:提高了代码的可维护性和可读性 + +### 使用示例 +```csharp +// 在应用层使用 +var inactiveRuntimeDevices = await _protocolLogRepository + .GetRuntimeDeviceCodesNotInActiveRuntimesAsync(cancellationToken); + +foreach (var item in inactiveRuntimeDevices) +{ + Console.WriteLine($"RuntimeCode: {item.RuntimeCode}, DeviceCode: {item.DeviceCode}"); +} +``` + +### 注意事项 +- 该方法适用于大数据表查询,建议在非高峰期使用 +- 查询结果会根据GROUP BY去重,确保唯一性 +- 建议在生产环境中监控查询性能,必要时添加索引优化 + +--- + +## 2025-01-29 - 实现ProtocolLogs查询功能 + +### 修改原因 +根据用户需求,在 `X1.Application.Features.ProtocolLogs.Queries` 中实现查询功能,参考 `GetMessageDetailJsonById` 的实现模式,为 `GetRuntimeDeviceCodesNotInActiveRuntimes` 方法提供完整的应用层支持。 + +### 修改文件 + +#### 1. 新增查询类 +- `X1.Application/Features/ProtocolLogs/Queries/GetRuntimeDeviceCodesNotInActiveRuntimes/GetRuntimeDeviceCodesNotInActiveRuntimesQuery.cs` - 查询请求类 + +#### 2. 新增响应类 +- `X1.Application/Features/ProtocolLogs/Queries/GetRuntimeDeviceCodesNotInActiveRuntimes/GetRuntimeDeviceCodesNotInActiveRuntimesResponse.cs` - 查询响应类 + +#### 3. 新增查询处理器 +- `X1.Application/Features/ProtocolLogs/Queries/GetRuntimeDeviceCodesNotInActiveRuntimes/GetRuntimeDeviceCodesNotInActiveRuntimesQueryHandler.cs` - 查询处理器 + +#### 4. 扩展控制器 +- `X1.Presentation/Controllers/ProtocolLogsController.cs` - 添加新的API端点 + +### 修改内容 + +#### 1. 查询类设计 +- **类名**:`GetRuntimeDeviceCodesNotInActiveRuntimesQuery` +- **接口实现**:`IRequest>` +- **参数设计**:无需参数,直接获取所有符合条件的记录 +- **设计模式**:遵循CQRS模式,使用MediatR进行查询处理 + +#### 2. 响应类设计 +- **类名**:`GetRuntimeDeviceCodesNotInActiveRuntimesResponse` +- **属性设计**: + - `Items`:`IEnumerable` - 数据集合 + - `TotalCount`:`int` - 总数量 + - `HasData`:`bool` - 是否有数据(计算属性) +- **数据模型**:使用之前创建的 `ProtocolLogRuntimeDeviceDto` 作为数据模型 + +#### 3. 查询处理器实现 +- **类名**:`GetRuntimeDeviceCodesNotInActiveRuntimesQueryHandler` +- **接口实现**:`IRequestHandler>` +- **依赖注入**: + - `IProtocolLogRepository` - 协议日志仓储 + - `ILogger` - 日志记录器 +- **业务逻辑**: + - 调用仓储方法获取数据 + - 构建响应对象 + - 完整的错误处理和日志记录 + +#### 4. 控制器扩展 +- **API端点**:`GET /api/protocollogs/runtime-device-codes-not-in-active-runtimes` +- **方法名**:`GetRuntimeDeviceCodesNotInActiveRuntimes` +- **认证要求**:需要授权访问 +- **响应格式**:`OperationResult` +- **日志记录**:完整的请求和响应日志记录 + +### 技术特性 + +#### 1. CQRS模式实现 +- **查询分离**:查询逻辑与命令逻辑分离 +- **MediatR集成**:使用MediatR进行查询处理 +- **单一职责**:每个类都有明确的职责 +- **可测试性**:便于单元测试和集成测试 + +#### 2. 错误处理 +- **异常捕获**:完整的try-catch异常处理 +- **错误响应**:使用 `OperationResult` 统一错误响应格式 +- **日志记录**:详细的错误日志记录,便于问题排查 + +#### 3. 性能优化 +- **异步处理**:使用async/await进行异步操作 +- **取消令牌**:支持CancellationToken进行操作取消 +- **数据转换**:高效的数据转换和映射 + +#### 4. 代码质量 +- **文档注释**:完整的XML文档注释 +- **命名规范**:遵循C#命名规范 +- **代码结构**:清晰的代码结构和职责分离 + +### 业务价值 +- **功能完整性**:为NOT IN查询提供完整的应用层支持 +- **API一致性**:与其他API端点保持一致的接口设计 +- **开发效率**:提供标准化的查询处理模式 +- **系统稳定性**:通过完善的错误处理提高系统稳定性 + +### 影响范围 +- **新增功能**:添加了新的查询功能和API端点 +- **接口扩展**:扩展了应用层的查询能力 +- **架构完善**:完善了CQRS模式的实现 +- **开发体验**:提供了标准化的查询开发模式 + +### 使用示例 +```csharp +// 在控制器中使用 +[HttpGet("runtime-device-codes-not-in-active-runtimes")] +public async Task> GetRuntimeDeviceCodesNotInActiveRuntimes() +{ + var query = new GetRuntimeDeviceCodesNotInActiveRuntimesQuery(); + var result = await mediator.Send(query); + return result; +} + +// 在应用层使用 +var handler = new GetRuntimeDeviceCodesNotInActiveRuntimesQueryHandler(repository, logger); +var query = new GetRuntimeDeviceCodesNotInActiveRuntimesQuery(); +var result = await handler.Handle(query, CancellationToken.None); +``` + +### 注意事项 +- 该查询适用于大数据表,建议在非高峰期使用 +- 查询结果会返回所有符合条件的记录,注意内存使用 +- 建议在生产环境中监控API性能,必要时进行优化 +- 遵循现有的错误处理和日志记录模式 + +--- \ No newline at end of file