feat(web): 增加分支节点,增加流程检查的测试
This commit is contained in:
113
service-web/client/src/pages/ai/flow/FlowChecker.tsx
Normal file
113
service-web/client/src/pages/ai/flow/FlowChecker.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
import {find, findIdx, isEqual, lpad, toStr} from 'licia'
|
||||
import {type Connection, type Edge, getOutgoers, type Node} from '@xyflow/react'
|
||||
|
||||
export class CheckError extends Error {
|
||||
readonly id: string
|
||||
|
||||
constructor(
|
||||
id: number,
|
||||
message: string,
|
||||
) {
|
||||
super(message)
|
||||
this.id = `E${lpad(toStr(id), 6, '0')}`
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
return `${this.id}: ${this.message}`
|
||||
}
|
||||
}
|
||||
|
||||
export const multiStartNodeError = () => new CheckError(100, '只能存在1个开始节点')
|
||||
export const multiEndNodeError = () => new CheckError(101, '只能存在1个结束节点')
|
||||
|
||||
const getNodeById = (id: string, nodes: Node[]) => find(nodes, (n: Node) => isEqual(n.id, id))
|
||||
|
||||
// @ts-ignore
|
||||
export const checkAddNode: (type: string, nodes: Node[], edges: Edge[]) => void = (type, nodes, edges) => {
|
||||
if (isEqual(type, 'start-node') && findIdx(nodes, (node: Node) => isEqual(type, node.type)) > -1) {
|
||||
throw multiStartNodeError()
|
||||
}
|
||||
if (isEqual(type, 'end-node') && findIdx(nodes, (node: Node) => isEqual(type, node.type)) > -1) {
|
||||
throw multiEndNodeError()
|
||||
}
|
||||
}
|
||||
|
||||
export const sourceNodeNotFoundError = () => new CheckError(200, '连线起始节点未找到')
|
||||
export const targetNodeNotFoundError = () => new CheckError(201, '连线目标节点未找到')
|
||||
export const startNodeToEndNodeError = () => new CheckError(202, '开始节点不能直连结束节点')
|
||||
export const nodeToSelfError = () => new CheckError(203, '节点不能直连自身')
|
||||
export const hasCycleError = () => new CheckError(204, '禁止流程循环')
|
||||
export const nodeNotOnlyToEndNode = () => new CheckError(206, '直连结束节点的节点不允许连接其他节点')
|
||||
export const hasRedundantEdgeError = () => new CheckError(207, '禁止出现冗余边')
|
||||
|
||||
export const checkAddConnection: (connection: Connection, nodes: Node[], edges: Edge[]) => void = (connection, nodes, edges) => {
|
||||
let sourceNode = getNodeById(connection.source, nodes)
|
||||
if (!sourceNode) {
|
||||
throw sourceNodeNotFoundError()
|
||||
}
|
||||
let targetNode = getNodeById(connection.target, nodes)
|
||||
if (!targetNode) {
|
||||
throw targetNodeNotFoundError()
|
||||
}
|
||||
// 禁止短路整个流程
|
||||
if (isEqual('start-node', sourceNode.type) && isEqual('end-node', targetNode.type)) {
|
||||
throw startNodeToEndNodeError()
|
||||
}
|
||||
|
||||
// 禁止流程出现环,必须是有向无环图
|
||||
const hasCycle = (node: Node, visited = new Set<string>()) => {
|
||||
if (visited.has(node.id)) return false
|
||||
visited.add(node.id)
|
||||
for (const outgoer of getOutgoers(node, nodes, edges)) {
|
||||
if (isEqual(outgoer.id, sourceNode?.id)) return true
|
||||
if (hasCycle(outgoer, visited)) return true
|
||||
}
|
||||
}
|
||||
if (isEqual(sourceNode.id, targetNode.id)) {
|
||||
throw nodeToSelfError()
|
||||
} else if (hasCycle(targetNode)) {
|
||||
throw hasCycleError()
|
||||
}
|
||||
|
||||
let outgoers = [targetNode, ...getOutgoers(sourceNode, nodes, edges)]
|
||||
if (outgoers.length > 1 && findIdx(outgoers, (node: Node) => isEqual(node.type, 'end-node')) > -1) {
|
||||
throw nodeNotOnlyToEndNode()
|
||||
}
|
||||
|
||||
/*const hasRedundant = (source: Node, target: Node) => {
|
||||
const visited = new Set<string>()
|
||||
const queue = new Queue<Node>()
|
||||
queue.enqueue(source)
|
||||
visited.add(source.id)
|
||||
while (queue.size > 0) {
|
||||
const current = queue.dequeue()!
|
||||
console.log(current.id)
|
||||
for (const incomer of getIncomers(current, nodes, edges)) {
|
||||
if (isEqual(incomer.id, target.id)) {
|
||||
return true
|
||||
}
|
||||
if (!visited.has(incomer.id)) {
|
||||
visited.add(incomer.id)
|
||||
queue.enqueue(incomer)
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
if (hasRedundant(sourceNode, targetNode)) {
|
||||
throw new Error('出现冗余边')
|
||||
}*/
|
||||
}
|
||||
|
||||
export const atLeastOneStartNodeError = () => new CheckError(300, '至少存在1个开始节点')
|
||||
export const atLeastOneEndNodeError = () => new CheckError(301, '至少存在1个结束节点')
|
||||
|
||||
// @ts-ignore
|
||||
export const checkSave: (nodes: Node[], edges: Edge[], data: any) => void = (nodes, edges, data) => {
|
||||
if (nodes.filter(n => isEqual('start-node', n.type)).length < 1) {
|
||||
throw atLeastOneStartNodeError()
|
||||
}
|
||||
if (nodes.filter(n => isEqual('end-node', n.type)).length < 1) {
|
||||
throw atLeastOneEndNodeError()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user