Browse Source

修复Swagger schemaId冲突:提取共享的BatchOperationSummary类

feature/x1-web-request
hyh 2 days ago
parent
commit
d9ef1cb654
  1. 32
      src/X1.Application/Features/DeviceRuntimes/Commands/StartDeviceRuntime/StartDeviceRuntimeResponse.cs
  2. 28
      src/X1.Application/Features/DeviceRuntimes/Commands/StopDeviceRuntime/StopDeviceRuntimeResponse.cs
  3. 27
      src/X1.Domain/Common/BatchOperationSummary.cs
  4. 120
      src/X1.Presentation/Controllers/ProtocolLogsController.cs
  5. 579
      src/modify.md

32
src/X1.Application/Features/DeviceRuntimes/Commands/StartDeviceRuntime/StartDeviceRuntimeResponse.cs

@ -1,3 +1,5 @@
using CellularManagement.Domain.Common;
namespace CellularManagement.Application.Features.DeviceRuntimes.Commands.StartDeviceRuntime;
/// <summary>
@ -25,8 +27,6 @@ public class StartDeviceRuntimeResponse
/// </summary>
public string? NetworkStackCode { get; set; }
/// <summary>
/// 更新时间
/// </summary>
@ -68,8 +68,6 @@ public class DeviceStartResult
/// </summary>
public string? NetworkStackCode { get; set; }
/// <summary>
/// 更新时间
/// </summary>
@ -85,29 +83,3 @@ public class DeviceStartResult
/// </summary>
public string? ErrorMessage { get; set; }
}
/// <summary>
/// 批量操作统计
/// </summary>
public class BatchOperationSummary
{
/// <summary>
/// 总设备数
/// </summary>
public int TotalCount { get; set; }
/// <summary>
/// 成功数量
/// </summary>
public int SuccessCount { get; set; }
/// <summary>
/// 失败数量
/// </summary>
public int FailureCount { get; set; }
/// <summary>
/// 成功率
/// </summary>
public double SuccessRate => TotalCount > 0 ? (double)SuccessCount / TotalCount * 100 : 0;
}

28
src/X1.Application/Features/DeviceRuntimes/Commands/StopDeviceRuntime/StopDeviceRuntimeResponse.cs

@ -1,3 +1,5 @@
using CellularManagement.Domain.Common;
namespace CellularManagement.Application.Features.DeviceRuntimes.Commands.StopDeviceRuntime;
/// <summary>
@ -91,29 +93,3 @@ public class DeviceStopResult
/// </summary>
public string? ErrorMessage { get; set; }
}
/// <summary>
/// 批量操作统计
/// </summary>
public class BatchOperationSummary
{
/// <summary>
/// 总设备数
/// </summary>
public int TotalCount { get; set; }
/// <summary>
/// 成功数量
/// </summary>
public int SuccessCount { get; set; }
/// <summary>
/// 失败数量
/// </summary>
public int FailureCount { get; set; }
/// <summary>
/// 成功率
/// </summary>
public double SuccessRate => TotalCount > 0 ? (double)SuccessCount / TotalCount * 100 : 0;
}

27
src/X1.Domain/Common/BatchOperationSummary.cs

@ -0,0 +1,27 @@
namespace CellularManagement.Domain.Common;
/// <summary>
/// 批量操作统计
/// </summary>
public class BatchOperationSummary
{
/// <summary>
/// 总设备数
/// </summary>
public int TotalCount { get; set; }
/// <summary>
/// 成功数量
/// </summary>
public int SuccessCount { get; set; }
/// <summary>
/// 失败数量
/// </summary>
public int FailureCount { get; set; }
/// <summary>
/// 成功率
/// </summary>
public double SuccessRate => TotalCount > 0 ? (double)SuccessCount / TotalCount * 100 : 0;
}

120
src/X1.Presentation/Controllers/ProtocolLogsController.cs

@ -30,44 +30,76 @@ public class ProtocolLogsController : ApiController
_logger = logger;
}
/// <summary>
/// 协议日志查询请求模型
/// </summary>
public class ProtocolLogsQueryRequest
{
/// <summary>
/// 设备代码
/// </summary>
public string? DeviceCode { get; set; }
/// <summary>
/// 开始时间戳
/// </summary>
public long? StartTimestamp { get; set; }
/// <summary>
/// 结束时间戳
/// </summary>
public long? EndTimestamp { get; set; }
/// <summary>
/// 协议层类型
/// </summary>
public string? LayerType { get; set; }
/// <summary>
/// 设备运行时状态
/// </summary>
public int? DeviceRuntimeStatus { get; set; }
/// <summary>
/// 运行时代码数组
/// </summary>
public string[]? RuntimeCodes { get; set; }
/// <summary>
/// 运行时状态数组
/// </summary>
public int[]? RuntimeStatuses { get; set; }
/// <summary>
/// 是否按时间戳降序排序
/// </summary>
public bool OrderByDescending { get; set; } = true;
}
/// <summary>
/// 根据设备代码获取协议日志
/// </summary>
/// <param name="deviceCode">设备代码</param>
/// <param name="startTimestamp">开始时间戳</param>
/// <param name="endTimestamp">结束时间戳</param>
/// <param name="layerType">协议层类型</param>
/// <param name="deviceRuntimeStatus">设备运行时状态</param>
/// <param name="runtimeCodes">运行时代码数组</param>
/// <param name="runtimeStatuses">运行时状态数组</param>
/// <param name="orderByDescending">是否按时间戳降序排序</param>
/// <param name="request">查询请求</param>
/// <returns>协议日志列表</returns>
[HttpGet("device/{deviceCode}")]
[HttpPost("device/{deviceCode}")]
public async Task<OperationResult<GetProtocolLogsByDeviceResponse>> GetProtocolLogsByDevice(
string deviceCode,
[FromQuery] long? startTimestamp = null,
[FromQuery] long? endTimestamp = null,
[FromQuery] string? layerType = null,
[FromQuery] int? deviceRuntimeStatus = null,
[FromQuery] string[]? runtimeCodes = null,
[FromQuery] int[]? runtimeStatuses = null,
[FromQuery] bool orderByDescending = true)
[FromBody] ProtocolLogsQueryRequest request)
{
_logger.LogInformation("开始获取设备 {DeviceCode} 的协议日志,开始时间戳: {StartTimestamp}, 结束时间戳: {EndTimestamp}, 协议层类型: {LayerType}, 运行时状态: {DeviceRuntimeStatus}, 运行时代码数量: {RuntimeCodesCount}, 运行时状态数量: {RuntimeStatusesCount}",
deviceCode, startTimestamp, endTimestamp, layerType, deviceRuntimeStatus, runtimeCodes?.Length ?? 0, runtimeStatuses?.Length ?? 0);
_logger.LogInformation("开始获取设备{DeviceCode} 的协议日志,开始时间戳: {StartTimestamp}, 结束时间戳: {EndTimestamp}, 协议层类型: {LayerType}, 运行时状态: {DeviceRuntimeStatus}, 运行时代码数量: {RuntimeCodesCount}, 运行时状态数量: {RuntimeStatusesCount}",
deviceCode, request.StartTimestamp, request.EndTimestamp, request.LayerType, request.DeviceRuntimeStatus, request.RuntimeCodes?.Length ?? 0, request.RuntimeStatuses?.Length ?? 0);
var query = new GetProtocolLogsByDeviceQuery
{
DeviceCode = deviceCode,
StartTimestamp = startTimestamp,
EndTimestamp = endTimestamp,
LayerType = layerType,
DeviceRuntimeStatus = deviceRuntimeStatus.HasValue ? (DeviceRuntimeStatus)deviceRuntimeStatus.Value : null,
RuntimeCodes = runtimeCodes ?? Array.Empty<string>(),
RuntimeStatuses = runtimeStatuses ?? Array.Empty<int>(),
OrderByDescending = orderByDescending
StartTimestamp = request.StartTimestamp,
EndTimestamp = request.EndTimestamp,
LayerType = request.LayerType,
DeviceRuntimeStatus = request.DeviceRuntimeStatus.HasValue ? (DeviceRuntimeStatus)request.DeviceRuntimeStatus.Value : null,
RuntimeCodes = request.RuntimeCodes ?? Array.Empty<string>(),
RuntimeStatuses = request.RuntimeStatuses ?? Array.Empty<int>(),
OrderByDescending = request.OrderByDescending
};
var result = await mediator.Send(query);
@ -77,7 +109,7 @@ public class ProtocolLogsController : ApiController
return result;
}
_logger.LogInformation("成功获取设备 {DeviceCode} 的协议日志,共 {Count} 条记录",
_logger.LogInformation("成功获取设备 {DeviceCode} 的协议日志,共{Count} 条记录",
deviceCode, result.Data?.Items?.Count ?? 0);
return result;
}
@ -85,39 +117,25 @@ public class ProtocolLogsController : ApiController
/// <summary>
/// 获取协议日志(支持可选的设备代码)
/// </summary>
/// <param name="deviceCode">设备代码(可选)</param>
/// <param name="startTimestamp">开始时间戳</param>
/// <param name="endTimestamp">结束时间戳</param>
/// <param name="layerType">协议层类型</param>
/// <param name="deviceRuntimeStatus">设备运行时状态</param>
/// <param name="runtimeCodes">运行时代码数组</param>
/// <param name="runtimeStatuses">运行时状态数组</param>
/// <param name="orderByDescending">是否按时间戳降序排序</param>
/// <param name="request">查询请求</param>
/// <returns>协议日志列表</returns>
[HttpGet("logs")]
[HttpPost("logs")]
public async Task<OperationResult<GetProtocolLogsByDeviceResponse>> GetProtocolLogs(
[FromQuery] string? deviceCode = null,
[FromQuery] long? startTimestamp = null,
[FromQuery] long? endTimestamp = null,
[FromQuery] string? layerType = null,
[FromQuery] int? deviceRuntimeStatus = null,
[FromQuery] string[]? runtimeCodes = null,
[FromQuery] int[]? runtimeStatuses = null,
[FromQuery] bool orderByDescending = true)
[FromBody] ProtocolLogsQueryRequest request)
{
_logger.LogInformation("开始获取协议日志,设备代码: {DeviceCode}, 开始时间戳: {StartTimestamp}, 结束时间戳: {EndTimestamp}, 协议层类型: {LayerType}, 运行时状态: {DeviceRuntimeStatus}, 运行时代码数量: {RuntimeCodesCount}, 运行时状态数量: {RuntimeStatusesCount}",
deviceCode, startTimestamp, endTimestamp, layerType, deviceRuntimeStatus, runtimeCodes?.Length ?? 0, runtimeStatuses?.Length ?? 0);
request.DeviceCode, request.StartTimestamp, request.EndTimestamp, request.LayerType, request.DeviceRuntimeStatus, request.RuntimeCodes?.Length ?? 0, request.RuntimeStatuses?.Length ?? 0);
var query = new GetProtocolLogsByDeviceQuery
{
DeviceCode = deviceCode,
StartTimestamp = startTimestamp,
EndTimestamp = endTimestamp,
LayerType = layerType,
DeviceRuntimeStatus = deviceRuntimeStatus.HasValue ? (DeviceRuntimeStatus)deviceRuntimeStatus.Value : null,
RuntimeCodes = runtimeCodes ?? Array.Empty<string>(),
RuntimeStatuses = runtimeStatuses ?? Array.Empty<int>(),
OrderByDescending = orderByDescending
DeviceCode = request.DeviceCode,
StartTimestamp = request.StartTimestamp,
EndTimestamp = request.EndTimestamp,
LayerType = request.LayerType,
DeviceRuntimeStatus = request.DeviceRuntimeStatus.HasValue ? (DeviceRuntimeStatus)request.DeviceRuntimeStatus.Value : null,
RuntimeCodes = request.RuntimeCodes ?? Array.Empty<string>(),
RuntimeStatuses = request.RuntimeStatuses ?? Array.Empty<int>(),
OrderByDescending = request.OrderByDescending
};
var result = await mediator.Send(query);

579
src/modify.md

@ -1,5 +1,108 @@
# 修改记录
## 2025-01-29 - ProtocolLogsController 修复 GetProtocolLogsByDevice 和 GetProtocolLogs 改为 POST 方式
### 修改概述
根据用户需求,将 ProtocolLogsController 中的 `GetProtocolLogsByDevice``GetProtocolLogs` 方法从 GET 方式改为 POST 方式,并创建相应的请求模型来接收参数。
### 修改文件
- `X1.Presentation/Controllers/ProtocolLogsController.cs` - 修改协议日志控制器
### 修改内容
#### 1. 新增请求模型
- **ProtocolLogsQueryRequest 类**
- `DeviceCode?: string` - 设备代码(可选)
- `StartTimestamp?: long` - 开始时间戳
- `EndTimestamp?: long` - 结束时间戳
- `LayerType?: string` - 协议层类型
- `DeviceRuntimeStatus?: int` - 设备运行时状态
- `RuntimeCodes?: string[]` - 运行时代码数组
- `RuntimeStatuses?: int[]` - 运行时状态数组
- `OrderByDescending: bool` - 是否按时间戳降序排序(默认 true)
#### 2. 方法签名修改
- **GetProtocolLogsByDevice 方法**
- 路由:`[HttpPost("device/{deviceCode}")]` 替代 `[HttpGet("device/{deviceCode}")]`
- 参数:`[FromBody] ProtocolLogsQueryRequest request` 替代多个 `[FromQuery]` 参数
- 设备代码:从路由参数获取,其他参数从请求体获取
- **GetProtocolLogs 方法**
- 路由:`[HttpPost("logs")]` 替代 `[HttpGet("logs")]`
- 参数:`[FromBody] ProtocolLogsQueryRequest request` 替代多个 `[FromQuery]` 参数
- 所有参数从请求体获取
#### 3. 业务逻辑更新
- **参数处理**:从 `request` 对象中获取所有查询参数
- **默认值处理**:使用空合并运算符提供默认值
- **日志记录**:更新日志记录,使用请求对象中的参数
- **查询构建**:保持原有的查询逻辑不变,只修改参数来源
### 技术特性
#### 1. POST 方式优势
- **参数复杂性**:支持复杂的查询参数,包括数组类型
- **安全性**:敏感参数不会出现在 URL 中
- **数据大小**:支持更大的查询参数
- **一致性**:与其他复杂查询接口保持一致
#### 2. 请求模型设计
- **类型安全**:使用强类型的请求模型
- **可选参数**:大部分参数都是可选的,提供灵活性
- **默认值**:为常用参数提供合理的默认值
- **文档完整**:为所有属性添加 XML 文档注释
#### 3. 向后兼容性
- **API 路径**:保持原有的 API 路径不变
- **响应格式**:保持原有的响应格式不变
- **业务逻辑**:保持原有的查询逻辑不变
### API 使用示例
#### 1. 根据设备代码获取协议日志
```http
POST /api/protocol-logs/device/DEV001
Content-Type: application/json
{
"startTimestamp": 1640995200000,
"endTimestamp": 1641081600000,
"layerType": "NAS",
"deviceRuntimeStatus": 1,
"runtimeCodes": ["RT-001-DEV001", "RT-002-DEV001"],
"runtimeStatuses": [1, 2],
"orderByDescending": true
}
```
#### 2. 获取所有协议日志
```http
POST /api/protocol-logs/logs
Content-Type: application/json
{
"deviceCode": "DEV001",
"startTimestamp": 1640995200000,
"endTimestamp": 1641081600000,
"layerType": "RRC",
"runtimeStatuses": [1]
}
```
### 影响范围
- **API 接口**:从 GET 改为 POST 方式,需要更新客户端调用
- **参数传递**:从 URL 查询参数改为请求体 JSON 参数
- **客户端适配**:前端需要更新调用方式
- **文档更新**:需要更新 API 文档
### 注意事项
- 客户端需要将原来的 GET 请求改为 POST 请求
- 查询参数需要从 URL 移到请求体中
- 需要设置正确的 Content-Type 头
- 保持原有的业务逻辑和响应格式不变
---
## 2025-01-29 - StopDeviceRuntimeCommandHandler重构优化
### 修改概述
@ -1022,480 +1125,6 @@ const addResult = await rolePermissionService.addRolePermissions({
4. **错误处理**:完善错误处理和用户提示
5. **测试验证**:为所有服务方法添加单元测试
---
## 2025-01-29 修复设备运行时服务接口与后端控制器完全对应
### 修改原因
确保前端设备运行时服务的接口定义与后端控制器完全对应,避免数据类型不匹配和字段缺失问题。
### 修改文件
- `X1.WebUI/src/services/deviceRuntimeService.ts` - 更新接口定义以匹配后端响应实体
### 修改内容
#### 1. 设备运行时信息接口 (DeviceRuntime)
**对应后端 DeviceRuntimeDto**:
- `deviceCode: string` - 设备编号
- `runtimeStatus: number` - 运行时状态(对应后端的 int 类型)
- `networkStackCode?: string` - 网络栈配置编号
- `name?: string` - 设备名称(对应后端的 Name 字段)
- `createdAt: string` - 创建时间(对应后端的 DateTime)
#### 2. 获取设备运行时状态响应接口 (GetDeviceRuntimeStatusResponse)
**对应后端 GetDeviceRuntimeStatusResponse**:
- `deviceCode: string` - 设备编号
- `runtimeStatus: string` - 运行时状态(对应后端的 string 类型)
- `runtimeCode?: string` - 运行编码
- `networkStackCode?: string` - 网络栈配置编号
#### 3. 启动设备请求接口 (StartDeviceRequest)
**对应后端 DeviceStartRequest**:
- `deviceCode: string` - 设备编号
- `networkStackCode: string` - 网络栈配置编号(必填字段)
#### 4. 启动设备响应接口 (StartDeviceRuntimeResponse)
**对应后端 StartDeviceRuntimeResponse**:
- `id?: string` - 运行时状态ID
- `deviceCode?: string` - 设备编号
- `runtimeStatus?: string` - 运行时状态
- `networkStackCode?: string` - 网络栈配置编号
- `updatedAt: string` - 更新时间
- `deviceResults?: DeviceStartResult[]` - 设备启动结果列表
- `summary?: BatchOperationSummary` - 批量操作统计
#### 5. 设备启动结果接口 (DeviceStartResult)
**对应后端 DeviceStartResult**:
- `deviceCode: string` - 设备编号
- `id: string` - 运行时状态ID
- `runtimeStatus: string` - 运行时状态
- `networkStackCode?: string` - 网络栈配置编号
- `updatedAt: string` - 更新时间
- `isSuccess: boolean` - 是否成功
- `errorMessage?: string` - 错误信息
#### 6. 批量操作统计接口 (BatchOperationSummary)
**对应后端 BatchOperationSummary**:
- `totalCount: number` - 总设备数
- `successCount: number` - 成功数量
- `failureCount: number` - 失败数量
- `successRate: number` - 成功率(对应后端的计算属性)
#### 7. 停止设备响应接口 (StopDeviceRuntimeResponse)
**对应后端 StopDeviceRuntimeResponse**:
- `id: string` - 运行时状态ID
- `deviceCode: string` - 设备编号
- `runtimeStatus: string` - 运行时状态
- `runtimeCode?: string` - 运行编码
- `networkStackCode?: string` - 网络栈配置编号
- `updatedAt: string` - 更新时间
### 技术改进
#### 1. 状态类型处理
- **数字状态转换**:添加 `getRuntimeStatusString()` 方法,将数字状态转换为字符串状态
- **状态描述方法**:更新 `getRuntimeStatusDescription()``getRuntimeStatusColor()` 方法,支持数字和字符串状态
- **状态映射**
- 0: Initializing(初始化)
- 1: Running(运行中)
- 2: Stopped(已停止)
- 3: Error(错误)
#### 2. 查询参数处理
- **运行时状态过滤**:修复 `runtimeStatus` 参数处理,支持数字类型
- **参数验证**:使用 `!== undefined` 检查,避免 0 值被忽略
#### 3. 接口注释
- **对应关系说明**:为每个接口添加注释,说明对应的后端实体
- **字段类型说明**:标注字段类型与后端的对应关系
### 影响范围
- **类型安全**:确保前端接口定义与后端API完全匹配
- **数据一致性**:避免因字段不匹配导致的数据丢失
- **开发体验**:提供准确的类型提示和代码补全
- **错误减少**:减少运行时类型错误
### 使用示例更新
```
// 获取设备运行时状态列表(使用数字状态过滤)
const result = await deviceRuntimeService.getDeviceRuntimes({
pageNumber: 1,
pageSize: 10,
searchTerm: 'DEV001',
runtimeStatus: 1 // 对应 Running 状态
});
// 启动设备(需要提供网络栈配置编号)
const startResult = await deviceRuntimeService.startDevices([
{ deviceCode: 'DEV001', networkStackCode: 'STACK001' },
{ deviceCode: 'DEV002', networkStackCode: 'STACK002' }
]);
// 处理启动结果
if (startResult.isSuccess && startResult.data?.summary) {
console.log(`启动完成:总数 ${startResult.data.summary.totalCount},成功 ${startResult.data.summary.successCount},失败 ${startResult.data.summary.failureCount}`);
}
```
---
## 2025-01-29 实现设备运行时管理前端界面
### 修改原因
为设备运行时管理功能实现完整的前端界面,包括列表页面、详情页面、搜索功能、批量操作等,提供用户友好的操作体验。
### 新增文件
#### 1. 设备运行时管理页面
- `X1.WebUI/src/pages/device-runtimes/DeviceRuntimesView.tsx` - 设备运行时管理主页面
- `X1.WebUI/src/pages/device-runtimes/DeviceRuntimesTable.tsx` - 设备运行时表格组件
- `X1.WebUI/src/pages/device-runtimes/DeviceRuntimeDetail.tsx` - 设备运行时详情页面
### 修改文件
#### 1. 路由配置
- `X1.WebUI/src/routes/AppRouter.tsx` - 添加设备运行时管理路由
#### 2. 菜单配置
- `X1.WebUI/src/constants/menuConfig.ts` - 添加设备运行时管理菜单项和权限
#### 3. 服务接口
- `X1.WebUI/src/services/deviceRuntimeService.ts` - 修复GetDeviceRuntimeStatusResponse接口,添加updatedAt字段
### 功能特性
#### 1. 设备运行时管理主页面 (DeviceRuntimesView.tsx)
- **统计卡片**:显示总设备数、运行中、已停止、错误状态的数量
- **搜索功能**:支持按设备编号/名称搜索、按运行时状态过滤
- **批量操作**:支持批量启动设备,需要指定网络栈配置编号
- **表格显示**:显示设备运行时状态列表,支持分页、排序、列显示控制
- **实时操作**:支持单个设备停止操作
#### 2. 设备运行时表格组件 (DeviceRuntimesTable.tsx)
- **选择功能**:支持单选、全选设备
- **状态显示**:使用不同颜色的Badge显示运行时状态
- **操作菜单**:根据设备状态显示不同的操作选项
- **响应式设计**:支持紧凑、默认、舒适三种密度模式
- **加载状态**:显示加载中和空数据状态
#### 3. 设备运行时详情页面 (DeviceRuntimeDetail.tsx)
- **状态概览**:显示设备运行时状态和基本信息
- **详细信息**:分卡片显示网络配置和时间信息
- **实时刷新**:支持手动刷新设备状态
- **状态持续时间**:计算并显示当前状态的持续时间
- **导航功能**:提供返回列表的导航
### 技术特性
#### 1. 组件设计
- **模块化**:将功能拆分为独立的组件,便于维护和复用
- **类型安全**:完整的TypeScript类型定义
- **响应式**:支持不同屏幕尺寸的响应式布局
- **可访问性**:遵循ARIA标准,支持键盘导航
#### 2. 状态管理
- **本地状态**:使用React hooks管理组件状态
- **加载状态**:统一的加载和错误状态处理
- **表单状态**:搜索条件和批量操作的表单状态管理
#### 3. 用户体验
- **即时反馈**:操作后立即显示结果和状态变化
- **错误处理**:友好的错误提示和恢复机制
- **操作确认**:重要操作提供确认对话框
- **进度指示**:长时间操作显示进度状态
### 界面功能
#### 1. 搜索和过滤
```typescript
// 搜索条件
const searchConditions = {
searchTerm: string, // 设备编号或名称
runtimeStatus: number, // 运行时状态过滤
pageSize: number // 每页显示数量
};
```
#### 2. 批量操作
```typescript
// 批量启动设备
const batchStartDevices = async (deviceCodes: string[], networkStackCode: string) => {
const deviceRequests = deviceCodes.map(code => ({
deviceCode: code,
networkStackCode: networkStackCode
}));
return await startDevices(deviceRequests);
};
```
#### 3. 状态显示
```typescript
// 状态映射
const statusMapping = {
0: { text: '初始化', color: 'default' },
1: { text: '运行中', color: 'success' },
2: { text: '已停止', color: 'warning' },
3: { text: '错误', color: 'error' }
};
```
### 路由配置
#### 1. 新增路由
```typescript
// 设备运行时管理路由
<Route path="device-runtimes">
<Route index element={<Navigate to="list" replace />} />
<Route path="list" element={<DeviceRuntimesView />} />
<Route path="detail/:deviceCode" element={<DeviceRuntimeDetail />} />
</Route>
```
#### 2. 权限控制
```typescript
// 权限配置
const permissions = {
'deviceruntimes.view': '查看设备运行时',
'deviceruntimes.manage': '管理设备运行时'
};
```
### 菜单配置
#### 1. 新增菜单项
```typescript
{
title: '设备运行时',
icon: Gauge,
href: '/dashboard/device-runtimes',
permission: 'deviceruntimes.view',
children: [
{
title: '运行时状态',
href: '/dashboard/device-runtimes/list',
permission: 'deviceruntimes.view',
}
],
}
```
### 使用流程
#### 1. 查看设备运行时状态
1. 导航到"设备运行时" → "运行时状态"
2. 查看设备运行时状态列表
3. 使用搜索和过滤功能查找特定设备
4. 点击设备编号查看详细信息
#### 2. 批量启动设备
1. 在设备列表中勾选要启动的设备
2. 点击"批量启动"按钮
3. 在弹出的对话框中输入网络栈配置编号
4. 确认启动操作
#### 3. 停止单个设备
1. 在设备列表中找到运行中的设备
2. 点击操作菜单中的"停止设备"
3. 确认停止操作
#### 4. 查看设备详情
1. 点击设备编号进入详情页面
2. 查看设备的运行时状态、网络配置等信息
3. 使用刷新按钮更新状态
4. 返回列表页面
### 影响范围
- **前端界面**:提供了完整的设备运行时管理界面
- **用户体验**:改善了设备运行时管理的操作体验
- **功能完整性**:实现了设备运行时管理的所有核心功能
- **系统集成**:与现有的权限系统和导航系统完全集成
### 后续优化建议
1. **实时更新**:添加WebSocket支持,实现设备状态的实时更新
2. **历史记录**:添加设备运行时历史记录查看功能
3. **批量导入**:支持批量导入设备配置
4. **监控告警**:添加设备状态异常告警功能
5. **操作日志**:记录和查看设备操作历史
### 修改原因
根据后端 `DeviceRuntimesController` 的实现,为前端 `X1.WebUI/src/services` 目录创建对应的服务,实现设备运行时状态管理的完整功能。
### 新增文件
#### 1. 设备运行时服务
- `X1.WebUI/src/services/deviceRuntimeService.ts` - 设备运行时状态管理服务
#### 2. 设备管理服务
- `X1.WebUI/src/services/deviceService.ts` - 设备管理服务
#### 3. 权限管理服务
- `X1.WebUI/src/services/permissionService.ts` - 权限管理服务
#### 4. 角色权限管理服务
- `X1.WebUI/src/services/rolePermissionService.ts` - 角色权限管理服务
### 修改文件
#### 1. API常量配置
- `X1.WebUI/src/constants/api.ts` - 添加设备运行时、设备、权限、角色权限相关的API路径
### 功能特性
#### 1. 设备运行时服务 (deviceRuntimeService.ts)
- **设备运行时状态枚举**:`DeviceRuntimeStatus`(Running、Stopped、Error、Unknown)
- **核心功能**
- `getDeviceRuntimes()` - 获取设备运行时状态列表(支持分页、搜索、状态过滤)
- `getDeviceRuntimeStatus()` - 根据设备编号获取设备运行时状态
- `startDevices()` - 批量启动设备
- `stopDevice()` - 停止单个设备
- **辅助功能**
- `getRuntimeStatusDescription()` - 获取状态的可读描述
- `getRuntimeStatusColor()` - 获取状态对应的颜色
#### 2. 设备管理服务 (deviceService.ts)
- **核心功能**
- `getDevices()` - 获取设备列表(支持分页、搜索、类型过滤、状态过滤)
- `getDeviceById()` - 根据ID获取设备详情
- `createDevice()` - 创建设备
- `updateDevice()` - 更新设备
- `deleteDevice()` - 删除设备
- **辅助功能**
- `getDeviceStatusDescription()` - 获取设备状态描述
- `getDeviceStatusColor()` - 获取设备状态颜色
#### 3. 权限管理服务 (permissionService.ts)
- **核心功能**
- `createPermission()` - 创建权限
- `getPermissions()` - 获取权限列表
- `getPermissionById()` - 根据ID获取权限详情
- `deletePermission()` - 删除权限
#### 4. 角色权限管理服务 (rolePermissionService.ts)
- **核心功能**
- `getRolePermissions()` - 获取角色权限
- `addRolePermissions()` - 添加角色权限
- `deleteRolePermissions()` - 删除角色权限
- `batchAddPermissions()` - 批量添加权限到角色
- `batchRemovePermissions()` - 批量从角色删除权限
- **辅助功能**
- `getRolePermissionStats()` - 获取角色权限统计信息
### 技术特性
#### 1. 类型安全
- 完整的 TypeScript 接口定义
- 与后端 API 响应格式完全匹配
- 提供良好的开发体验和代码提示
#### 2. 错误处理
- 统一的错误处理机制
- 使用 `OperationResult<T>` 包装响应
- 详细的错误信息记录
#### 3. HTTP 客户端集成
- 使用 `httpClient` 进行 API 调用
- 支持 GET、POST、PUT、DELETE 方法
- 自动处理查询参数构建
#### 4. 查询参数支持
- 分页参数:`pageNumber`、`pageSize`
- 搜索参数:`searchTerm`
- 过滤参数:状态、类型等
- 排序参数:按时间、名称等排序
### API 路径配置
#### 1. 新增的 API 路径
```typescript
export const API_PATHS = {
// 设备相关
DEVICES: '/devices',
DEVICE_RUNTIMES: '/api/device-runtimes',
// 权限相关
PERMISSIONS: '/api/permissions',
ROLE_PERMISSIONS: '/api/role-permissions',
// ... 其他现有路径
}
```
### 使用示例
#### 1. 设备运行时管理
```typescript
// 获取设备运行时状态列表
const result = await deviceRuntimeService.getDeviceRuntimes({
pageNumber: 1,
pageSize: 10,
searchTerm: 'DEV001',
runtimeStatus: DeviceRuntimeStatus.Running
});
// 启动设备
const startResult = await deviceRuntimeService.startDevices([
{ deviceCode: 'DEV001', deviceName: '设备1' },
{ deviceCode: 'DEV002', deviceName: '设备2' }
]);
// 停止设备
const stopResult = await deviceRuntimeService.stopDevice('DEV001');
```
#### 2. 设备管理
```typescript
// 获取设备列表
const result = await deviceService.getDevices({
pageNumber: 1,
pageSize: 10,
searchTerm: 'LTE',
deviceType: 'Cellular',
isActive: true
});
// 创建设备
const createResult = await deviceService.createDevice({
deviceName: '新设备',
deviceCode: 'DEV003',
deviceType: 'Cellular',
description: '测试设备'
});
```
#### 3. 权限管理
```typescript
// 创建权限
const result = await permissionService.createPermission({
name: 'CreateUser',
description: '创建用户的权限'
});
```
#### 4. 角色权限管理
```typescript
// 获取角色权限
const result = await rolePermissionService.getRolePermissions('roleId', true);
// 添加角色权限
const addResult = await rolePermissionService.addRolePermissions({
roleId: 'roleId',
permissionIds: ['perm1', 'perm2']
});
```
### 影响范围
- **前端服务**:提供了完整的设备运行时、设备、权限、角色权限管理前端服务
- **API 集成**:与后端控制器完全对应
- **类型安全**:提供了完整的 TypeScript 类型定义
- **开发体验**:统一的服务接口,便于前端开发使用
### 后续工作建议
1. **前端页面开发**:基于这些服务开发对应的前端页面
2. **组件开发**:开发设备运行时状态显示组件
3. **状态管理**:集成到前端状态管理系统中
4. **错误处理**:完善错误处理和用户提示
5. **测试验证**:为所有服务方法添加单元测试
## 2025-01-29 更新instrumentService.ts和DevicesView.tsx以匹配后端API
### 修改原因
@ -2415,7 +2044,7 @@ return runtimes.OrderByDescending(r => r.CreatedAt).FirstOrDefault();
### 技术细节
- 将 `key={column.key}` 修改为 `key={`${configuration.raN_ConfigurationId}-${column.key}`}`、`key={`${configuration.ims_ConfigurationId}-${column.key}`}` 和 `key={`${config.CoreNetworkConfigId}-${column.key}`}`
- 重构操作列按钮渲染:使用数组map方法替代静态JSX,确保每个按钮都有唯一key
- 重构操作列按钮渲染:使用数组map渲染,确保每个按钮都有唯一key
- 使用动态key:将TableHeader和TableBody的key改为 `key={`header-${Date.now()}`}` 和 `key={`body-${Date.now()}`}`
- 确保每个表格单元格和子元素都有唯一的key属性
- 避免React渲染时的key冲突问题

Loading…
Cancel
Save