Browse Source

feat: 修复StopTerminalServiceCommandHandler并优化设备离线逻辑

主要修改:
- 修复StopTerminalServiceCommandHandler语法错误和方法签名问题
- 修复WebSocket API接口不匹配问题(POST vs DELETE)
- 优化设备离线状态设置逻辑,只处理在线设备
- 改进代码命名规范和日志记录

技术细节:
- 修复第97行缺少分号和第94行返回类型错误
- 将CreateWebSocketConnectionAsync重命名为DisconnectWebSocketAsync
- 修复C#客户端HTTP方法从DELETE改为POST以匹配Python API
- 优化设备处理逻辑,只获取和更新在线设备,提高性能
- 添加详细的操作日志记录

影响范围:
- StopTerminalServiceCommandHandler: 停止终端服务功能
- TestTerminalRequestClient: WebSocket客户端断开连接
- 设备状态管理: 性能优化和业务逻辑改进

测试建议:
- 验证WebSocket客户端断开连接功能
- 检查设备离线状态设置是否正确
- 确认大量设备场景下的性能表现
refactor/permission-config
root 3 months ago
parent
commit
6198905bd6
  1. 8
      src/X1.Application/Features/TerminalServices/Commands/StartTerminalService/StartTerminalServiceCommandHandler.cs
  2. 95
      src/X1.Application/Features/TerminalServices/Commands/StopTerminalService/StopTerminalServiceCommandHandler.cs
  3. 7
      src/X1.DynamicClientCore/Service/TestTerminalRequestClient.cs
  4. 107
      src/modify_20250121_device_offline_optimization.md
  5. 62
      src/modify_20250121_stopterminalservice_fix.md
  6. 101
      src/modify_20250121_websocket_api_fix.md

8
src/X1.Application/Features/TerminalServices/Commands/StartTerminalService/StartTerminalServiceCommandHandler.cs

@ -5,6 +5,7 @@ using X1.Domain.Repositories.Terminal;
using X1.Domain.Services;
using X1.Domain.ThirdPartyDeviceHttpClient.ITerminal;
using X1.Domain.Entities.Terminal;
using X1.Domain.Repositories.Base;
namespace X1.Application.Features.TerminalServices.Commands.StartTerminalService;
@ -17,6 +18,7 @@ public class StartTerminalServiceCommandHandler : IRequestHandler<StartTerminalS
private readonly ILogger<StartTerminalServiceCommandHandler> _logger;
private readonly ICurrentUserService _currentUserService;
private readonly IBaseTerminalClient _terminalClient;
private readonly IUnitOfWork _unitOfWork;
/// <summary>
/// 初始化命令处理器
@ -25,12 +27,14 @@ public class StartTerminalServiceCommandHandler : IRequestHandler<StartTerminalS
ITerminalServiceRepository serviceRepository,
IBaseTerminalClient terminalClient,
ILogger<StartTerminalServiceCommandHandler> logger,
ICurrentUserService currentUserService)
ICurrentUserService currentUserService,
IUnitOfWork unitOfWork)
{
_serviceRepository = serviceRepository;
_logger = logger;
_currentUserService = currentUserService;
_terminalClient = terminalClient;
_unitOfWork = unitOfWork;
}
/// <summary>
@ -111,6 +115,8 @@ public class StartTerminalServiceCommandHandler : IRequestHandler<StartTerminalS
// 保存到数据库
_serviceRepository.UpdateService(existingService);
await _unitOfWork.SaveChangesAsync();
// 构建响应
var response = new StartTerminalServiceResponse
{

95
src/X1.Application/Features/TerminalServices/Commands/StopTerminalService/StopTerminalServiceCommandHandler.cs

@ -1,8 +1,13 @@
using System.Xml.Linq;
using MediatR;
using Microsoft.Extensions.Logging;
using X1.Application.Features.TerminalServices.Commands.StartTerminalService;
using X1.Domain.Common;
using X1.Domain.Entities.Terminal;
using X1.Domain.Repositories.Base;
using X1.Domain.Repositories.Terminal;
using X1.Domain.Services;
using X1.Domain.ThirdPartyDeviceHttpClient.ITerminal;
namespace X1.Application.Features.TerminalServices.Commands.StopTerminalService;
@ -14,18 +19,26 @@ public class StopTerminalServiceCommandHandler : IRequestHandler<StopTerminalSer
private readonly ITerminalServiceRepository _serviceRepository;
private readonly ILogger<StopTerminalServiceCommandHandler> _logger;
private readonly ICurrentUserService _currentUserService;
private readonly IUnitOfWork _unitOfWork;
private readonly ITerminalDeviceRepository _deviceRepository;
private readonly IBaseTerminalClient _terminalClient;
/// <summary>
/// 初始化命令处理器
/// </summary>
public StopTerminalServiceCommandHandler(
ITerminalServiceRepository serviceRepository,
ILogger<StopTerminalServiceCommandHandler> logger,
ICurrentUserService currentUserService)
ICurrentUserService currentUserService,
IUnitOfWork unitOfWork,
ITerminalDeviceRepository deviceRepository,
IBaseTerminalClient terminalClient)
{
_serviceRepository = serviceRepository;
_logger = logger;
_currentUserService = currentUserService;
_unitOfWork = unitOfWork;
_deviceRepository = deviceRepository;
_terminalClient = terminalClient;
}
/// <summary>
@ -59,9 +72,42 @@ public class StopTerminalServiceCommandHandler : IRequestHandler<StopTerminalSer
existingService.StopService();
existingService.UpdateAuditInfo(currentUser);
// 只获取在线设备并设置为离线状态
var onlineDevices = await _deviceRepository.GetOnlineDevicesAsync(cancellationToken);
if (onlineDevices.Any())
{
_logger.LogInformation("开始设置在线设备为离线状态,在线设备数量: {OnlineDeviceCount}", onlineDevices.Count);
foreach (var device in onlineDevices)
{
device.SetOffline();
}
_deviceRepository.UpdateRange(onlineDevices);
_logger.LogInformation("成功设置 {DeviceCount} 个设备为离线状态", onlineDevices.Count);
}
else
{
_logger.LogInformation("没有在线设备需要设置为离线状态");
}
// 保存到数据库
_serviceRepository.UpdateService(existingService);
await _unitOfWork.SaveChangesAsync();
var serviceEndpoint = _currentUserService.GetCurrentServiceEndpoint();
// 验证服务端点信息
if (string.IsNullOrWhiteSpace(serviceEndpoint))
{
_logger.LogError("无法获取服务端点信息,服务ID: {ServiceId}, 用户: {UserId}", request.ServiceId, currentUser);
return OperationResult<StopTerminalServiceResponse>.CreateFailure("无法获取服务端点信息,请检查系统配置");
}
await DisconnectWebSocketAsync(existingService.ServiceCode, serviceEndpoint, cancellationToken);
// 构建响应
var response = new StopTerminalServiceResponse
{
@ -81,4 +127,49 @@ public class StopTerminalServiceCommandHandler : IRequestHandler<StopTerminalSer
return OperationResult<StopTerminalServiceResponse>.CreateFailure("停止终端服务时发生错误");
}
}
/// <summary>
/// 断开WebSocket连接
/// </summary>
private async Task<OperationResult<bool>> DisconnectWebSocketAsync(
string serviceCode,
string serviceEndpoint,
CancellationToken cancellationToken)
{
try
{
_logger.LogDebug("断开WebSocket连接请求 - 服务编码: {ServiceCode}, 服务端点: {ServiceEndpoint}",
serviceCode, serviceEndpoint);
// 断开WebSocket连接
var webSocketResponse = await _terminalClient.DisconnectTerminalWebSocketAsync(serviceCode, cancellationToken: cancellationToken);
if (webSocketResponse == null)
{
_logger.LogError("WebSocket断开连接响应为空,服务编码: {ServiceCode}", serviceCode);
return OperationResult<bool>.CreateFailure("WebSocket断开连接失败:服务器无响应");
}
if (!webSocketResponse.Success)
{
var errorMessage = string.IsNullOrWhiteSpace(webSocketResponse.Message)
? "未知错误"
: webSocketResponse.Message;
_logger.LogError("WebSocket断开连接失败,服务编码: {ServiceCode}, 错误信息: {ErrorMessage}",
serviceCode, errorMessage);
return OperationResult<bool>.CreateFailure($"WebSocket断开连接失败:{errorMessage}");
}
_logger.LogInformation("WebSocket断开连接成功,服务编码: {ServiceCode}", serviceCode);
return OperationResult<bool>.CreateSuccess(true);
}
catch (Exception ex)
{
_logger.LogError(ex, "断开WebSocket连接时发生异常,服务编码: {ServiceCode}, 服务端点: {ServiceEndpoint}",
serviceCode, serviceEndpoint);
return OperationResult<bool>.CreateFailure($"WebSocket断开连接异常:{ex.Message}");
}
}
}

7
src/X1.DynamicClientCore/Service/TestTerminalRequestClient.cs

@ -189,9 +189,9 @@ namespace X1.DynamicClientCore.Service
/// <exception cref="ArgumentNullException">当clientName为null时抛出</exception>
/// <exception cref="ArgumentException">当clientName为空时抛出</exception>
/// <remarks>
/// 该方法通过HTTP DELETE请求调用WebSocket客户端断开连接API,断开指定的WebSocket客户端连接。
/// 该方法通过HTTP POST请求调用WebSocket客户端断开连接API,断开指定的WebSocket客户端连接。
/// 使用固定的服务名称"websocket"进行API调用,端点格式为"/api/v1/websocket/clients/{clientName}/disconnect"。
/// 返回的TerminalWebSocketResponse包含断开连接的结果信息。
/// 返回的TestTerminalResponse包含断开连接的结果信息。
/// </remarks>
public async Task<TestTerminalResponse?> DisconnectTerminalWebSocketAsync(
string clientName,
@ -210,9 +210,10 @@ namespace X1.DynamicClientCore.Service
var endpoint = $"websocket/clients/{clientName}/disconnect";
// 调用WebSocket客户端断开连接API
var response = await _dynamicHttpClient.DeleteAsync<TestTerminalResponse>(
var response = await _dynamicHttpClient.PostAsync<TestTerminalResponse>(
clientName,
endpoint,
null, // POST请求体为空
options,
cancellationToken);

107
src/modify_20250121_device_offline_optimization.md

@ -0,0 +1,107 @@
# 2025-01-21 优化设备离线状态设置逻辑
## 概述
优化了 `StopTerminalServiceCommandHandler` 中设备离线状态设置的逻辑,采用方案1:只更新在线设备为离线状态,提高性能并确保业务逻辑的合理性。
## 问题分析
### 原始代码问题
```csharp
var devices = await _deviceRepository.GetAllAsync(); // 加载所有设备到内存
foreach (var device in devices)
{
device.SetOffline(); // 逐个处理
}
_deviceRepository.UpdateRange(devices); // 批量更新
```
**问题**:
1. **性能问题**: 当设备数量很大时,会消耗大量内存
2. **业务逻辑问题**: 即使设备已经是离线状态,也会重复处理
3. **资源浪费**: 加载所有设备,但只需要处理在线设备
## 优化方案
### 方案1: 只更新在线设备(已采用)
```csharp
// 只获取在线设备并设置为离线状态
var onlineDevices = await _deviceRepository.GetOnlineDevicesAsync(cancellationToken);
if (onlineDevices.Any())
{
_logger.LogInformation("开始设置在线设备为离线状态,在线设备数量: {OnlineDeviceCount}", onlineDevices.Count);
foreach (var device in onlineDevices)
{
device.SetOffline();
}
_deviceRepository.UpdateRange(onlineDevices);
_logger.LogInformation("成功设置 {DeviceCount} 个设备为离线状态", onlineDevices.Count);
}
else
{
_logger.LogInformation("没有在线设备需要设置为离线状态");
}
```
## 优化效果
### 1. 性能提升
- **内存使用**: 只加载在线设备,减少内存消耗
- **处理效率**: 只处理需要更新的设备,避免无效操作
- **数据库负载**: 减少不必要的数据传输
### 2. 业务逻辑优化
- **合理性**: 只处理在线设备,符合业务逻辑
- **一致性**: 服务停止时,所有在线设备必须离线
- **准确性**: 避免重复处理已离线的设备
### 3. 日志完善
- **操作日志**: 记录在线设备数量和处理结果
- **状态跟踪**: 清晰记录设备状态变更过程
- **问题排查**: 便于后续问题定位和调试
## 技术实现
### 使用的仓储方法
```csharp
// 获取在线设备列表
var onlineDevices = await _deviceRepository.GetOnlineDevicesAsync(cancellationToken);
```
### 设备状态设置
```csharp
// 设置设备离线状态
device.SetOffline(); // 设置状态为离线,更新LastDisconnectedAt和UpdatedAt
```
### 批量更新
```csharp
// 批量更新设备状态
_deviceRepository.UpdateRange(onlineDevices);
```
## 业务逻辑说明
### 服务停止时的设备处理
1. **服务停止**: 终端服务停止运行
2. **设备离线**: 所有在线设备必须设置为离线状态
3. **状态同步**: 确保设备状态与服务状态保持一致
4. **日志记录**: 记录设备状态变更过程
### 为什么只处理在线设备
- **效率**: 离线设备无需重复处理
- **逻辑**: 只有在线设备需要设置为离线
- **性能**: 减少不必要的数据操作
- **准确性**: 避免状态混乱
## 文件修改
- **文件**: `X1.Application/Features/TerminalServices/Commands/StopTerminalService/StopTerminalServiceCommandHandler.cs`
- **修改类型**: 性能优化、业务逻辑优化、日志完善
- **影响范围**: 停止终端服务功能
## 测试建议
1. 验证只有在线设备被设置为离线状态
2. 检查离线设备不会被重复处理
3. 确认日志记录正确显示设备数量
4. 测试大量设备场景下的性能表现

62
src/modify_20250121_stopterminalservice_fix.md

@ -0,0 +1,62 @@
# 2025-01-21 修复 StopTerminalServiceCommandHandler 问题
## 概述
修复 `StopTerminalServiceCommandHandler` 中的语法错误、方法签名问题、冗余字段和命名规范问题。
## 主要修复
### 1. 语法错误修复
- **第97行**: 添加缺失的分号
- **第94行**: 修正返回类型从 `StartTerminalServiceResponse``StopTerminalServiceResponse`
### 2. 方法签名修复
- **重命名方法**: `CreateWebSocketConnectionAsync``DisconnectWebSocketAsync`
- **修正参数**: 移除未使用的 `webSocketRequest` 参数
- **更新日志**: 将"创建连接"改为"断开连接",更符合停止服务的语义
### 3. 冗余字段删除
- **删除未使用变量**: 移除 `webSocketRequest` 相关代码
- **清理日志参数**: 移除不存在的变量引用
### 4. 命名规范改进
- **变量命名**: `ss``devices`,`item` → `device`
- **方法命名**: 使用更清晰的方法名 `DisconnectWebSocketAsync`
- **参数命名**: 使用 `serviceCode` 而不是 `ServiceCode`
### 5. 代码优化
- **添加注释**: 为私有方法添加完整的XML文档注释
- **日志优化**: 改进日志消息的清晰度和准确性
- **错误处理**: 保持一致的错误处理模式
## 技术细节
### 修复前的问题
```csharp
// 语法错误:缺少分号
await CreateWebSocketConnectionAsync(existingService.ServiceCode)
// 返回类型错误
return OperationResult<StartTerminalServiceResponse>.CreateFailure(...)
// 未定义的变量
_logger.LogDebug("WebSocket连接请求参数 - 客户端名称: {ClientName}, 连接地址: {Url}, 心跳间隔: {HeartbeatInterval}秒",
webSocketRequest.Name, webSocketRequest.Url, webSocketRequest.HeartbeatInterval);
```
### 修复后的代码
```csharp
// 正确的语法
await DisconnectWebSocketAsync(existingService.ServiceCode, serviceEndpoint, cancellationToken);
// 正确的返回类型
return OperationResult<StopTerminalServiceResponse>.CreateFailure(...);
// 清晰的日志
_logger.LogDebug("断开WebSocket连接请求 - 服务编码: {ServiceCode}, 服务端点: {ServiceEndpoint}",
serviceCode, serviceEndpoint);
```
## 文件修改
- **文件**: `X1.Application/Features/TerminalServices/Commands/StopTerminalService/StopTerminalServiceCommandHandler.cs`
- **修改类型**: 语法修复、方法重构、命名优化
- **影响范围**: 停止终端服务功能

101
src/modify_20250121_websocket_api_fix.md

@ -0,0 +1,101 @@
# 2025-01-21 修复 WebSocket API 接口不匹配问题
## 概述
修复了C#客户端与Python WebSocket API之间的HTTP方法不匹配问题,确保接口调用的一致性。
## 问题分析
### 发现的不匹配问题
1. **HTTP方法不匹配**
- **Python API**: 使用 `@router.post` (POST方法)
- **C# 客户端**: 使用 `DeleteAsync` (DELETE方法)
2. **接口对应关系**
- **Python API**: `/websocket/clients/{name}/disconnect`
- **C# 客户端**: `websocket/clients/{clientName}/disconnect`
## 修复内容
### 1. HTTP方法修复
**文件**: `X1.DynamicClientCore/Service/TestTerminalRequestClient.cs`
#### 修复前
```csharp
// 调用WebSocket客户端断开连接API
var response = await _dynamicHttpClient.DeleteAsync<TestTerminalResponse>(
clientName,
endpoint,
options,
cancellationToken);
```
#### 修复后
```csharp
// 调用WebSocket客户端断开连接API
var response = await _dynamicHttpClient.PostAsync<TestTerminalResponse>(
clientName,
endpoint,
null, // POST请求体为空
options,
cancellationToken);
```
### 2. 注释更新
更新了方法注释,将HTTP方法从DELETE改为POST:
```csharp
/// <remarks>
/// 该方法通过HTTP POST请求调用WebSocket客户端断开连接API,断开指定的WebSocket客户端连接。
/// 使用固定的服务名称"websocket"进行API调用,端点格式为"/api/v1/websocket/clients/{clientName}/disconnect"。
/// 返回的TestTerminalResponse包含断开连接的结果信息。
/// </remarks>
```
## 接口对应关系确认
### Python API
```python
@router.post("/websocket/clients/{name}/disconnect", summary="断开WebSocket客户端", response_model=SuccessResponse)
@handle_api_errors
async def disconnect_client(name: str):
"""断开已存在客户端"""
success = await websocket_manager.disconnect_client(name)
# ...
```
### C# 客户端
```csharp
public async Task<TestTerminalResponse?> DisconnectTerminalWebSocketAsync(
string clientName,
RequestOptions? options = null,
CancellationToken cancellationToken = default)
{
var endpoint = $"websocket/clients/{clientName}/disconnect";
var response = await _dynamicHttpClient.PostAsync<TestTerminalResponse>(
clientName,
endpoint,
null, // POST请求体为空
options,
cancellationToken);
// ...
}
```
## 验证结果
### ✅ 修复完成
- **HTTP方法**: POST ✅
- **端点格式**: `/websocket/clients/{name}/disconnect`
- **参数传递**: 路径参数 ✅
- **请求体**: 空请求体 ✅
### 影响范围
- **文件**: `X1.DynamicClientCore/Service/TestTerminalRequestClient.cs`
- **方法**: `DisconnectTerminalWebSocketAsync`
- **功能**: WebSocket客户端断开连接
- **调用方**: `StopTerminalServiceCommandHandler`
## 测试建议
1. 验证WebSocket客户端断开连接功能
2. 检查API调用是否成功
3. 确认错误处理机制正常工作
Loading…
Cancel
Save