重试发送任务功能.md 11 KB

重试发送任务功能实现文档

功能概述

实现了传输队列中的【重试发送任务】功能,允许用户选择失败的发送任务并重新提交到后端执行。

一、涉及的参与者

1. 页面层(Page Layer)

  • OutputlistPage (src/pages/patient/OutputList.tsx)
    • 主容器页面,负责展示传输队列
    • 管理表格、分页和操作面板
    • 维护任务选中状态

2. 组件层(Component Layer)

  • OutputOperationPanel (src/pages/patient/components/OutputOperationPanel.tsx)

    • 操作面板容器组件
  • OutputActionPanel (src/pages/patient/components/OutputActionPanel.tsx)

    • 包含重试和删除操作按钮
    • 实现重试按钮的点击处理逻辑

3. 状态管理层(State Management Layer)

  • sendJobSlice (src/states/output/sendJob/slices/sendJobSlice.ts)

    • retrySendJobThunk: 重试任务的异步 Thunk
    • createRetryHandlers: 处理重试结果的 handlers
  • Redux Store

    • sendJobSelection.selectedIds: 维护选中的任务 IDs
    • sendJobEntities.data: 存储任务数据

4. API 层(API Layer)

  • sendJobActions (src/API/output/sendJobActions.ts)
    • retrySendTask(taskId): 调用后端重试接口
    • RetrySendTaskRequest: 请求参数类型
    • RetrySendTaskResponse: 响应数据类型

5. 领域模型层(Domain Layer)

  • SendJob (src/domain/output/sendJob.ts)
    • 任务实体模型
    • 包含 task_idstatusretry_count 等字段

二、实现修改清单

2.1 API 层修改

文件: src/API/output/sendJobActions.ts

修改内容:

  1. 修改请求参数接口 ```typescript // 修改前 export interface RetrySendTaskRequest { instance_uid: string; }

// 修改后 export interface RetrySendTaskRequest { task_id: string; }


2. **修改 retrySendTask 函数**
```typescript
// 修改前
export const retrySendTask = async (
  instanceUid: string
): Promise<RetrySendTaskResponse> => {
  const response = await axiosInstance.post<RetrySendTaskResponse>(
    '/auth/scp/store_reply',
    { instance_uid: instanceUid }
  );
  // ...
}

// 修改后
export const retrySendTask = async (
  taskId: string
): Promise<RetrySendTaskResponse> => {
  const response = await axiosInstance.post<RetrySendTaskResponse>(
    '/auth/scp/store_reply',
    { task_id: taskId }
  );
  // ...
}

2.2 状态管理层修改

文件: src/states/output/sendJob/slices/sendJobSlice.ts

修改内容:

  1. 简化 retrySendJobThunk 参数 ```typescript // 修改前 export const retrySendJobThunk = createAsyncThunk( 'sendJob/retry', async ({ taskId, instanceUid }: { taskId: string; instanceUid: string }) => { const result = await retrySendTask(instanceUid); return { taskId, result }; } );

// 修改后 export const retrySendJobThunk = createAsyncThunk( 'sendJob/retry', async (taskId: string) => {

const result = await retrySendTask(taskId);
return { taskId, result };

} );


### 2.3 组件层修改

**文件:** `src/pages/patient/components/OutputActionPanel.tsx`

#### 修改内容:

1. **添加必要的导入**
```typescript
import { message } from 'antd';
import { useAppDispatch, useAppSelector } from '@/states/store';
import { retrySendJobThunk } from '@/states/output/sendJob/slices/sendJobSlice';
  1. 添加重试处理逻辑 ```typescript const OutputActionPanel: React.FC = () => { const dispatch = useAppDispatch(); const selectedIds = useAppSelector((state) => state.sendJobSelection.selectedIds);

// 处理重试按钮点击 const handleRetry = async () => {

// 校验是否有选中的任务
if (selectedIds.length === 0) {
  message.warning('请先选择要重试的任务');
  return;
}

try {
  // 遍历选中的任务,逐个调用重试 API
  const retryPromises = selectedIds.map((taskId) =>
    dispatch(retrySendJobThunk(taskId)).unwrap()
  );

  await Promise.all(retryPromises);
  message.success(`成功提交 ${selectedIds.length} 个重试任务`);
} catch (error) {
  console.error('重试任务失败:', error);
  message.error('重试任务失败,请稍后再试');
}

};

return (

// ...

); };


3. **添加按钮点击事件**
```typescript
<Button
  type="default"
  icon={/* ... */}
  aria-label="重试"
  style={{ width: '1.5rem', height: '1.5rem' }}
  onClick={handleRetry}  // 添加此行
/>

三、组件交互流程

3.1 用户操作流程

sequenceDiagram
    participant User as 用户
    participant Button as 重试按钮
    participant Handler as handleRetry
    participant Redux as Redux Store
    participant Thunk as retrySendJobThunk
    participant API as retrySendTask API
    participant Backend as 后端服务

    User->>Button: 1. 点击重试按钮
    Button->>Handler: 2. 触发 onClick
    Handler->>Redux: 3. 获取 selectedIds
    
    alt 没有选中任务
        Handler->>User: 4a. 显示警告消息
    else 有选中任务
        loop 每个选中的任务
            Handler->>Thunk: 4b. dispatch(retrySendJobThunk(taskId))
            Thunk->>API: 5. 调用 retrySendTask(taskId)
            API->>Backend: 6. POST /auth/scp/store_reply
            Backend-->>API: 7. 返回结果
            API-->>Thunk: 8. 返回 response
            Thunk->>Redux: 9. 更新任务状态
        end
        Handler->>User: 10. 显示成功消息
    end

3.2 数据流向

1. 用户选择任务
   └─> handleRowClick (OutputList)
       └─> dispatch(setSelectedIds([taskId]))
           └─> Redux: sendJobSelection.selectedIds = [taskId]

2. 用户点击重试
   └─> handleRetry (OutputActionPanel)
       └─> 读取 selectedIds
           └─> 遍历并 dispatch(retrySendJobThunk(taskId))
               └─> retrySendTask(taskId) API 调用
                   └─> POST /auth/scp/store_reply { task_id }
                       └─> 后端处理并返回结果
                           └─> Redux 更新任务状态
                               └─> 表格自动刷新

四、相关数据结构

4.1 SendJob 实体

interface SendJob {
  task_id: string;           // 任务唯一标识
  patient_name: string;      // 患者姓名
  patient_id: string;        // 患者 ID
  priority: SendJobPriority; // 优先级: High/Medium/Low
  status: SendJobStatus;     // 状态: ARRIVED/SENDING/FAILED/SUCCESS
  retry_count?: number;      // 重试次数
  destination: string;       // 目标 PACS 节点
}

4.2 API 请求/响应

// 请求参数
interface RetrySendTaskRequest {
  task_id: string;
}

// 响应数据
interface RetrySendTaskResponse {
  code: string;              // 响应码:0x000000 表示成功
  description: string;       // 描述信息
  solution: string;          // 解决方案
  data: StoreReplyData;
}

interface StoreReplyData {
  '@type': string;
  ok: boolean;              // 是否成功
  output: string;           // 输出信息
}

4.3 Redux State

// 选中状态
interface SelectionState {
  selectedIds: string[];    // 选中的 task_id 数组
}

// 任务列表状态
interface EntitiesState<SendJob> {
  data: SendJob[];          // 任务数组
  total: number;            // 总数
}

五、功能特性

5.1 用户体验优化

  1. 校验提示

    • 未选中任务时显示警告:"请先选择要重试的任务"
  2. 成功反馈

    • 显示具体重试的任务数量:"成功提交 N 个重试任务"
  3. 错误处理

    • API 调用失败时显示友好错误消息
    • Console 输出详细错误信息便于调试
  4. 批量操作

    • 支持同时重试多个任务
    • 使用 Promise.all 并行执行,提高效率

5.2 状态自动更新

重试成功后,Redux Store 会自动更新:

  • status 更新为 'SENDING'
  • retry_count 自动增加 1
  • 表格自动刷新显示最新状态

5.3 错误容错

  • API 层捕获并抛出错误
  • Thunk 层处理 rejected 状态
  • 组件层显示用户友好的错误提示

六、测试要点

6.1 功能测试

  1. 正常流程

    • 选中单个任务,点击重试,验证成功
    • 选中多个任务,点击重试,验证批量成功
    • 验证重试后任务状态更新为 SENDING
    • 验证 retry_count 正确增加
  2. 边界情况

    • 未选中任务时点击重试,显示警告
    • 选中后取消选中,再点击重试,显示警告
  3. 错误处理

    • 模拟网络错误,验证错误提示
    • 模拟后端返回错误码,验证错误处理

6.2 集成测试

  1. 与表格的集成

    • 验证选中状态正确同步
    • 验证表格数据更新后正确显示
  2. 与分页的集成

    • 跨页选中任务,验证重试功能

七、API 接口说明

7.1 重试接口

端点: POST /auth/scp/store_reply

请求体:

{
  "task_id": "0199cd46-82f0-76c5-b1d3-9399668a1a05"
}

响应示例(成功):

{
  "code": "0x000000",
  "description": "Success",
  "solution": "",
  "data": {
    "@type": "StoreReply",
    "ok": true,
    "output": "Successfully retried task"
  }
}

响应示例(失败):

{
  "code": "0x000001",
  "description": "Task not found",
  "solution": "Please check the task ID",
  "data": {
    "@type": "StoreReply",
    "ok": false,
    "output": "Task does not exist"
  }
}

八、注意事项

  1. 参数变更

    • 原设计使用 instance_uid,实际实现改为使用 task_id
    • 确保后端接口支持 task_id 参数
  2. 状态更新

    • 重试成功后状态设为 SENDING,而非 SUCCESS
    • 实际成功与否需要后续状态轮询确认
  3. 并发控制

    • 当前实现使用 Promise.all 并行重试
    • 如需控制并发数,可使用 Promise.allSettled 或限流
  4. 权限控制

    • 当前未做权限检查
    • 如需要,应在按钮层或 API 层添加权限校验

九、未来改进方向

  1. 加载状态

    • 添加按钮 loading 状态,防止重复点击
    • 显示重试进度(如 "重试中... 2/5")
  2. 单独重试

    • 在表格行中添加单独的重试按钮
    • 支持直接重试单个任务,无需选中
  3. 重试策略

    • 支持配置重试次数限制
    • 支持指数退避重试
  4. 结果反馈

    • 显示详细的重试结果(成功 N 个,失败 M 个)
    • 失败时显示具体原因

十、总结

本次实现完成了传输队列的重试发送任务功能,主要包括:

  1. ✅ 修改 API 层参数,从 instance_uid 改为 task_id
  2. ✅ 简化状态管理层 Thunk 参数
  3. ✅ 实现组件层重试逻辑和用户交互
  4. ✅ 支持批量重试多个任务
  5. ✅ 实现完善的错误处理和用户反馈
  6. ✅ 自动更新任务状态和重试计数

该功能已完全集成到现有的传输队列管理系统中,遵循了项目的架构设计和编码规范。


实现日期: 2025-10-11
实现人员: Cline
文档版本: 1.0