Browse Source

新增 GetRuntimeDeviceCodesNotInActiveRuntimesAsync

feature/x1-web-request
hyh 3 days ago
parent
commit
61ae3e587f
  1. 12
      src/X1.Application/Features/ProtocolLogs/Queries/GetRuntimeDeviceCodesNotInActiveRuntimes/GetRuntimeDeviceCodesNotInActiveRuntimesQuery.cs
  2. 92
      src/X1.Application/Features/ProtocolLogs/Queries/GetRuntimeDeviceCodesNotInActiveRuntimes/GetRuntimeDeviceCodesNotInActiveRuntimesQueryHandler.cs
  3. 29
      src/X1.Application/Features/ProtocolLogs/Queries/GetRuntimeDeviceCodesNotInActiveRuntimes/GetRuntimeDeviceCodesNotInActiveRuntimesResponse.cs
  4. 23
      src/X1.Domain/Models/ProtocolLogRuntimeDeviceDto.cs
  5. 49
      src/X1.Domain/Models/ProtocolLogRuntimeDeviceTreeDto.cs
  6. 7
      src/X1.Domain/Repositories/Logging/IProtocolLogRepository.cs
  7. 36
      src/X1.Infrastructure/Repositories/Logging/ProtocolLogRepository.cs
  8. 24
      src/X1.Presentation/Controllers/ProtocolLogsController.cs
  9. 426
      src/modify.md

12
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;
/// <summary>
/// 获取不在活跃运行时状态中的协议日志RuntimeCode和DeviceCode查询
/// </summary>
public class GetRuntimeDeviceCodesNotInActiveRuntimesQuery : IRequest<OperationResult<GetRuntimeDeviceCodesNotInActiveRuntimesResponse>>
{
// 此查询不需要参数,直接获取所有符合条件的记录
}

92
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;
/// <summary>
/// 获取不在活跃运行时状态中的协议日志RuntimeCode和DeviceCode查询处理器
/// </summary>
public class GetRuntimeDeviceCodesNotInActiveRuntimesQueryHandler : IRequestHandler<GetRuntimeDeviceCodesNotInActiveRuntimesQuery, OperationResult<GetRuntimeDeviceCodesNotInActiveRuntimesResponse>>
{
private readonly IProtocolLogRepository _protocolLogRepository;
private readonly ILogger<GetRuntimeDeviceCodesNotInActiveRuntimesQueryHandler> _logger;
/// <summary>
/// 初始化查询处理器
/// </summary>
public GetRuntimeDeviceCodesNotInActiveRuntimesQueryHandler(
IProtocolLogRepository protocolLogRepository,
ILogger<GetRuntimeDeviceCodesNotInActiveRuntimesQueryHandler> logger)
{
_protocolLogRepository = protocolLogRepository;
_logger = logger;
}
/// <summary>
/// 处理获取不在活跃运行时状态中的协议日志RuntimeCode和DeviceCode查询
/// </summary>
public async Task<OperationResult<GetRuntimeDeviceCodesNotInActiveRuntimesResponse>> 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<GetRuntimeDeviceCodesNotInActiveRuntimesResponse>.CreateSuccess(response);
}
catch (Exception ex)
{
_logger.LogError(ex, "获取不在活跃运行时状态中的协议日志RuntimeCode和DeviceCode时发生错误");
return OperationResult<GetRuntimeDeviceCodesNotInActiveRuntimesResponse>.CreateFailure($"获取协议日志运行时设备代码时发生错误: {ex.Message}");
}
}
/// <summary>
/// 构建树形结构数据
/// 将平面数据按DeviceCode分组,每个设备下包含其对应的RuntimeCode列表
/// </summary>
/// <param name="items">平面数据列表</param>
/// <returns>树形结构数据</returns>
private IEnumerable<ProtocolLogRuntimeDeviceTreeDto> BuildTreeStructure(IEnumerable<ProtocolLogRuntimeDeviceDto> 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;
}
}

29
src/X1.Application/Features/ProtocolLogs/Queries/GetRuntimeDeviceCodesNotInActiveRuntimes/GetRuntimeDeviceCodesNotInActiveRuntimesResponse.cs

@ -0,0 +1,29 @@
using CellularManagement.Domain.Models;
namespace CellularManagement.Application.Features.ProtocolLogs.Queries.GetRuntimeDeviceCodesNotInActiveRuntimes;
/// <summary>
/// 获取不在活跃运行时状态中的协议日志RuntimeCode和DeviceCode响应
/// </summary>
public class GetRuntimeDeviceCodesNotInActiveRuntimesResponse
{
/// <summary>
/// 协议日志运行时设备树形结构(按设备分组)
/// </summary>
public IEnumerable<ProtocolLogRuntimeDeviceTreeDto> TreeItems { get; set; } = Enumerable.Empty<ProtocolLogRuntimeDeviceTreeDto>();
/// <summary>
/// 总数量
/// </summary>
public int TotalCount { get; set; }
/// <summary>
/// 设备数量
/// </summary>
public int DeviceCount { get; set; }
/// <summary>
/// 是否有树形数据
/// </summary>
public bool HasData => TreeItems.Any();
}

23
src/X1.Domain/Models/ProtocolLogRuntimeDeviceDto.cs

@ -0,0 +1,23 @@
namespace CellularManagement.Domain.Models;
/// <summary>
/// 协议日志运行时设备DTO
/// 用于返回协议日志的RuntimeCode和DeviceCode
/// </summary>
public class ProtocolLogRuntimeDeviceDto
{
/// <summary>
/// 运行时代码
/// </summary>
public string RuntimeCode { get; set; } = string.Empty;
/// <summary>
/// 设备代码
/// </summary>
public string DeviceCode { get; set; } = string.Empty;
/// <summary>
/// 时间戳
/// </summary>
public long Timestamp { get; set; }
}

49
src/X1.Domain/Models/ProtocolLogRuntimeDeviceTreeDto.cs

@ -0,0 +1,49 @@
namespace CellularManagement.Domain.Models;
/// <summary>
/// 协议日志运行时设备树形结构DTO
/// 用于表示按设备分组的运行时数据,便于前端渲染树形结构
/// </summary>
public class ProtocolLogRuntimeDeviceTreeDto
{
/// <summary>
/// 设备代码
/// </summary>
public string DeviceCode { get; set; } = string.Empty;
/// <summary>
/// 设备名称(可选,用于显示)
/// </summary>
public string? DeviceName { get; set; }
/// <summary>
/// 该设备下的运行时列表
/// </summary>
public List<ProtocolLogRuntimeInfo> Runtimes { get; set; } = new();
/// <summary>
/// 该设备下的运行时数量
/// </summary>
public int RuntimeCount => Runtimes.Count;
}
/// <summary>
/// 协议日志运行时信息
/// </summary>
public class ProtocolLogRuntimeInfo
{
/// <summary>
/// 运行时代码
/// </summary>
public string RuntimeCode { get; set; } = string.Empty;
/// <summary>
/// 时间戳
/// </summary>
public long Timestamp { get; set; }
/// <summary>
/// 格式化后的时间显示
/// </summary>
public string FormattedTime => DateTimeOffset.FromUnixTimeMilliseconds(Timestamp).ToString("yyyy-MM-dd HH:mm:ss.fff");
}

7
src/X1.Domain/Repositories/Logging/IProtocolLogRepository.cs

@ -84,4 +84,11 @@ public interface IProtocolLogRepository : IBaseRepository<ProtocolLog>
/// <returns>MessageDetailJson内容</returns>
Task<string?> GetMessageDetailJsonByIdAsync(string id, CancellationToken cancellationToken = default);
/// <summary>
/// 获取不在活跃运行时状态中的协议日志RuntimeCode和DeviceCode
/// </summary>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>RuntimeCode和DeviceCode列表</returns>
Task<IEnumerable<ProtocolLogRuntimeDeviceDto>> GetRuntimeDeviceCodesNotInActiveRuntimesAsync(CancellationToken cancellationToken = default);
}

36
src/X1.Infrastructure/Repositories/Logging/ProtocolLogRepository.cs

@ -320,4 +320,40 @@ public class ProtocolLogRepository : BaseRepository<ProtocolLog>, IProtocolLogRe
throw;
}
}
/// <summary>
/// 获取不在活跃运行时状态中的协议日志RuntimeCode和DeviceCode
/// </summary>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>RuntimeCode和DeviceCode列表</returns>
public async Task<IEnumerable<ProtocolLogRuntimeDeviceDto>> 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<ProtocolLogRuntimeDeviceDto>(sql, Array.Empty<object>(), cancellationToken);
_logger.LogDebug("获取不在活跃运行时状态中的协议日志RuntimeCode和DeviceCode,结果数量:{Count}", result.Count());
return result;
}
catch (Exception ex)
{
_logger.LogError(ex, "获取不在活跃运行时状态中的协议日志RuntimeCode和DeviceCode时发生错误");
throw;
}
}
}

24
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;
}
/// <summary>
/// 获取不在活跃运行时状态中的协议日志RuntimeCode和DeviceCode
/// </summary>
/// <returns>协议日志运行时设备代码列表</returns>
[HttpGet("runtime-device-codes-not-in-active-runtimes")]
public async Task<OperationResult<GetRuntimeDeviceCodesNotInActiveRuntimesResponse>> 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;
}
}

426
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)
### 验证结果
- 项目编译成功,无类型错误
- 保持了原有的业务逻辑功能
- 符合用户"不要瞎改"的要求,只修改必要的类型适配
- 符合用户"不要瞎改"的要求,只修改必要的类型适配
---
## 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<IEnumerable<ProtocolLogRuntimeDeviceDto>>`
- **功能说明**:获取不在活跃运行时状态中的协议日志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<OperationResult<GetRuntimeDeviceCodesNotInActiveRuntimesResponse>>`
- **参数设计**:无需参数,直接获取所有符合条件的记录
- **设计模式**:遵循CQRS模式,使用MediatR进行查询处理
#### 2. 响应类设计
- **类名**:`GetRuntimeDeviceCodesNotInActiveRuntimesResponse`
- **属性设计**
- `Items`:`IEnumerable<ProtocolLogRuntimeDeviceDto>` - 数据集合
- `TotalCount`:`int` - 总数量
- `HasData`:`bool` - 是否有数据(计算属性)
- **数据模型**:使用之前创建的 `ProtocolLogRuntimeDeviceDto` 作为数据模型
#### 3. 查询处理器实现
- **类名**:`GetRuntimeDeviceCodesNotInActiveRuntimesQueryHandler`
- **接口实现**:`IRequestHandler<GetRuntimeDeviceCodesNotInActiveRuntimesQuery, OperationResult<GetRuntimeDeviceCodesNotInActiveRuntimesResponse>>`
- **依赖注入**
- `IProtocolLogRepository` - 协议日志仓储
- `ILogger<GetRuntimeDeviceCodesNotInActiveRuntimesQueryHandler>` - 日志记录器
- **业务逻辑**
- 调用仓储方法获取数据
- 构建响应对象
- 完整的错误处理和日志记录
#### 4. 控制器扩展
- **API端点**:`GET /api/protocollogs/runtime-device-codes-not-in-active-runtimes`
- **方法名**:`GetRuntimeDeviceCodesNotInActiveRuntimes`
- **认证要求**:需要授权访问
- **响应格式**:`OperationResult<GetRuntimeDeviceCodesNotInActiveRuntimesResponse>`
- **日志记录**:完整的请求和响应日志记录
### 技术特性
#### 1. CQRS模式实现
- **查询分离**:查询逻辑与命令逻辑分离
- **MediatR集成**:使用MediatR进行查询处理
- **单一职责**:每个类都有明确的职责
- **可测试性**:便于单元测试和集成测试
#### 2. 错误处理
- **异常捕获**:完整的try-catch异常处理
- **错误响应**:使用 `OperationResult<T>` 统一错误响应格式
- **日志记录**:详细的错误日志记录,便于问题排查
#### 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<OperationResult<GetRuntimeDeviceCodesNotInActiveRuntimesResponse>> 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性能,必要时进行优化
- 遵循现有的错误处理和日志记录模式
---
Loading…
Cancel
Save