From 1cd09a9501f38a77eb053260eca07ffa4dc74496 Mon Sep 17 00:00:00 2001 From: dengqichen Date: Mon, 20 Oct 2025 22:45:43 +0800 Subject: [PATCH] 1 --- .../src/pages/Workflow2/Definition/service.ts | 32 +++-- .../src/pages/Workflow2/Definition/types.ts | 45 +++++-- .../Workflow2/Design/hooks/useWorkflowLoad.ts | 125 +++++++++++------- .../Workflow2/Design/hooks/useWorkflowSave.ts | 76 +++++------ frontend/src/pages/Workflow2/Design/index.tsx | 3 +- 5 files changed, 165 insertions(+), 116 deletions(-) diff --git a/frontend/src/pages/Workflow2/Definition/service.ts b/frontend/src/pages/Workflow2/Definition/service.ts index 4b0458d1..e6e3a7fd 100644 --- a/frontend/src/pages/Workflow2/Definition/service.ts +++ b/frontend/src/pages/Workflow2/Definition/service.ts @@ -3,8 +3,7 @@ import type { WorkflowDefinition, WorkflowDefinitionQuery, WorkflowCategory } fr import type { Page } from '@/types/base'; // API 基础路径 -const DEFINITION_URL = '/api/v1/workflow-definitions'; -const CATEGORY_URL = '/api/v1/workflow-categories'; +const DEFINITION_URL = '/api/v1/workflow/definition'; /** * 获取工作流定义列表 @@ -19,9 +18,15 @@ export const getDefinitionDetail = (id: number) => request.get(`${DEFINITION_URL}/${id}`); /** - * 创建工作流定义 + * 获取已发布的工作流定义 */ -export const createDefinition = (data: Partial) => +export const getPublishedDefinitions = () => + request.get(`${DEFINITION_URL}/published`); + +/** + * 保存工作流定义(创建) + */ +export const saveDefinition = (data: WorkflowDefinition) => request.post(DEFINITION_URL, data); /** @@ -40,23 +45,28 @@ export const deleteDefinition = (id: number) => * 发布工作流定义 */ export const publishDefinition = (id: number) => - request.post(`${DEFINITION_URL}/${id}/publish`); + request.post(`${DEFINITION_URL}/${id}/published`); + +/** + * 部署工作流定义 + */ +export const deployDefinition = (id: number) => + request.post(`${DEFINITION_URL}/${id}/deploy`); /** * 启动工作流实例 */ -export const startWorkflowInstance = (key: string, category: string, variables?: Record) => - request.post(`/api/v1/workflow-instances/start`, { - definitionKey: key, - category, - variables +export const startWorkflowInstance = (processKey: string, categoryCode: string) => + request.post(`/api/v1/workflow/instance/start`, { + processKey, + businessKey: `${categoryCode}_${Date.now()}`, }); /** * 获取工作流分类 */ export const getWorkflowCategories = () => - request.get(CATEGORY_URL); + request.get(`${DEFINITION_URL}/categories`); /** * 保存工作流图形数据 diff --git a/frontend/src/pages/Workflow2/Definition/types.ts b/frontend/src/pages/Workflow2/Definition/types.ts index a06641f9..30d82365 100644 --- a/frontend/src/pages/Workflow2/Definition/types.ts +++ b/frontend/src/pages/Workflow2/Definition/types.ts @@ -1,21 +1,26 @@ import { BaseResponse, BaseQuery } from '@/types/base'; // 复用现有的工作流定义类型,保持API兼容性 -export interface WorkflowDefinition extends BaseResponse { +export interface WorkflowDefinition { id: number; + createTime: string | null; + createBy: string | null; + updateTime: string | null; + updateBy: string | null; + version: number; + deleted: boolean; + extraData: any; name: string; key: string; - description?: string; - flowVersion?: number; - status?: string; + description: string; category: string; triggers: string[]; + flowVersion: number; + bpmnXml: string | null; + status: string; graph: { nodes: WorkflowDefinitionNode[]; - edges: any[]; - }; - formConfig: { - formItems: any[]; + edges: WorkflowDefinitionEdge[]; }; formVariablesSchema?: { type: string; @@ -36,17 +41,31 @@ export interface WorkflowDefinition extends BaseResponse { }; }; }; - bpmnXml?: string; } export interface WorkflowDefinitionNode { - id: number; + id: string; nodeCode: string; nodeType: string; nodeName: string; - uiVariables: JSON; - panelVariables: JSON; - localVariables: JSON; + position: { + x: number; + y: number; + }; + configs: Record; + inputMapping: Record; + outputMapping: Record; +} + +export interface WorkflowDefinitionEdge { + id: string; + from: string; + to: string; + name: string; + config: { + type: string; + }; + vertices: any[]; } export interface WorkflowDefinitionQuery extends BaseQuery { diff --git a/frontend/src/pages/Workflow2/Design/hooks/useWorkflowLoad.ts b/frontend/src/pages/Workflow2/Design/hooks/useWorkflowLoad.ts index 63729d9e..752c4e92 100644 --- a/frontend/src/pages/Workflow2/Design/hooks/useWorkflowLoad.ts +++ b/frontend/src/pages/Workflow2/Design/hooks/useWorkflowLoad.ts @@ -3,6 +3,7 @@ import { message } from 'antd'; import * as definitionService from '../../Definition/service'; import type { FlowNode, FlowEdge } from '../types'; import type { WorkflowDefinition } from '../../Definition/types'; +import { NODE_DEFINITIONS } from '../nodes/definitions'; interface LoadedWorkflowData { nodes: FlowNode[]; @@ -16,40 +17,39 @@ export const useWorkflowLoad = () => { // 从后端数据转换为React Flow格式 const convertToFlowFormat = useCallback((definition: WorkflowDefinition): LoadedWorkflowData => { - const nodes: FlowNode[] = (definition.graph?.nodes || []).map(node => ({ - id: node.id.toString(), - type: node.nodeType, - position: node.uiVariables?.position || { x: 100, y: 100 }, - data: { - label: node.nodeName, - nodeType: node.nodeType as any, - category: 'TASK' as any, // 默认分类,可以根据nodeType推导 - icon: getNodeIcon(node.nodeType), - color: getNodeColor(node.nodeType), - configs: node.panelVariables || {}, - inputMapping: node.localVariables?.inputMapping || {}, - outputMapping: node.localVariables?.outputMapping || {}, - nodeDefinition: { - nodeCode: node.nodeCode, - nodeName: node.nodeName, + const nodes: FlowNode[] = (definition.graph?.nodes || []).map(node => { + // 根据nodeType查找对应的节点定义 + const nodeDefinition = NODE_DEFINITIONS.find(def => def.nodeType === node.nodeType); + + return { + id: node.id.toString(), + type: node.nodeType, + position: node.position || { x: 100, y: 100 }, + data: { + label: node.nodeName, nodeType: node.nodeType as any, - category: 'TASK' as any, - icon: getNodeIcon(node.nodeType), - color: getNodeColor(node.nodeType) - } - }, - selected: node.uiVariables?.selected || false, - dragging: node.uiVariables?.dragging || false - })); + category: getNodeCategory(node.nodeType) as any, + icon: nodeDefinition?.uiConfig.style.icon || getNodeIcon(node.nodeType), + color: nodeDefinition?.uiConfig.style.fill || getNodeColor(node.nodeType), + configs: node.configs || {}, + inputMapping: node.inputMapping || {}, + outputMapping: node.outputMapping || {}, + // 添加节点定义引用,用于配置弹窗 + nodeDefinition + }, + selected: false, + dragging: false + }; + }); const edges: FlowEdge[] = (definition.graph?.edges || []).map(edge => ({ id: edge.id, - source: edge.source, - target: edge.target, - type: edge.type || 'default', - animated: edge.animated || true, - data: edge.data || { - label: '连接', + source: edge.from, // 后端使用from字段作为source + target: edge.to, // 后端使用to字段作为target + type: 'default', + animated: true, + data: { + label: edge.name || '连接', condition: { type: 'DEFAULT', priority: 0 @@ -64,32 +64,49 @@ export const useWorkflowLoad = () => { }; }, []); - // 根据节点类型获取图标 + // 根据节点类型获取分类 + const getNodeCategory = (nodeType: string): string => { + const categoryMap: Record = { + 'START_EVENT': 'EVENT', + 'END_EVENT': 'EVENT', + 'USER_TASK': 'TASK', + 'SERVICE_TASK': 'TASK', + 'SCRIPT_TASK': 'TASK', + 'DEPLOY_NODE': 'TASK', + 'JENKINS_BUILD': 'TASK', + 'GATEWAY_NODE': 'GATEWAY', + 'SUB_PROCESS': 'CONTAINER', + 'CALL_ACTIVITY': 'CONTAINER' + }; + return categoryMap[nodeType] || 'TASK'; + }; + + // 根据节点类型获取图标(图标名称格式) const getNodeIcon = (nodeType: string): string => { const iconMap: Record = { - 'START_EVENT': '▶️', - 'END_EVENT': '⏹️', - 'USER_TASK': '👤', - 'SERVICE_TASK': '⚙️', - 'SCRIPT_TASK': '📜', - 'DEPLOY_NODE': '🚀', - 'JENKINS_BUILD': '🔨', - 'GATEWAY_NODE': '◇', - 'SUB_PROCESS': '📦', - 'CALL_ACTIVITY': '📞' + 'START_EVENT': 'play-circle', + 'END_EVENT': 'stop-circle', + 'USER_TASK': 'user', + 'SERVICE_TASK': 'api', + 'SCRIPT_TASK': 'code', + 'DEPLOY_NODE': 'build', + 'JENKINS_BUILD': 'jenkins', + 'GATEWAY_NODE': 'gateway', + 'SUB_PROCESS': 'container', + 'CALL_ACTIVITY': 'phone' }; - return iconMap[nodeType] || '📋'; + return iconMap[nodeType] || 'api'; }; // 根据节点类型获取颜色 const getNodeColor = (nodeType: string): string => { const colorMap: Record = { - 'START_EVENT': '#10b981', - 'END_EVENT': '#ef4444', - 'USER_TASK': '#6366f1', - 'SERVICE_TASK': '#f59e0b', + 'START_EVENT': '#52c41a', + 'END_EVENT': '#ff4d4f', + 'USER_TASK': '#722ed1', + 'SERVICE_TASK': '#fa8c16', 'SCRIPT_TASK': '#8b5cf6', - 'DEPLOY_NODE': '#06b6d4', + 'DEPLOY_NODE': '#1890ff', 'JENKINS_BUILD': '#f97316', 'GATEWAY_NODE': '#84cc16', 'SUB_PROCESS': '#ec4899', @@ -123,18 +140,24 @@ export const useWorkflowLoad = () => { const createNewWorkflow = useCallback((): LoadedWorkflowData => { const emptyDefinition: WorkflowDefinition = { id: 0, + createTime: null, + createBy: null, + updateTime: null, + updateBy: null, + version: 1, + deleted: false, + extraData: null, name: '新建工作流', key: `workflow_${Date.now()}`, description: '', category: 'GENERAL', - triggers: ['MANUAL'], + triggers: [], + flowVersion: 1, + bpmnXml: null, status: 'DRAFT', graph: { nodes: [], edges: [] - }, - formConfig: { - formItems: [] } }; diff --git a/frontend/src/pages/Workflow2/Design/hooks/useWorkflowSave.ts b/frontend/src/pages/Workflow2/Design/hooks/useWorkflowSave.ts index d25aa54b..04572f9e 100644 --- a/frontend/src/pages/Workflow2/Design/hooks/useWorkflowSave.ts +++ b/frontend/src/pages/Workflow2/Design/hooks/useWorkflowSave.ts @@ -2,7 +2,6 @@ import { useCallback, useState } from 'react'; import { message } from 'antd'; import * as definitionService from '../../Definition/service'; import type { FlowNode, FlowEdge } from '../types'; -import type { WorkflowDefinition } from '../../Definition/types'; interface WorkflowSaveData { nodes: FlowNode[]; @@ -10,6 +9,7 @@ interface WorkflowSaveData { workflowId?: number; name?: string; description?: string; + definitionData?: any; // 原始工作流定义数据 } export const useWorkflowSave = () => { @@ -25,57 +25,53 @@ export const useWorkflowSave = () => { // 转换节点和边数据为后端格式 const graph = { nodes: data.nodes.map(node => ({ - id: parseInt(node.id) || 0, - nodeCode: `node_${node.id}`, + id: node.id, // 使用React Flow的节点ID + nodeCode: node.id, // nodeCode与ID相同 nodeType: node.data.nodeType, nodeName: node.data.label, - uiVariables: { - position: node.position, - selected: node.selected || false, - dragging: node.dragging || false + position: { + x: Math.round(node.position.x), + y: Math.round(node.position.y) }, - panelVariables: node.data.configs || {}, - localVariables: { - inputMapping: node.data.inputMapping || {}, - outputMapping: node.data.outputMapping || {} - } + configs: node.data.configs || {}, + inputMapping: node.data.inputMapping || {}, + outputMapping: node.data.outputMapping || {} })), edges: data.edges.map(edge => ({ id: edge.id, - source: edge.source, - target: edge.target, - type: edge.type || 'default', - animated: edge.animated || false, - data: edge.data || {} + from: edge.source, // 后端使用from字段 + to: edge.target, // 后端使用to字段 + name: edge.data?.label || "", // 边的名称 + config: { + type: "sequence" // 固定为sequence类型 + }, + vertices: [] // 暂时为空数组 })) }; - const workflowData: Partial = { - name: data.name || '未命名工作流', - description: data.description || '', - graph, - // 生成简单的表单配置 - formConfig: { - formItems: [] + // 构建保存数据 - 完全模仿原始系统的逻辑 + const workflowData = data.definitionData + ? { + // 如果有原始定义数据,展开它并覆盖 graph + ...data.definitionData, + graph } - }; - - let result: WorkflowDefinition; - - if (data.workflowId) { - // 更新现有工作流 - result = await definitionService.updateDefinition(data.workflowId, workflowData); - message.success('工作流保存成功'); - } else { - // 创建新工作流 - result = await definitionService.createDefinition({ - ...workflowData, + : { + // 如果没有原始定义数据,创建新的工作流 + name: data.name || '新建工作流', + description: data.description || '', key: `workflow_${Date.now()}`, category: 'GENERAL', - triggers: ['MANUAL'] - } as any); - message.success('工作流创建成功'); - } + triggers: [], + flowVersion: 1, + bpmnXml: null, + status: 'DRAFT', + graph + }; + + // 无论新建还是更新,都调用 saveDefinition (POST 请求) + await definitionService.saveDefinition(workflowData as any); + message.success('工作流保存成功'); setLastSaved(new Date()); setHasUnsavedChanges(false); diff --git a/frontend/src/pages/Workflow2/Design/index.tsx b/frontend/src/pages/Workflow2/Design/index.tsx index 1365c7ff..07789dc7 100644 --- a/frontend/src/pages/Workflow2/Design/index.tsx +++ b/frontend/src/pages/Workflow2/Design/index.tsx @@ -158,7 +158,8 @@ const WorkflowDesignInner: React.FC = () => { edges, workflowId: currentWorkflowId, name: workflowTitle, - description: workflowDefinition?.description || '' + description: workflowDefinition?.description || '', + definitionData: workflowDefinition // 传递原始定义数据 }); if (success) {