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