Browse Source
- 修复 NodeExecutionEventRouter 死循环问题 - 重构 InitialNodeInfo 支持多流程场景,优化数据库查询性能 - 统一 TaskExecutionController 路由设计 - 提升安全性:移除 ExecutorId 参数,使用用户上下文 - 性能优化:查询次数从 O(n) 降低到 O(1)refactor/permission-config
27 changed files with 1401 additions and 185 deletions
@ -0,0 +1,66 @@ |
|||
using X1.Domain.Events; |
|||
using X1.Domain.Entities.TestCase; |
|||
|
|||
namespace X1.Application.Features.TaskExecution.Events.NodeExecutionEvents; |
|||
|
|||
/// <summary>
|
|||
/// 控制器执行事件
|
|||
/// 专门用于 ControllerHandlers 处理的事件,避免与 NodeExecutionEventRouter 产生死循环
|
|||
/// </summary>
|
|||
public class ControllerExecutionEvent : INodeExecutionEvent |
|||
{ |
|||
/// <summary>
|
|||
/// 事件ID
|
|||
/// </summary>
|
|||
public string EventId { get; set; } = Guid.NewGuid().ToString(); |
|||
|
|||
/// <summary>
|
|||
/// 任务执行ID
|
|||
/// </summary>
|
|||
public string TaskExecutionId { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// 节点ID
|
|||
/// </summary>
|
|||
public string NodeId { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// 步骤映射类型
|
|||
/// </summary>
|
|||
public StepMapping StepMapping { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// 执行人/执行终端ID
|
|||
/// </summary>
|
|||
public string ExecutorId { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// 蜂窝设备运行时编码
|
|||
/// </summary>
|
|||
public string RuntimeCode { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// 测试场景编码
|
|||
/// </summary>
|
|||
public string ScenarioCode { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// 测试场景ID
|
|||
/// </summary>
|
|||
public string ScenarioId { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// 测试用例流程名称
|
|||
/// </summary>
|
|||
public string FlowName { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// 测试用例流程ID
|
|||
/// </summary>
|
|||
public string FlowId { get; set; } = null!; |
|||
|
|||
/// <summary>
|
|||
/// 事件时间戳
|
|||
/// </summary>
|
|||
public DateTime Timestamp { get; set; } = DateTime.UtcNow; |
|||
} |
|||
@ -0,0 +1,88 @@ |
|||
import { httpClient } from '@/lib/http-client'; |
|||
import { OperationResult } from '@/types/auth'; |
|||
import { API_PATHS } from '@/constants/api'; |
|||
|
|||
// ============================================================================
|
|||
// 类型定义 - 与后端 TaskExecution 实体保持一致
|
|||
// ============================================================================
|
|||
|
|||
// 任务执行状态枚举 - 与后端 TaskExecutionStatus 保持一致
|
|||
export type TaskExecutionStatus = 'Pending' | 'Running' | 'Success' | 'Failed' | 'Cancelled'; |
|||
|
|||
// 任务执行详情接口 - 与后端 TestScenarioTaskExecutionDetail 实体保持一致
|
|||
export interface TaskExecutionDetail { |
|||
id: string; |
|||
taskId: string; |
|||
scenarioCode: string; |
|||
executorId: string; |
|||
status: TaskExecutionStatus; |
|||
startTime?: string; |
|||
endTime?: string; |
|||
duration: number; // 执行时长(秒)
|
|||
loop: number; // 执行轮次
|
|||
progress: number; // 执行进度百分比 (0-100)
|
|||
runtimeCode: string; |
|||
} |
|||
|
|||
// ============================================================================
|
|||
// 请求/响应接口定义 - 与后端 Controller 完全对应
|
|||
// ============================================================================
|
|||
|
|||
// 启动任务执行请求接口 - 对应 StartTaskExecutionCommand
|
|||
export interface StartTaskExecutionRequest { |
|||
taskId: string; |
|||
} |
|||
|
|||
// 启动任务执行响应接口 - 对应 StartTaskExecutionResponse
|
|||
export interface StartTaskExecutionResponse { |
|||
taskExecution: TaskExecutionDetail; |
|||
} |
|||
|
|||
// 停止任务执行请求接口 - 对应 StopTaskExecutionCommand
|
|||
export interface StopTaskExecutionRequest { |
|||
taskExecutionId: string; |
|||
} |
|||
|
|||
// 停止任务执行响应接口 - 对应 StopTaskExecutionResponse
|
|||
export interface StopTaskExecutionResponse { |
|||
success: boolean; |
|||
message?: string; |
|||
} |
|||
|
|||
|
|||
// ============================================================================
|
|||
// 任务执行服务类 - 完全对应 TaskExecutionController 的 API 端点
|
|||
// ============================================================================
|
|||
|
|||
class TaskExecutionService { |
|||
private readonly baseUrl = API_PATHS.TASK_EXECUTION; |
|||
|
|||
// ============================================================================
|
|||
// 任务执行操作 - 完全对应 TaskExecutionController 的方法
|
|||
// ============================================================================
|
|||
|
|||
/** |
|||
* 启动任务执行 |
|||
* 对应: POST /api/taskexecution/start |
|||
* 对应控制器方法: StartTaskExecution(StartTaskExecutionCommand request) |
|||
*/ |
|||
async startTaskExecution(request: StartTaskExecutionRequest): Promise<OperationResult<StartTaskExecutionResponse>> { |
|||
return httpClient.post<StartTaskExecutionResponse>(`${this.baseUrl}/start`, request); |
|||
} |
|||
|
|||
/** |
|||
* 停止任务执行 |
|||
* 对应: POST /api/taskexecution/stop |
|||
* 对应控制器方法: StopTaskExecution(StopTaskExecutionCommand request) |
|||
*/ |
|||
async stopTaskExecution(request: StopTaskExecutionRequest): Promise<OperationResult<StopTaskExecutionResponse>> { |
|||
return httpClient.post<StopTaskExecutionResponse>(`${this.baseUrl}/stop`, request); |
|||
} |
|||
|
|||
} |
|||
|
|||
// ============================================================================
|
|||
// 导出服务实例
|
|||
// ============================================================================
|
|||
|
|||
export const taskExecutionService = new TaskExecutionService(); |
|||
@ -0,0 +1,316 @@ |
|||
# 2025-01-21 重构 InitialNodeInfo 和 GetInitialNodeAsync 方法 |
|||
|
|||
## 概述 |
|||
|
|||
根据用户需求,重构了 `InitialNodeInfo` 类的结构和 `GetInitialNodeAsync` 方法的逻辑,使其能够处理一个场景中包含多个测试用例流程的情况。 |
|||
|
|||
## 主要变更 |
|||
|
|||
### 1. 重构 InitialNodeInfo 类结构 |
|||
|
|||
**文件**: `X1.Domain/Services/ITaskExecutionService.cs` |
|||
|
|||
**变更前**: |
|||
```csharp |
|||
public class InitialNodeInfo |
|||
{ |
|||
public string NodeId { get; set; } = null!; |
|||
public StepMapping StepMapping { get; set; } |
|||
public string ScenarioId { get; set; } = null!; |
|||
public string ScenarioCode { get; set; } = null!; |
|||
public string FlowId { get; set; } = null!; |
|||
public string FlowName { get; set; } = null!; |
|||
} |
|||
``` |
|||
|
|||
**变更后**: |
|||
```csharp |
|||
public class InitialNodeInfo |
|||
{ |
|||
public string TaskId { get; set; } = null!; |
|||
public string ScenarioId { get; set; } = null!; |
|||
public string ScenarioCode { get; set; } = null!; |
|||
public List<InitialNodeItem> InitialNodes { get; set; } = new(); |
|||
} |
|||
|
|||
public class InitialNodeItem |
|||
{ |
|||
public string NodeId { get; set; } = null!; |
|||
public StepMapping StepMapping { get; set; } |
|||
public string FlowId { get; set; } = null!; |
|||
public string FlowName { get; set; } = null!; |
|||
} |
|||
``` |
|||
|
|||
### 2. 重构 GetInitialNodeAsync 方法 |
|||
|
|||
**文件**: `X1.Application/ApplicationServices/TaskExecutionService.cs` |
|||
|
|||
**主要变更**: |
|||
- 使用 `GetEnabledByScenarioIdAsync` 方法在数据库层面进行过滤和排序 |
|||
- 遍历所有启用的测试用例流程,获取每个流程的第一个节点 |
|||
- 创建包含多个初始节点的 `InitialNodeInfo` 对象 |
|||
- 改进了错误处理和日志记录 |
|||
|
|||
**核心逻辑**: |
|||
```csharp |
|||
// 3. 根据场景ID获取启用的场景测试用例(按执行顺序排序) |
|||
var scenarioTestCases = await _scenarioTestCaseRepository.GetEnabledByScenarioIdAsync(scenario.Id, cancellationToken); |
|||
|
|||
// 4. 创建初始节点信息 |
|||
var initialNodeInfo = new InitialNodeInfo |
|||
{ |
|||
TaskId = taskId, |
|||
ScenarioId = scenario.Id, |
|||
ScenarioCode = task.ScenarioCode, |
|||
InitialNodes = new List<InitialNodeItem>() |
|||
}; |
|||
|
|||
// 5. 遍历所有启用的测试用例,获取每个流程的第一个节点 |
|||
foreach (var scenarioTestCase in scenarioTestCases) |
|||
{ |
|||
// 获取测试用例流程和第一个节点 |
|||
// 添加到初始节点集合 |
|||
} |
|||
``` |
|||
|
|||
### 3. 更新 StartTaskExecutionCommandHandler |
|||
|
|||
**文件**: `X1.Application/Features/TaskExecution/Commands/StartTaskExecution/StartTaskExecutionCommandHandler.cs` |
|||
|
|||
**变更**: |
|||
- 适配新的 `InitialNodeInfo` 结构 |
|||
- 为每个初始节点发布 `NodeExecutionStartedEvent` 事件 |
|||
- 改进了日志记录,显示节点数量和流程信息 |
|||
|
|||
**核心逻辑**: |
|||
```csharp |
|||
var initialNodeInfo = await _taskExecutionService.GetInitialNodeAsync(request.TaskId, cancellationToken); |
|||
if (initialNodeInfo != null && initialNodeInfo.InitialNodes.Any()) |
|||
{ |
|||
// 为每个初始节点发布事件 |
|||
foreach (var initialNode in initialNodeInfo.InitialNodes) |
|||
{ |
|||
var nodeExecutionStartedEvent = new NodeExecutionStartedEvent |
|||
{ |
|||
// ... 设置事件属性 |
|||
}; |
|||
_ = _mediator.Publish(nodeExecutionStartedEvent, cancellationToken); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
## 技术改进 |
|||
|
|||
### 1. 数据库查询优化 |
|||
- 使用 `GetEnabledByScenarioIdAsync` 替代内存中的 `Where` 和 `OrderBy` 操作 |
|||
- 在数据库层面进行过滤和排序,提高性能 |
|||
|
|||
### 2. 错误处理改进 |
|||
- 使用 `continue` 语句跳过无效的测试用例流程,而不是直接返回 null |
|||
- 只有在没有任何有效节点时才返回 null |
|||
- 改进了日志记录的详细程度 |
|||
|
|||
### 3. 代码结构优化 |
|||
- 将节点信息分离为独立的 `InitialNodeItem` 类 |
|||
- 使 `InitialNodeInfo` 更符合实际业务需求(一个任务对应多个初始节点) |
|||
- 提高了代码的可读性和可维护性 |
|||
|
|||
## 业务逻辑说明 |
|||
|
|||
### 场景与测试用例流程的关系 |
|||
- 一个场景(TestScenario)可以包含多个测试用例流程(TestCaseFlow) |
|||
- 每个场景测试用例(ScenarioTestCase)记录表示场景中的一个测试用例流程 |
|||
- 包含执行顺序、循环次数、是否启用等配置信息 |
|||
|
|||
### 初始节点获取逻辑 |
|||
1. 根据任务ID获取任务信息 |
|||
2. 根据场景编码获取场景信息 |
|||
3. 获取场景中所有启用的测试用例(按执行顺序排序) |
|||
4. 遍历每个测试用例流程,获取其第一个节点 |
|||
5. 将所有初始节点信息组织成 `InitialNodeInfo` 对象返回 |
|||
|
|||
## 影响范围 |
|||
|
|||
- **向后兼容性**: 需要更新所有使用 `InitialNodeInfo` 的代码 |
|||
- **事件发布**: 现在会为每个初始节点发布独立的事件 |
|||
- **日志记录**: 改进了日志信息的详细程度 |
|||
|
|||
## 性能优化(2025-01-21 更新) |
|||
|
|||
### 问题 |
|||
原始的 `GetInitialNodeAsync` 方法存在 N+1 查询问题: |
|||
- 在循环中逐个查询测试用例流程 |
|||
- 在循环中逐个查询每个流程的第一个节点 |
|||
- 导致数据库查询次数过多,性能低下 |
|||
|
|||
### 解决方案 |
|||
|
|||
#### 1. 添加批量查询方法 |
|||
|
|||
**ITestCaseFlowRepository**: |
|||
```csharp |
|||
Task<IEnumerable<TestCaseFlow>> GetByIdsAsync(IEnumerable<string> ids, CancellationToken cancellationToken = default); |
|||
``` |
|||
|
|||
**ITestCaseNodeRepository**: |
|||
```csharp |
|||
Task<IEnumerable<TestCaseNode>> GetStartNodesByTestCaseIdsAsync(IEnumerable<string> testCaseIds, CancellationToken cancellationToken = default); |
|||
``` |
|||
|
|||
#### 2. 优化查询逻辑 |
|||
|
|||
**变更前**: |
|||
```csharp |
|||
foreach (var scenarioTestCase in scenarioTestCases) |
|||
{ |
|||
// 每次循环都查询数据库 |
|||
var testCaseFlow = await _testCaseFlowRepository.GetTestCaseFlowByIdAsync(...); |
|||
var nodes = await _testCaseNodeRepository.GetByTestCaseIdOrderedAsync(...); |
|||
var firstNode = nodes.FirstOrDefault(); |
|||
} |
|||
``` |
|||
|
|||
**变更后**: |
|||
```csharp |
|||
// 批量获取所有数据 |
|||
var testCaseFlowIds = scenarioTestCases.Select(stc => stc.TestCaseFlowId).ToList(); |
|||
var testCaseFlows = await _testCaseFlowRepository.GetByIdsAsync(testCaseFlowIds, cancellationToken); |
|||
var firstNodes = await _testCaseNodeRepository.GetFirstNodesByTestCaseIdsAsync(testCaseFlowIds, cancellationToken); |
|||
|
|||
// 转换为字典提高查找效率 |
|||
var testCaseFlowDict = testCaseFlows.ToDictionary(tcf => tcf.Id, tcf => tcf); |
|||
var firstNodeDict = firstNodes.ToDictionary(fn => fn.TestCaseId, fn => fn); |
|||
|
|||
// 在内存中构建结果 |
|||
foreach (var scenarioTestCase in scenarioTestCases) |
|||
{ |
|||
if (testCaseFlowDict.TryGetValue(scenarioTestCase.TestCaseFlowId, out var testCaseFlow) && |
|||
firstNodeDict.TryGetValue(scenarioTestCase.TestCaseFlowId, out var firstNode)) |
|||
{ |
|||
// 构建初始节点信息 |
|||
} |
|||
} |
|||
``` |
|||
|
|||
#### 3. 使用高效SQL查询 |
|||
|
|||
对于获取每个测试用例的起始节点,使用了关联查询: |
|||
```sql |
|||
SELECT n.* |
|||
FROM tb_testcasenode n |
|||
INNER JOIN tb_casestepconfig c ON n.stepid = c.id |
|||
WHERE n.testcaseid IN (...) |
|||
AND c.steptype = 1 -- CaseStepType.Start = 1 |
|||
ORDER BY n.testcaseid |
|||
``` |
|||
|
|||
**重要修正**: 根据业务逻辑,应该查找 `CaseStepConfig.StepType = CaseStepType.Start` 的节点,而不是简单地取序号最小的节点。 |
|||
|
|||
### 业务逻辑修正(2025-01-21 更新) |
|||
|
|||
#### 问题发现 |
|||
用户指出原始实现有误:应该根据 `TestCaseNode` 中的 `CaseStepConfig` 的 `StepType` 来找到 `CaseStepType.Start` 类型的节点,而不是简单地取序号最小的节点。 |
|||
|
|||
#### 修正内容 |
|||
|
|||
1. **方法名称更新**: |
|||
- `GetFirstNodesByTestCaseIdsAsync` → `GetStartNodesByTestCaseIdsAsync` |
|||
- 更准确地反映业务逻辑 |
|||
|
|||
2. **SQL查询修正**: |
|||
```sql |
|||
-- 修正前:取序号最小的节点 |
|||
WITH FirstNodes AS ( |
|||
SELECT *, ROW_NUMBER() OVER (PARTITION BY TestCaseId ORDER BY SequenceNumber ASC) as rn |
|||
FROM tb_testcasenodes WHERE TestCaseId IN (...) |
|||
) |
|||
SELECT * FROM FirstNodes WHERE rn = 1 |
|||
|
|||
-- 修正后:根据StepType查找起始节点 |
|||
SELECT n.* |
|||
FROM tb_testcasenode n |
|||
INNER JOIN tb_casestepconfig c ON n.stepid = c.id |
|||
WHERE n.testcaseid IN (...) |
|||
AND c.steptype = 1 -- CaseStepType.Start = 1 |
|||
ORDER BY n.testcaseid |
|||
``` |
|||
|
|||
3. **业务逻辑正确性**: |
|||
- 确保获取的是真正的起始节点(`CaseStepType.Start`) |
|||
- 而不是流程中序号最小的节点 |
|||
- 符合测试用例流程的设计意图 |
|||
|
|||
4. **PostgreSQL 语法修正**: |
|||
```sql |
|||
-- 修正前:SQL Server 语法 |
|||
SELECT n.* |
|||
FROM tb_testcasenode n |
|||
INNER JOIN tb_casestepconfig c ON n.stepid = c.id |
|||
WHERE n.testcaseid IN (@p0, @p1, @p2) |
|||
AND c.steptype = 1 |
|||
|
|||
-- 修正后:PostgreSQL 语法 |
|||
SELECT n.* |
|||
FROM "tb_testcasenode" n |
|||
INNER JOIN "tb_casestepconfig" c ON n."stepid" = c."id" |
|||
WHERE n."testcaseid" = ANY({0}) |
|||
AND c."steptype" = 1 |
|||
``` |
|||
|
|||
**关键修正**: |
|||
- 表名和字段名使用双引号包围 |
|||
- 使用 `ANY({0})` 替代 `IN (@p0, @p1, ...)` |
|||
- 参数传递方式改为 `new object[] { testCaseIdList.ToArray() }` |
|||
|
|||
5. **优化数据获取方式**: |
|||
```csharp |
|||
// 修正前:使用原生SQL查询,无法获取导航属性 |
|||
var sql = "SELECT n.* FROM tb_testcasenode n INNER JOIN tb_casestepconfig c ON n.stepid = c.id WHERE ..."; |
|||
var nodes = await QueryRepository.ExecuteSqlQueryAsync(sql, parameters, cancellationToken); |
|||
|
|||
// 修正后:使用 EF Core 查询,包含 StepConfig 导航属性 |
|||
var nodes = await QueryRepository.FindAsync( |
|||
x => testCaseIdList.Contains(x.TestCaseId) && |
|||
x.StepConfig != null && |
|||
x.StepConfig.StepType == CaseStepType.Start, |
|||
query => query.Include(x => x.StepConfig), |
|||
cancellationToken); |
|||
``` |
|||
|
|||
**优势**: |
|||
- 直接获取 `StepConfig.Mapping` 信息,无需额外查询 |
|||
- 删除了 `GetStepMappingFromStepId` 方法 |
|||
- 简化了 `TaskExecutionService` 中的逻辑 |
|||
- 使用 `startNode.StepConfig?.Mapping ?? StepMapping.None` 直接获取映射类型 |
|||
|
|||
### 性能提升 |
|||
|
|||
- **查询次数**: 从 O(n) 降低到 O(1)(固定3次查询) |
|||
- **数据库负载**: 大幅减少数据库连接和查询开销 |
|||
- **内存效率**: 使用字典提高数据查找效率 |
|||
- **可扩展性**: 支持大量测试用例流程的场景 |
|||
|
|||
### 查询次数对比 |
|||
|
|||
**优化前**: |
|||
- 1次查询场景测试用例 |
|||
- N次查询测试用例流程(N = 测试用例数量) |
|||
- N次查询每个流程的节点 |
|||
- **总计**: 1 + 2N 次查询 |
|||
|
|||
**优化后**: |
|||
- 1次查询场景测试用例 |
|||
- 1次批量查询测试用例流程 |
|||
- 1次批量查询第一个节点 |
|||
- **总计**: 3次查询 |
|||
|
|||
## 测试建议 |
|||
|
|||
1. 测试包含单个测试用例流程的场景 |
|||
2. 测试包含多个测试用例流程的场景 |
|||
3. 测试没有启用测试用例的场景 |
|||
4. 测试测试用例流程中没有节点的情况 |
|||
5. 验证事件发布的正确性 |
|||
6. **性能测试**: 测试包含大量测试用例流程的场景,验证查询性能 |
|||
7. **并发测试**: 测试高并发场景下的性能表现 |
|||
@ -0,0 +1,90 @@ |
|||
# 2025-01-21 修复 NodeExecutionEventRouter 死循环问题 |
|||
|
|||
## 问题描述 |
|||
|
|||
在 `NodeExecutionEventRouter` 中存在死循环问题: |
|||
- `NodeExecutionEventRouter` 实现了 `INotificationHandler<NodeExecutionStartedEvent>` |
|||
- 当它重新发布 `NodeExecutionStartedEvent` 时,MediatR 会调用所有实现了该接口的处理器 |
|||
- 这导致 `NodeExecutionEventRouter` 再次被调用,形成死循环 |
|||
- 日志显示:`找到对应的处理器,步骤映射: StartFlow, 处理器类型: StartFlowControllerHandler` 但实际没有路由到 `StartFlowControllerHandler` |
|||
|
|||
## 解决方案 |
|||
|
|||
### 1. 创建专门的 ControllerExecutionEvent 事件 |
|||
- **文件**: `X1.Application/Features/TaskExecution/Events/NodeExecutionEvents/ControllerExecutionEvent.cs` |
|||
- **功能**: 专门用于 ControllerHandlers 处理的事件,避免与 NodeExecutionEventRouter 产生死循环 |
|||
- **特点**: |
|||
- 实现 `INodeExecutionEvent` 接口 |
|||
- 包含完整的执行上下文信息 |
|||
- 与 `NodeExecutionStartedEvent` 结构相同,但类型不同 |
|||
|
|||
### 2. 更新 NodeExecutionEventRouter 逻辑 |
|||
- **文件**: `X1.Application/Features/TaskExecution/Events/EventHandlers/NodeExecutionEventRouter.cs` |
|||
- **修改内容**: |
|||
- 不再重新发布 `NodeExecutionStartedEvent` |
|||
- 创建并发布 `ControllerExecutionEvent` |
|||
- 避免死循环问题 |
|||
|
|||
### 3. 更新所有 ControllerHandlers |
|||
更新以下处理器,让它们处理 `ControllerExecutionEvent` 而不是 `NodeExecutionStartedEvent`: |
|||
|
|||
#### 3.1 StartFlowControllerHandler |
|||
- **文件**: `X1.Application/Features/TaskExecution/Events/ControllerHandlers/StartFlowControllerHandler.cs` |
|||
- **修改**: 继承 `INodeExecutionHandlerBase<ControllerExecutionEvent>` |
|||
|
|||
#### 3.2 EndFlowControllerHandler |
|||
- **文件**: `X1.Application/Features/TaskExecution/Events/ControllerHandlers/EndFlowControllerHandler.cs` |
|||
- **修改**: 继承 `INodeExecutionHandlerBase<ControllerExecutionEvent>` |
|||
|
|||
#### 3.3 EnableFlightModeControllerHandler |
|||
- **文件**: `X1.Application/Features/TaskExecution/Events/ControllerHandlers/EnableFlightModeControllerHandler.cs` |
|||
- **修改**: 继承 `INodeExecutionHandlerBase<ControllerExecutionEvent>` |
|||
|
|||
#### 3.4 DisableFlightModeControllerHandler |
|||
- **文件**: `X1.Application/Features/TaskExecution/Events/ControllerHandlers/DisableFlightModeControllerHandler.cs` |
|||
- **修改**: 继承 `INodeExecutionHandlerBase<ControllerExecutionEvent>` |
|||
|
|||
#### 3.5 ImsiRegistrationControllerHandler |
|||
- **文件**: `X1.Application/Features/TaskExecution/Events/ControllerHandlers/ImsiRegistrationControllerHandler.cs` |
|||
- **修改**: 继承 `INodeExecutionHandlerBase<ControllerExecutionEvent>` |
|||
|
|||
## 事件流程 |
|||
|
|||
修复后的事件流程: |
|||
|
|||
1. **任务启动**: `StartTaskExecutionCommandHandler` 发布 `NodeExecutionStartedEvent` |
|||
2. **事件路由**: `NodeExecutionEventRouter` 接收 `NodeExecutionStartedEvent`,创建并发布 `ControllerExecutionEvent` |
|||
3. **控制器处理**: 对应的 `ControllerHandler` 接收并处理 `ControllerExecutionEvent` |
|||
4. **完成处理**: `ControllerHandler` 发布 `NodeExecutionCompletedEvent` |
|||
5. **流程控制**: `NodeExecutionCompletedEventHandler` 接收完成事件,发布下一个节点的 `NodeExecutionStartedEvent` |
|||
|
|||
## 技术特点 |
|||
|
|||
### 1. 避免死循环 |
|||
- `NodeExecutionEventRouter` 只处理 `NodeExecutionStartedEvent` |
|||
- `ControllerHandlers` 只处理 `ControllerExecutionEvent` |
|||
- 两者不会相互调用,避免死循环 |
|||
|
|||
### 2. 职责分离 |
|||
- `NodeExecutionEventRouter`: 负责事件路由和分发 |
|||
- `ControllerHandlers`: 负责具体的业务逻辑执行 |
|||
- `NodeExecutionCompletedEventHandler`: 负责流程控制和下一个节点的启动 |
|||
|
|||
### 3. 类型安全 |
|||
- 基于不同的事件类型进行路由 |
|||
- 编译时类型检查 |
|||
- 避免运行时类型错误 |
|||
|
|||
## 验证 |
|||
|
|||
修复后应该能够看到: |
|||
1. `NodeExecutionEventRouter` 正确路由到对应的 `ControllerHandler` |
|||
2. `StartFlowControllerHandler` 能够正常接收和处理事件 |
|||
3. 不再出现死循环问题 |
|||
4. 日志显示正确的处理器调用路径 |
|||
|
|||
## 注意事项 |
|||
|
|||
- `NodeExecutionCompletedEventHandler` 不需要修改,它应该继续发布 `NodeExecutionStartedEvent` |
|||
- 所有 `ControllerHandlers` 的方法签名都已更新为使用 `ControllerExecutionEvent` |
|||
- 保持了原有的业务逻辑不变,只是改变了事件类型 |
|||
@ -0,0 +1,152 @@ |
|||
## 2025-01-21 优化 StartTaskExecutionCommand 移除 ExecutorId 参数 |
|||
|
|||
### 概述 |
|||
|
|||
优化 StartTaskExecutionCommand,移除 ExecutorId 参数,改为在 Handler 中通过 ICurrentUserService 获取当前用户ID,提高安全性和代码简洁性。 |
|||
|
|||
### 主要变更 |
|||
|
|||
#### 1. 优化 StartTaskExecutionCommand |
|||
- **文件**: `X1.Application/Features/TaskExecution/Commands/StartTaskExecution/StartTaskExecutionCommand.cs` |
|||
- **变更**: 移除 `ExecutorId` 属性 |
|||
- **原因**: 执行人ID应该从当前用户上下文获取,而不是通过参数传递 |
|||
|
|||
#### 2. 更新 StartTaskExecutionCommandHandler |
|||
- **文件**: `X1.Application/Features/TaskExecution/Commands/StartTaskExecution/StartTaskExecutionCommandHandler.cs` |
|||
- **新增依赖**: 注入 `ICurrentUserService` |
|||
- **新增逻辑**: 在 Handle 方法中获取当前用户ID |
|||
- **安全检查**: 验证用户ID是否为空,为空时返回友好的错误信息 |
|||
|
|||
#### 3. 更新前端 TaskExecutionService |
|||
- **文件**: `X1.WebUI/src/services/taskExecutionService.ts` |
|||
- **变更**: 移除 `StartTaskExecutionRequest` 接口中的 `executorId` 字段 |
|||
- **简化**: 请求参数只需要 `taskId` |
|||
|
|||
#### 4. 更新前端 TaskExecutionView |
|||
- **文件**: `X1.WebUI/src/pages/taskExecution/TaskExecutionView.tsx` |
|||
- **变更**: 移除 `handleStart` 方法中的 `executorId` 参数设置 |
|||
- **简化**: 请求构建更加简洁 |
|||
|
|||
### 技术实现 |
|||
|
|||
#### 1. 后端优化 |
|||
```csharp |
|||
// 优化前 |
|||
public class StartTaskExecutionCommand : IRequest<OperationResult<StartTaskExecutionResponse>> |
|||
{ |
|||
public string TaskId { get; set; } = null!; |
|||
public string ExecutorId { get; set; } = null!; // 需要移除 |
|||
} |
|||
|
|||
// 优化后 |
|||
public class StartTaskExecutionCommand : IRequest<OperationResult<StartTaskExecutionResponse>> |
|||
{ |
|||
public string TaskId { get; set; } = null!; |
|||
} |
|||
``` |
|||
|
|||
#### 2. Handler 中的用户ID获取 |
|||
```csharp |
|||
public async Task<OperationResult<StartTaskExecutionResponse>> Handle(StartTaskExecutionCommand request, CancellationToken cancellationToken) |
|||
{ |
|||
try |
|||
{ |
|||
// 获取当前用户ID |
|||
var currentUserId = _currentUserService.GetCurrentUserId(); |
|||
if (string.IsNullOrEmpty(currentUserId)) |
|||
{ |
|||
_logger.LogWarning("无法获取当前用户ID,任务ID: {TaskId}", request.TaskId); |
|||
return OperationResult<StartTaskExecutionResponse>.CreateFailure("无法获取当前用户信息,请重新登录"); |
|||
} |
|||
|
|||
// 使用获取到的用户ID |
|||
var taskExecution = await _taskExecutionService.StartTaskExecutionAsync( |
|||
request.TaskId, |
|||
currentUserId, |
|||
cancellationToken); |
|||
|
|||
// ... 其他逻辑 |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
// ... 错误处理 |
|||
} |
|||
} |
|||
``` |
|||
|
|||
#### 3. 前端接口简化 |
|||
```typescript |
|||
// 优化前 |
|||
export interface StartTaskExecutionRequest { |
|||
taskId: string; |
|||
executorId: string; // 需要移除 |
|||
} |
|||
|
|||
// 优化后 |
|||
export interface StartTaskExecutionRequest { |
|||
taskId: string; |
|||
} |
|||
``` |
|||
|
|||
#### 4. 前端调用简化 |
|||
```typescript |
|||
// 优化前 |
|||
const request: StartTaskExecutionRequest = { |
|||
taskId: task.taskId, |
|||
executorId: 'current-user-id' // TODO: 从用户上下文获取当前用户ID |
|||
}; |
|||
|
|||
// 优化后 |
|||
const request: StartTaskExecutionRequest = { |
|||
taskId: task.taskId |
|||
}; |
|||
``` |
|||
|
|||
### 优化优势 |
|||
|
|||
#### 1. 安全性提升 |
|||
- **防止伪造**: 用户无法通过前端参数伪造执行人ID |
|||
- **权限控制**: 确保只有当前登录用户才能执行任务 |
|||
- **审计追踪**: 执行人ID始终来自可信的用户上下文 |
|||
|
|||
#### 2. 代码简洁性 |
|||
- **参数减少**: 前端调用更加简洁,只需要传递任务ID |
|||
- **逻辑集中**: 用户ID获取逻辑集中在后端Handler中 |
|||
- **维护性**: 减少了前后端之间的参数传递复杂度 |
|||
|
|||
#### 3. 一致性 |
|||
- **架构统一**: 与其他需要用户上下文的操作保持一致 |
|||
- **错误处理**: 统一的用户认证失败处理逻辑 |
|||
- **日志记录**: 更准确的日志记录,不依赖前端传递的参数 |
|||
|
|||
### 依赖注入更新 |
|||
|
|||
需要在依赖注入容器中确保 `ICurrentUserService` 已正确注册: |
|||
|
|||
```csharp |
|||
// 在 DependencyInjection.cs 中 |
|||
services.AddScoped<ICurrentUserService, CurrentUserService>(); |
|||
``` |
|||
|
|||
### 测试建议 |
|||
|
|||
#### 1. 单元测试 |
|||
- 测试用户ID为空时的错误处理 |
|||
- 测试正常用户ID获取和任务执行流程 |
|||
- 测试异常情况下的错误处理 |
|||
|
|||
#### 2. 集成测试 |
|||
- 测试完整的任务启动流程 |
|||
- 测试用户认证失败的情况 |
|||
- 测试前后端接口的兼容性 |
|||
|
|||
### 总结 |
|||
|
|||
本次优化通过移除 ExecutorId 参数,改为在 Handler 中获取当前用户ID,实现了: |
|||
|
|||
1. ✅ **安全性提升**: 防止用户伪造执行人ID |
|||
2. ✅ **代码简化**: 减少前后端参数传递 |
|||
3. ✅ **架构一致**: 与其他需要用户上下文的功能保持一致 |
|||
4. ✅ **维护性**: 集中用户ID获取逻辑,便于维护 |
|||
|
|||
该优化完全符合安全最佳实践,提高了系统的安全性和可维护性。 |
|||
@ -0,0 +1,88 @@ |
|||
## 2025-01-21 完善 TaskExecutionTable.tsx 实现和创建 TaskExecutionService |
|||
|
|||
### 概述 |
|||
|
|||
完善 TaskExecutionTable.tsx 组件的实现,创建 TaskExecutionService 服务类,实现与后端任务执行 API 的完整集成。 |
|||
|
|||
### 主要变更 |
|||
|
|||
#### 1. 创建 TaskExecutionService 服务类 |
|||
- **文件**: `X1.WebUI/src/services/taskExecutionService.ts` |
|||
- **功能**: 提供任务执行相关的 API 调用服务 |
|||
- **接口**: 与后端 TaskExecutionController 完全对应 |
|||
|
|||
#### 2. 实现的功能接口 |
|||
- **启动任务执行**: `POST /api/taskexecution/start` |
|||
- **停止任务执行**: `POST /api/taskexecution/stop` |
|||
- **获取任务执行状态**: `GET /api/taskexecution/{executionId}/status` |
|||
- **获取任务执行进度**: `GET /api/taskexecution/{executionId}/progress` |
|||
|
|||
#### 3. 接口定义 |
|||
- **StartTaskExecutionRequest/Response**: 启动任务执行请求和响应 |
|||
- **StopTaskExecutionRequest/Response**: 停止任务执行请求和响应 |
|||
- **GetTaskExecutionStatusRequest/Response**: 获取执行状态请求和响应 |
|||
- **GetTaskExecutionProgressRequest/Response**: 获取执行进度请求和响应 |
|||
|
|||
#### 4. 更新 TaskExecutionView.tsx |
|||
- **集成服务**: 导入并使用 TaskExecutionService |
|||
- **完善逻辑**: 实现真正的 API 调用逻辑 |
|||
- **错误处理**: 完善的错误处理和用户提示 |
|||
- **状态刷新**: 操作成功后自动刷新任务列表 |
|||
|
|||
#### 5. 技术特点 |
|||
- **类型安全**: 完整的 TypeScript 类型定义 |
|||
- **错误处理**: 统一的错误处理和用户友好的错误消息 |
|||
- **响应格式**: 使用统一的响应格式 `{ isSuccess, data, errorMessages }` |
|||
- **日志记录**: 完整的操作日志记录 |
|||
|
|||
### 实现细节 |
|||
|
|||
#### 1. TaskExecutionService 服务类结构 |
|||
```typescript |
|||
class TaskExecutionService { |
|||
private readonly baseUrl = '/api/taskexecution'; |
|||
|
|||
async startTaskExecution(request: StartTaskExecutionRequest): Promise<...> |
|||
async stopTaskExecution(request: StopTaskExecutionRequest): Promise<...> |
|||
async getTaskExecutionStatus(request: GetTaskExecutionStatusRequest): Promise<...> |
|||
async getTaskExecutionProgress(request: GetTaskExecutionProgressRequest): Promise<...> |
|||
} |
|||
``` |
|||
|
|||
#### 2. 与后端 API 的对应关系 |
|||
- 前端服务方法名与后端 Controller 方法名保持一致 |
|||
- 请求/响应接口与后端 Command/Response 完全对应 |
|||
- 错误处理与后端 OperationResult 格式保持一致 |
|||
|
|||
#### 3. 用户体验优化 |
|||
- 操作成功后显示详细的成功信息(包括执行ID) |
|||
- 操作失败时显示具体的错误原因 |
|||
- 自动刷新任务列表以反映最新状态 |
|||
- 保持原有的加载状态和交互反馈 |
|||
|
|||
### 待完善项目 |
|||
|
|||
#### 1. 用户上下文集成 |
|||
- 需要从用户上下文获取当前用户ID,替换硬编码的 `'current-user-id'` |
|||
- 实现用户权限验证和任务执行权限检查 |
|||
|
|||
#### 2. 任务执行状态管理 |
|||
- 需要从任务状态中获取当前执行的任务执行ID |
|||
- 实现任务执行状态的实时更新和显示 |
|||
- 添加任务执行进度的实时监控 |
|||
|
|||
#### 3. 功能扩展 |
|||
- 实现任务执行状态的实时查询 |
|||
- 添加任务执行进度的可视化显示 |
|||
- 支持任务执行日志的查看和下载 |
|||
|
|||
### 总结 |
|||
|
|||
TaskExecutionTable.tsx 组件现在已经基本完成,包括: |
|||
1. ✅ 完整的表格组件实现 |
|||
2. ✅ 完整的页面布局和交互 |
|||
3. ✅ 完整的服务类实现 |
|||
4. ✅ 与后端 API 的集成 |
|||
5. ✅ 错误处理和用户反馈 |
|||
|
|||
该实现为后续的任务执行功能扩展提供了良好的基础,完全符合 Clean Architecture 的设计原则。 |
|||
@ -0,0 +1,150 @@ |
|||
## 2025-01-21 修复 TaskExecutionController 路由设计和简化服务 |
|||
|
|||
### 概述 |
|||
|
|||
修复 TaskExecutionController 的路由设计,使其符合项目架构标准,并简化 taskExecutionService.ts 只保留与 Controller 对应的方法。 |
|||
|
|||
### 问题分析 |
|||
|
|||
#### 1. 路由设计问题 |
|||
- **错误路由**: 使用了 `[Route("api/[controller]/[action]")]` |
|||
- **正确路由**: 应该使用 `[Route("api/taskexecution")]` 或 `[Route("api/[controller]")]` |
|||
- **参考标准**: 其他控制器如 TestScenarioTasksController、TestScenariosController 都使用具体路径 |
|||
|
|||
#### 2. 服务冗余问题 |
|||
- **冗余方法**: taskExecutionService.ts 包含了 Controller 中不存在的方法 |
|||
- **不一致性**: 前端服务与后端 Controller 不匹配 |
|||
- **维护困难**: 多余的代码增加了维护成本 |
|||
|
|||
### 主要变更 |
|||
|
|||
#### 1. 修复 TaskExecutionController 路由 |
|||
- **文件**: `X1.Presentation/Controllers/TaskExecutionController.cs` |
|||
- **路由修复**: |
|||
```csharp |
|||
// 修复前 |
|||
[Route("api/[controller]/[action]")] |
|||
|
|||
// 修复后 |
|||
[Route("api/taskexecution")] |
|||
``` |
|||
|
|||
#### 2. 更新方法路由 |
|||
- **启动任务**: `[HttpPost("start")]` → `/api/taskexecution/start` |
|||
- **停止任务**: `[HttpPost("stop")]` → `/api/taskexecution/stop` |
|||
|
|||
#### 3. 移除冗余的 Query 方法 |
|||
- **移除**: `GetTaskExecutionStatus` 和 `GetTaskExecutionProgress` 方法 |
|||
- **原因**: Controller 中没有对应的端点 |
|||
- **清理**: 移除相关的 using 语句和导入 |
|||
|
|||
#### 4. 简化 taskExecutionService.ts |
|||
- **移除接口**: `GetTaskExecutionStatusRequest/Response` 和 `GetTaskExecutionProgressRequest/Response` |
|||
- **移除方法**: `getTaskExecutionStatus()` 和 `getTaskExecutionProgress()` |
|||
- **保留方法**: 只保留 `startTaskExecution()` 和 `stopTaskExecution()` |
|||
|
|||
### 技术实现 |
|||
|
|||
#### 1. Controller 路由设计 |
|||
```csharp |
|||
[ApiController] |
|||
[Route("api/taskexecution")] // 使用具体路径,与其他控制器保持一致 |
|||
[Authorize] |
|||
public class TaskExecutionController : ApiController |
|||
{ |
|||
[HttpPost("start")] // POST /api/taskexecution/start |
|||
public async Task<OperationResult<StartTaskExecutionResponse>> StartTaskExecution(...) |
|||
|
|||
[HttpPost("stop")] // POST /api/taskexecution/stop |
|||
public async Task<OperationResult<StopTaskExecutionResponse>> StopTaskExecution(...) |
|||
} |
|||
``` |
|||
|
|||
#### 2. 前端服务简化 |
|||
```typescript |
|||
class TaskExecutionService { |
|||
private readonly baseUrl = API_PATHS.TASK_EXECUTION; // '/taskexecution' |
|||
|
|||
// 只保留与 Controller 对应的方法 |
|||
async startTaskExecution(request: StartTaskExecutionRequest): Promise<OperationResult<StartTaskExecutionResponse>> { |
|||
return httpClient.post<StartTaskExecutionResponse>(`${this.baseUrl}/start`, request); |
|||
} |
|||
|
|||
async stopTaskExecution(request: StopTaskExecutionRequest): Promise<OperationResult<StopTaskExecutionResponse>> { |
|||
return httpClient.post<StopTaskExecutionResponse>(`${this.baseUrl}/stop`, request); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
#### 3. API 路径对应关系 |
|||
``` |
|||
前端服务方法 → 后端 Controller 方法 |
|||
startTaskExecution() → POST /api/taskexecution/start |
|||
stopTaskExecution() → POST /api/taskexecution/stop |
|||
``` |
|||
|
|||
### 架构优势 |
|||
|
|||
#### 1. 一致性 |
|||
- **路由标准**: 与其他控制器保持完全一致的路由设计 |
|||
- **命名规范**: 使用具体的路径而不是动态路由 |
|||
- **方法对应**: 前端服务与后端 Controller 完全对应 |
|||
|
|||
#### 2. 简洁性 |
|||
- **代码精简**: 移除冗余的接口和方法 |
|||
- **维护简单**: 减少不必要的代码维护成本 |
|||
- **逻辑清晰**: 只保留实际需要的功能 |
|||
|
|||
#### 3. 可维护性 |
|||
- **单一职责**: 每个方法都有明确的对应关系 |
|||
- **易于测试**: 简化的结构便于单元测试 |
|||
- **易于扩展**: 需要时可以轻松添加新的端点 |
|||
|
|||
### 与其他控制器的对比 |
|||
|
|||
#### 1. TestScenarioTasksController |
|||
```csharp |
|||
[Route("api/testscenariotasks")] // 具体路径 |
|||
[HttpGet] // GET /api/testscenariotasks |
|||
[HttpGet("{id}")] // GET /api/testscenariotasks/{id} |
|||
[HttpPost] // POST /api/testscenariotasks |
|||
``` |
|||
|
|||
#### 2. TestScenariosController |
|||
```csharp |
|||
[Route("api/testscenarios")] // 具体路径 |
|||
[HttpGet] // GET /api/testscenarios |
|||
[HttpGet("{id}")] // GET /api/testscenarios/{id} |
|||
[HttpPost] // POST /api/testscenarios |
|||
``` |
|||
|
|||
#### 3. TaskExecutionController (修复后) |
|||
```csharp |
|||
[Route("api/taskexecution")] // 具体路径,保持一致 |
|||
[HttpPost("start")] // POST /api/taskexecution/start |
|||
[HttpPost("stop")] // POST /api/taskexecution/stop |
|||
``` |
|||
|
|||
### 测试建议 |
|||
|
|||
#### 1. API 端点测试 |
|||
- 测试 `POST /api/taskexecution/start` 端点 |
|||
- 测试 `POST /api/taskexecution/stop` 端点 |
|||
- 验证路由是否正确解析 |
|||
|
|||
#### 2. 前端集成测试 |
|||
- 测试 `startTaskExecution()` 方法调用 |
|||
- 测试 `stopTaskExecution()` 方法调用 |
|||
- 验证请求和响应格式 |
|||
|
|||
### 总结 |
|||
|
|||
本次修复解决了以下问题: |
|||
|
|||
1. ✅ **路由设计**: 修复了不符合项目标准的路由设计 |
|||
2. ✅ **架构一致性**: 与其他控制器保持完全一致的设计模式 |
|||
3. ✅ **代码精简**: 移除了冗余的接口和方法 |
|||
4. ✅ **维护性**: 简化了代码结构,提高了可维护性 |
|||
5. ✅ **对应关系**: 前端服务与后端 Controller 完全对应 |
|||
|
|||
现在 TaskExecutionController 和 taskExecutionService.ts 都符合项目的架构标准,具有良好的一致性和可维护性。 |
|||
@ -0,0 +1,186 @@ |
|||
## 2025-01-21 修复 TaskExecutionService 架构设计 |
|||
|
|||
### 概述 |
|||
|
|||
修复 TaskExecutionService 的架构设计,使其符合当前项目的设计模式,参考 testScenarioTaskService.ts 的实现架构。 |
|||
|
|||
### 问题分析 |
|||
|
|||
#### 原始问题 |
|||
- 使用了不存在的 `apiClient` 导入 |
|||
- 没有遵循当前项目的架构模式 |
|||
- 响应格式与后端不一致 |
|||
- 缺少统一的错误处理机制 |
|||
|
|||
#### 架构不一致性 |
|||
- 应该使用 `httpClient` 而不是 `apiClient` |
|||
- 应该使用 `OperationResult<T>` 统一响应格式 |
|||
- 应该使用 `API_PATHS` 常量管理 API 路径 |
|||
- 应该与后端 Controller 完全对应 |
|||
|
|||
### 主要变更 |
|||
|
|||
#### 1. 修复导入和依赖 |
|||
- **文件**: `X1.WebUI/src/services/taskExecutionService.ts` |
|||
- **变更**: 使用正确的导入 |
|||
```typescript |
|||
// 修复前 |
|||
import { apiClient } from './apiClient'; |
|||
|
|||
// 修复后 |
|||
import { httpClient } from '@/lib/http-client'; |
|||
import { OperationResult } from '@/types/auth'; |
|||
import { API_PATHS } from '@/constants/api'; |
|||
``` |
|||
|
|||
#### 2. 添加 API 路径常量 |
|||
- **文件**: `X1.WebUI/src/constants/api.ts` |
|||
- **新增**: 任务执行相关 API 路径 |
|||
```typescript |
|||
// 任务执行相关 |
|||
TASK_EXECUTION: '/api/taskexecution', |
|||
``` |
|||
|
|||
#### 3. 统一响应格式 |
|||
- **变更**: 使用 `OperationResult<T>` 统一响应格式 |
|||
- **优势**: 与后端 Controller 返回格式完全一致 |
|||
- **包含**: `isSuccess`, `data`, `errorMessages`, `successMessage` |
|||
|
|||
#### 4. 完善类型定义 |
|||
- **新增**: `TaskExecutionStatus` 枚举 |
|||
- **新增**: `TaskExecutionDetail` 接口 |
|||
- **完善**: 请求/响应接口定义,与后端完全对应 |
|||
|
|||
#### 5. 更新响应处理 |
|||
- **文件**: `X1.WebUI/src/pages/taskExecution/TaskExecutionView.tsx` |
|||
- **变更**: 适配新的响应格式 |
|||
```typescript |
|||
// 修复前 |
|||
result.data.taskExecutionId |
|||
|
|||
// 修复后 |
|||
result.data.taskExecution.id |
|||
``` |
|||
|
|||
### 技术实现 |
|||
|
|||
#### 1. 服务类架构 |
|||
```typescript |
|||
class TaskExecutionService { |
|||
private readonly baseUrl = API_PATHS.TASK_EXECUTION; |
|||
|
|||
async startTaskExecution(request: StartTaskExecutionRequest): Promise<OperationResult<StartTaskExecutionResponse>> { |
|||
return httpClient.post<StartTaskExecutionResponse>(`${this.baseUrl}/start`, request); |
|||
} |
|||
|
|||
async stopTaskExecution(request: StopTaskExecutionRequest): Promise<OperationResult<StopTaskExecutionResponse>> { |
|||
return httpClient.post<StopTaskExecutionResponse>(`${this.baseUrl}/stop`, request); |
|||
} |
|||
|
|||
// ... 其他方法 |
|||
} |
|||
``` |
|||
|
|||
#### 2. 类型定义 |
|||
```typescript |
|||
// 任务执行状态枚举 |
|||
export type TaskExecutionStatus = 'Pending' | 'Running' | 'Success' | 'Failed' | 'Cancelled'; |
|||
|
|||
// 任务执行详情接口 |
|||
export interface TaskExecutionDetail { |
|||
id: string; |
|||
taskId: string; |
|||
scenarioCode: string; |
|||
executorId: string; |
|||
status: TaskExecutionStatus; |
|||
startTime?: string; |
|||
endTime?: string; |
|||
duration: number; |
|||
loop: number; |
|||
progress: number; |
|||
runtimeCode: string; |
|||
} |
|||
|
|||
// 启动任务执行响应接口 |
|||
export interface StartTaskExecutionResponse { |
|||
taskExecution: TaskExecutionDetail; |
|||
} |
|||
``` |
|||
|
|||
#### 3. API 路径管理 |
|||
```typescript |
|||
// 在 API_PATHS 中统一管理 |
|||
export const API_PATHS = { |
|||
// ... 其他路径 |
|||
TASK_EXECUTION: '/api/taskexecution', |
|||
} as const; |
|||
``` |
|||
|
|||
### 架构优势 |
|||
|
|||
#### 1. 一致性 |
|||
- **统一导入**: 使用项目标准的 `httpClient` |
|||
- **统一响应**: 使用 `OperationResult<T>` 格式 |
|||
- **统一路径**: 使用 `API_PATHS` 常量管理 |
|||
|
|||
#### 2. 可维护性 |
|||
- **类型安全**: 完整的 TypeScript 类型定义 |
|||
- **错误处理**: 统一的错误处理机制 |
|||
- **代码复用**: 遵循项目既定的架构模式 |
|||
|
|||
#### 3. 扩展性 |
|||
- **模块化**: 清晰的服务类结构 |
|||
- **可测试**: 易于单元测试和集成测试 |
|||
- **可扩展**: 便于添加新的 API 端点 |
|||
|
|||
### 与 testScenarioTaskService.ts 的一致性 |
|||
|
|||
#### 1. 导入模式 |
|||
```typescript |
|||
// 两个服务都使用相同的导入模式 |
|||
import { httpClient } from '@/lib/http-client'; |
|||
import { OperationResult } from '@/types/auth'; |
|||
import { API_PATHS } from '@/constants/api'; |
|||
``` |
|||
|
|||
#### 2. 服务类结构 |
|||
```typescript |
|||
// 相同的服务类结构 |
|||
class ServiceName { |
|||
private readonly baseUrl = API_PATHS.SERVICE_PATH; |
|||
|
|||
async methodName(request: RequestType): Promise<OperationResult<ResponseType>> { |
|||
return httpClient.post<ResponseType>(`${this.baseUrl}/endpoint`, request); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
#### 3. 导出模式 |
|||
```typescript |
|||
// 相同的导出模式 |
|||
export const serviceName = new ServiceName(); |
|||
``` |
|||
|
|||
### 测试建议 |
|||
|
|||
#### 1. 单元测试 |
|||
- 测试服务方法的正确调用 |
|||
- 测试响应格式的正确解析 |
|||
- 测试错误情况的处理 |
|||
|
|||
#### 2. 集成测试 |
|||
- 测试与后端 API 的完整交互 |
|||
- 测试前端组件的正确集成 |
|||
- 测试错误处理和用户反馈 |
|||
|
|||
### 总结 |
|||
|
|||
本次修复通过以下方式解决了架构不一致问题: |
|||
|
|||
1. ✅ **修复导入**: 使用正确的 `httpClient` 和类型定义 |
|||
2. ✅ **统一响应**: 使用 `OperationResult<T>` 格式 |
|||
3. ✅ **路径管理**: 使用 `API_PATHS` 常量 |
|||
4. ✅ **类型安全**: 完整的 TypeScript 类型定义 |
|||
5. ✅ **架构一致**: 与 `testScenarioTaskService.ts` 保持完全一致 |
|||
|
|||
现在 TaskExecutionService 完全符合当前项目的架构设计,具有良好的一致性、可维护性和扩展性。 |
|||
Loading…
Reference in new issue