# 部署记录流程图前端绘制指南

## 概述

本文档说明前端如何调用API获取部署记录的工作流流程图数据，以及如何使用这些数据绘制流程图并标记节点状态。

## API接口

### 获取部署流程图数据

**接口地址：** `GET /api/v1/deploy/records/{deployRecordId}/flow-graph`

**路径参数：**
- `deployRecordId` (Long): 部署记录ID

**响应示例：**
```json
{
  "success": true,
  "message": "操作成功",
  "data": {
    "deployRecordId": 16,
    "workflowInstanceId": 16,
    "processInstanceId": "b24d97ec-b97f-11f0-be70-de5a4815c9ef",
    "deployStatus": "REJECTED",
    "graph": {
      "nodes": [
        {
          "id": "startNode",
          "nodeCode": "start",
          "nodeType": "START",
          "nodeName": "开始",
          "position": {
            "x": 100,
            "y": 200
          },
          "configs": {},
          "inputMapping": {},
          "outputs": []
        },
        {
          "id": "sid_4ee9fab9_d626_4691_816c_9435902fc3d0",
          "nodeCode": "approval",
          "nodeType": "APPROVAL",
          "nodeName": "审批节点",
          "position": {
            "x": 300,
            "y": 200
          },
          "configs": {
            "userIds": ["admin"]
          },
          "inputMapping": {},
          "outputs": []
        },
        {
          "id": "endNode",
          "nodeCode": "end",
          "nodeType": "END",
          "nodeName": "结束",
          "position": {
            "x": 500,
            "y": 200
          },
          "configs": {},
          "inputMapping": {},
          "outputs": []
        }
      ],
      "edges": [
        {
          "source": "startNode",
          "target": "sid_4ee9fab9_d626_4691_816c_816c_9435902fc3d0"
        },
        {
          "source": "sid_4ee9fab9_d626_4691_816c_9435902fc3d0",
          "target": "endNode"
        }
      ]
    },
    "nodeInstances": [
      {
        "id": 1,
        "processInstanceId": "b24d97ec-b97f-11f0-be70-de5a4815c9ef",
        "nodeId": "startNode",
        "nodeName": "开始",
        "nodeType": "START",
        "status": "COMPLETED",
        "startTime": "2025-11-04T21:10:00",
        "endTime": "2025-11-04T21:10:01"
      },
      {
        "id": 2,
        "processInstanceId": "b24d97ec-b97f-11f0-be70-de5a4815c9ef",
        "nodeId": "sid_4ee9fab9_d626_4691_816c_9435902fc3d0",
        "nodeName": "审批节点",
        "nodeType": "APPROVAL",
        "status": "REJECTED",
        "startTime": "2025-11-04T21:10:01",
        "endTime": "2025-11-04T21:14:34"
      },
      {
        "id": null,
        "processInstanceId": "b24d97ec-b97f-11f0-be70-de5a4815c9ef",
        "nodeId": "endNode",
        "nodeName": "结束",
        "nodeType": "END",
        "status": "NOT_STARTED",
        "startTime": null,
        "endTime": null
      }
    ]
  }
}
```

## 数据结构说明

### DeployRecordFlowGraphDTO

| 字段 | 类型 | 说明 |
|------|------|------|
| `deployRecordId` | Long | 部署记录ID |
| `workflowInstanceId` | Long | 工作流实例ID |
| `processInstanceId` | String | Flowable流程实例ID |
| `deployStatus` | String | 部署状态（CREATED/PENDING_APPROVAL/RUNNING/SUCCESS/FAILED/REJECTED/CANCELLED/TERMINATED/PARTIAL_SUCCESS） |
| `graph` | WorkflowDefinitionGraph | 流程图结构数据（包含节点和边的位置信息） |
| `nodeInstances` | List<WorkflowNodeInstanceDTO> | 节点执行状态列表 |

### WorkflowDefinitionGraph

| 字段 | 类型 | 说明 |
|------|------|------|
| `nodes` | List<WorkflowDefinitionGraphNode> | 节点列表（包含位置信息） |
| `edges` | List<WorkflowDefinitionGraphEdge> | 边列表（连接关系） |

### WorkflowDefinitionGraphNode

| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | String | 节点ID（用于匹配nodeInstances） |
| `nodeCode` | String | 节点代码 |
| `nodeType` | String | 节点类型（START/END/APPROVAL/SHELL等） |
| `nodeName` | String | 节点名称 |
| `position` | Map<String, Object> | 节点位置信息（x, y坐标） |
| `configs` | Map<String, Object> | 节点配置信息 |
| `inputMapping` | Map<String, Object> | 输入映射 |
| `outputs` | List | 输出字段列表 |

### WorkflowNodeInstanceDTO

| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | Long | 节点实例ID（可能为null，表示未开始） |
| `nodeId` | String | 节点ID（与graph.nodes中的id匹配） |
| `nodeName` | String | 节点名称 |
| `nodeType` | String | 节点类型 |
| `status` | String | 节点执行状态（NOT_STARTED/RUNNING/COMPLETED/FAILED/REJECTED等） |
| `startTime` | LocalDateTime | 开始时间 |
| `endTime` | LocalDateTime | 结束时间 |

## 前端绘制流程

### 1. 调用API获取数据

```typescript
// 使用React示例
const fetchDeployFlowGraph = async (deployRecordId: number) => {
  const response = await fetch(`/api/v1/deploy/records/${deployRecordId}/flow-graph`, {
    headers: {
      'Authorization': `Bearer ${token}`
    }
  });
  const result = await response.json();
  return result.data;
};
```

### 2. 绘制流程图

使用流程图库（如 `react-flow`、`antv x6`、`mxGraph` 等）绘制流程图：

#### 步骤1：创建节点映射

```typescript
// 将nodeInstances转换为Map，方便查找
const nodeStatusMap = new Map(
  data.nodeInstances.map(node => [node.nodeId, node])
);
```

#### 步骤2：根据graph.nodes创建节点

```typescript
const flowNodes = data.graph.nodes.map(node => {
  const nodeInstance = nodeStatusMap.get(node.id);
  const status = nodeInstance?.status || 'NOT_STARTED';
  
  return {
    id: node.id,
    type: mapNodeType(node.nodeType), // 根据节点类型选择不同的节点组件
    position: { x: node.position.x, y: node.position.y },
    data: {
      label: node.nodeName,
      nodeType: node.nodeType,
      status: status, // 节点状态，用于样式标记
      nodeInstance: nodeInstance, // 完整的节点实例信息
      configs: node.configs
    },
    style: getNodeStyle(status) // 根据状态设置样式
  };
});
```

#### 步骤3：根据graph.edges创建边

```typescript
const flowEdges = data.graph.edges.map(edge => ({
  id: `${edge.source}-${edge.target}`,
  source: edge.source,
  target: edge.target,
  style: getEdgeStyle(edge, nodeStatusMap) // 根据节点状态设置边的样式
}));
```

### 3. 节点状态样式映射

```typescript
// 节点状态颜色映射
const statusColorMap = {
  'NOT_STARTED': '#d9d9d9',      // 灰色 - 未开始
  'RUNNING': '#1890ff',          // 蓝色 - 运行中
  'COMPLETED': '#52c41a',        // 绿色 - 已完成
  'FAILED': '#ff4d4f',           // 红色 - 失败
  'REJECTED': '#ff4d4f',         // 红色 - 审批被拒绝
  'CANCELLED': '#d9d9d9',        // 灰色 - 已取消
  'TERMINATED': '#ff4d4f'        // 红色 - 已终止
};

const getNodeStyle = (status: string) => {
  return {
    background: statusColorMap[status] || '#d9d9d9',
    border: `2px solid ${statusColorMap[status] || '#d9d9d9'}`,
    borderRadius: '8px',
    padding: '10px',
    color: '#fff',
    fontWeight: 'bold'
  };
};
```

### 4. 边的状态样式

```typescript
const getEdgeStyle = (edge: any, nodeStatusMap: Map<string, any>) => {
  const sourceStatus = nodeStatusMap.get(edge.source)?.status || 'NOT_STARTED';
  const targetStatus = nodeStatusMap.get(edge.target)?.status || 'NOT_STARTED';
  
  // 如果源节点已完成，边显示为已完成
  if (sourceStatus === 'COMPLETED') {
    return {
      stroke: '#52c41a',
      strokeWidth: 2
    };
  }
  
  // 如果源节点失败或被拒绝，边显示为失败
  if (sourceStatus === 'FAILED' || sourceStatus === 'REJECTED') {
    return {
      stroke: '#ff4d4f',
      strokeWidth: 2,
      strokeDasharray: '5,5' // 虚线表示流程中断
    };
  }
  
  // 默认样式
  return {
    stroke: '#d9d9d9',
    strokeWidth: 1
  };
};
```

### 5. 整体部署状态标记

```typescript
// 根据deployStatus显示整体状态
const deployStatusMap = {
  'CREATED': { color: '#1890ff', text: '已创建' },
  'PENDING_APPROVAL': { color: '#faad14', text: '待审批' },
  'RUNNING': { color: '#1890ff', text: '运行中' },
  'SUCCESS': { color: '#52c41a', text: '部署成功' },
  'FAILED': { color: '#ff4d4f', text: '部署失败' },
  'REJECTED': { color: '#ff4d4f', text: '审批被拒绝' },
  'CANCELLED': { color: '#d9d9d9', text: '已取消' },
  'TERMINATED': { color: '#ff4d4f', text: '已终止' },
  'PARTIAL_SUCCESS': { color: '#faad14', text: '部分成功' }
};

// 在流程图上方显示整体状态
const statusInfo = deployStatusMap[data.deployStatus];
```

## 完整示例（React + react-flow）

```typescript
import React, { useEffect, useState } from 'react';
import ReactFlow, { Node, Edge } from 'react-flow-renderer';

interface DeployFlowGraphProps {
  deployRecordId: number;
}

const DeployFlowGraph: React.FC<DeployFlowGraphProps> = ({ deployRecordId }) => {
  const [nodes, setNodes] = useState<Node[]>([]);
  const [edges, setEdges] = useState<Edge[]>([]);
  const [deployStatus, setDeployStatus] = useState<string>('');

  useEffect(() => {
    fetchDeployFlowGraph(deployRecordId).then(data => {
      // 创建节点状态映射
      const nodeStatusMap = new Map(
        data.nodeInstances.map(node => [node.nodeId, node])
      );

      // 创建节点
      const flowNodes = data.graph.nodes.map(node => {
        const nodeInstance = nodeStatusMap.get(node.id);
        const status = nodeInstance?.status || 'NOT_STARTED';
        
        return {
          id: node.id,
          type: 'default',
          position: { x: node.position.x, y: node.position.y },
          data: {
            label: (
              <div>
                <div>{node.nodeName}</div>
                <div style={{ fontSize: '12px', color: getStatusColor(status) }}>
                  {getStatusText(status)}
                </div>
              </div>
            ),
            status: status
          },
          style: {
            background: getStatusColor(status),
            color: '#fff',
            border: `2px solid ${getStatusColor(status)}`,
            borderRadius: '8px',
            padding: '10px',
            width: 150
          }
        };
      });

      // 创建边
      const flowEdges = data.graph.edges.map(edge => ({
        id: `${edge.source}-${edge.target}`,
        source: edge.source,
        target: edge.target,
        style: {
          stroke: getEdgeColor(edge, nodeStatusMap),
          strokeWidth: 2
        }
      }));

      setNodes(flowNodes);
      setEdges(flowEdges);
      setDeployStatus(data.deployStatus);
    });
  }, [deployRecordId]);

  return (
    <div>
      <div style={{ marginBottom: '20px' }}>
        <h3>部署状态：{getDeployStatusText(deployStatus)}</h3>
      </div>
      <ReactFlow nodes={nodes} edges={edges} fitView />
    </div>
  );
};
```

## 注意事项

1. **节点ID匹配**：`graph.nodes` 中的 `id` 与 `nodeInstances` 中的 `nodeId` 需要匹配，用于确定每个节点的执行状态。

2. **未开始的节点**：如果某个节点还没有开始执行，`nodeInstances` 中可能没有对应的记录，或者 `status` 为 `NOT_STARTED`，`id` 可能为 `null`。

3. **状态优先级**：
   - 如果部署状态为 `REJECTED`，说明审批被拒绝，相关审批节点的状态应该也是 `REJECTED`。
   - 如果部署状态为 `PARTIAL_SUCCESS`，说明部分节点失败，需要检查哪些节点的状态是 `FAILED`。

4. **节点位置**：`graph.nodes` 中的 `position` 字段包含了节点在画布上的位置信息（x, y坐标），直接使用即可。

5. **边的绘制顺序**：`graph.edges` 中的边定义了节点之间的连接关系，按照 `source` 和 `target` 绘制即可。

## 状态流转示意

```
CREATED → PENDING_APPROVAL → RUNNING → SUCCESS
                              ↓
                          FAILED/REJECTED
```

- **CREATED**：部署记录已创建，工作流已启动
- **PENDING_APPROVAL**：等待审批中
- **RUNNING**：审批通过，正在执行部署
- **SUCCESS**：部署成功
- **FAILED**：部署失败
- **REJECTED**：审批被拒绝（终态）
- **CANCELLED**：已取消
- **TERMINATED**：已终止
- **PARTIAL_SUCCESS**：部分成功（工作流完成但存在失败的节点）

