254 lines
6.9 KiB
TypeScript
254 lines
6.9 KiB
TypeScript
/*
|
||
* Copyright 2025 coze-dev Authors
|
||
*
|
||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
* you may not use this file except in compliance with the License.
|
||
* You may obtain a copy of the License at
|
||
*
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* Unless required by applicable law or agreed to in writing, software
|
||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
* See the License for the specific language governing permissions and
|
||
* limitations under the License.
|
||
*/
|
||
|
||
import { injectable, multiInject, optional } from 'inversify';
|
||
import {
|
||
WorkflowDocument,
|
||
FlowNodeBaseType,
|
||
WorkflowLineEntity,
|
||
type WorkflowNodeEntity,
|
||
type WorkflowEdgeJSON,
|
||
type WorkflowJSON,
|
||
type WorkflowNodeJSON,
|
||
WorkflowJSONFormatContribution,
|
||
} from '@flowgram-adapter/free-layout-editor';
|
||
import { compose } from '@flowgram-adapter/common';
|
||
|
||
@injectable()
|
||
export class WorkflowDocumentWithFormat extends WorkflowDocument {
|
||
@multiInject(WorkflowJSONFormatContribution)
|
||
@optional()
|
||
protected jsonFormats: WorkflowJSONFormatContribution[] = [];
|
||
|
||
/**
|
||
* 从数据加载
|
||
* @param json
|
||
*/
|
||
fromJSON(json: Partial<WorkflowJSON>, fireRender = true): void {
|
||
const { flattenJSON, nodeBlocks, nodeEdges } = this.flatJSON(json);
|
||
const formattedJSON: WorkflowJSON = this.formatWorkflowJSON<WorkflowJSON>(
|
||
flattenJSON,
|
||
'formatOnInit',
|
||
this,
|
||
);
|
||
const nestedJSON = this.nestJSON(formattedJSON, nodeBlocks, nodeEdges);
|
||
super.fromJSON(nestedJSON, fireRender);
|
||
}
|
||
|
||
private _formatCache = new Map<string | number | symbol, any>();
|
||
|
||
/**
|
||
* 转换 json
|
||
* @param json
|
||
* @param formatKey
|
||
* @param args
|
||
* @protected
|
||
*/
|
||
protected formatWorkflowJSON<T>(
|
||
json: T,
|
||
formatKey: keyof WorkflowJSONFormatContribution,
|
||
...args: any[]
|
||
): T {
|
||
if (this._formatCache.has(formatKey)) {
|
||
return this._formatCache.get(formatKey)(json, ...args);
|
||
}
|
||
const fns: any[] = this.jsonFormats
|
||
.map(format =>
|
||
format[formatKey] ? format[formatKey]!.bind(format) : undefined,
|
||
)
|
||
.filter(f => Boolean(f));
|
||
const fn = compose<T>(...fns);
|
||
this._formatCache.set(formatKey, fn);
|
||
return fn(json, ...args);
|
||
}
|
||
|
||
/**
|
||
* 创建流程节点
|
||
* @param json
|
||
*/
|
||
createWorkflowNode(
|
||
json: WorkflowNodeJSON,
|
||
isClone?: boolean,
|
||
parentId?: string,
|
||
): WorkflowNodeEntity {
|
||
json = this.formatWorkflowJSON<WorkflowNodeJSON>(
|
||
json,
|
||
'formatNodeOnInit',
|
||
this,
|
||
isClone,
|
||
);
|
||
return super.createWorkflowNode(json, isClone, parentId);
|
||
}
|
||
|
||
toNodeJSON(node: WorkflowNodeEntity): WorkflowNodeJSON {
|
||
const json = super.toNodeJSON(node);
|
||
// 格式化
|
||
const formattedJSON = this.formatWorkflowJSON<WorkflowNodeJSON>(
|
||
json,
|
||
'formatNodeOnSubmit',
|
||
this,
|
||
node,
|
||
);
|
||
return formattedJSON;
|
||
}
|
||
|
||
/**
|
||
* 导出数据
|
||
*/
|
||
toJSON(): WorkflowJSON {
|
||
const rootJSON = this.toNodeJSON(this.root);
|
||
const json = this.formatWorkflowJSON(
|
||
{
|
||
nodes: rootJSON.blocks ?? [],
|
||
edges: rootJSON.edges ?? [],
|
||
},
|
||
'formatOnSubmit',
|
||
this,
|
||
);
|
||
return json;
|
||
}
|
||
|
||
private getEdgeID(edge: WorkflowEdgeJSON): string {
|
||
return WorkflowLineEntity.portInfoToLineId({
|
||
from: edge.sourceNodeID,
|
||
to: edge.targetNodeID,
|
||
fromPort: edge.sourcePortID,
|
||
toPort: edge.targetPortID,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 拍平树形json结构,将结构信息提取到map
|
||
*/
|
||
private flatJSON(json: Partial<WorkflowJSON> = { nodes: [], edges: [] }): {
|
||
flattenJSON: WorkflowJSON;
|
||
nodeBlocks: Map<string, string[]>;
|
||
nodeEdges: Map<string, string[]>;
|
||
} {
|
||
const nodeBlocks = new Map<string, string[]>();
|
||
const nodeEdges = new Map<string, string[]>();
|
||
const rootNodes = json.nodes ?? [];
|
||
const rootEdges = json.edges ?? [];
|
||
const flattenNodeJSONs: WorkflowNodeJSON[] = [...rootNodes];
|
||
const flattenEdgeJSONs: WorkflowEdgeJSON[] = [...rootEdges];
|
||
|
||
const rootBlockIDs: string[] = rootNodes.map(node => node.id);
|
||
const rootEdgeIDs: string[] = rootEdges.map(edge => this.getEdgeID(edge));
|
||
|
||
nodeBlocks.set(FlowNodeBaseType.ROOT, rootBlockIDs);
|
||
nodeEdges.set(FlowNodeBaseType.ROOT, rootEdgeIDs);
|
||
|
||
// 如需支持多层结构,以下部分改为递归
|
||
rootNodes.forEach(nodeJSON => {
|
||
const { blocks, edges } = nodeJSON;
|
||
if (blocks) {
|
||
flattenNodeJSONs.push(...blocks);
|
||
const blockIDs: string[] = [];
|
||
blocks.forEach(block => {
|
||
blockIDs.push(block.id);
|
||
});
|
||
nodeBlocks.set(nodeJSON.id, blockIDs);
|
||
delete nodeJSON.blocks;
|
||
}
|
||
if (edges) {
|
||
flattenEdgeJSONs.push(...edges);
|
||
const edgeIDs: string[] = [];
|
||
edges.forEach(edge => {
|
||
const edgeID = this.getEdgeID(edge);
|
||
edgeIDs.push(edgeID);
|
||
});
|
||
nodeEdges.set(nodeJSON.id, edgeIDs);
|
||
delete nodeJSON.edges;
|
||
}
|
||
});
|
||
|
||
const flattenJSON: WorkflowJSON = {
|
||
nodes: flattenNodeJSONs,
|
||
edges: flattenEdgeJSONs,
|
||
};
|
||
|
||
return {
|
||
flattenJSON,
|
||
nodeBlocks,
|
||
nodeEdges,
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 对JSON进行分层
|
||
*/
|
||
private nestJSON(
|
||
flattenJSON: WorkflowJSON,
|
||
nodeBlocks: Map<string, string[]>,
|
||
nodeEdges: Map<string, string[]>,
|
||
): WorkflowJSON {
|
||
const nestJSON: WorkflowJSON = {
|
||
nodes: [],
|
||
edges: [],
|
||
};
|
||
const nodeMap = new Map<string, WorkflowNodeJSON>();
|
||
const edgeMap = new Map<string, WorkflowEdgeJSON>();
|
||
const rootBlockSet = new Set<string>(
|
||
nodeBlocks.get(FlowNodeBaseType.ROOT) ?? [],
|
||
);
|
||
const rootEdgeSet = new Set<string>(
|
||
nodeEdges.get(FlowNodeBaseType.ROOT) ?? [],
|
||
);
|
||
|
||
// 构造缓存
|
||
flattenJSON.nodes.forEach(nodeJSON => {
|
||
nodeMap.set(nodeJSON.id, nodeJSON);
|
||
});
|
||
|
||
flattenJSON.edges.forEach(edgeJSON => {
|
||
const edgeID = this.getEdgeID(edgeJSON);
|
||
edgeMap.set(edgeID, edgeJSON);
|
||
});
|
||
|
||
// 恢复层级数据
|
||
flattenJSON.nodes.forEach(nodeJSON => {
|
||
if (rootBlockSet.has(nodeJSON.id)) {
|
||
nestJSON.nodes.push(nodeJSON);
|
||
}
|
||
// 恢复blocks
|
||
if (nodeBlocks.has(nodeJSON.id)) {
|
||
const blockIDs = nodeBlocks.get(nodeJSON.id)!;
|
||
const blockJSONs: WorkflowNodeJSON[] = blockIDs
|
||
.map(blockID => nodeMap.get(blockID)!)
|
||
.filter(Boolean);
|
||
nodeJSON.blocks = blockJSONs;
|
||
}
|
||
// 恢复edges
|
||
if (nodeEdges.has(nodeJSON.id)) {
|
||
const edgeIDs = nodeEdges.get(nodeJSON.id)!;
|
||
const edgeJSONs: WorkflowEdgeJSON[] = edgeIDs
|
||
.map(edgeID => edgeMap.get(edgeID)!)
|
||
.filter(Boolean);
|
||
nodeJSON.edges = edgeJSONs;
|
||
}
|
||
});
|
||
|
||
flattenJSON.edges.forEach(edgeJSON => {
|
||
const edgeID = this.getEdgeID(edgeJSON);
|
||
if (rootEdgeSet.has(edgeID)) {
|
||
nestJSON.edges.push(edgeJSON);
|
||
}
|
||
});
|
||
|
||
return nestJSON;
|
||
}
|
||
}
|