deploy-ease-platform/backend/docs/deploy-flow-graph-frontend-guide.md
2025-11-04 22:37:01 +08:00

417 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 部署记录流程图前端绘制指南
## 概述
本文档说明前端如何调用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**:部分成功(工作流完成但存在失败的节点)