296 lines
8.2 KiB
TypeScript
296 lines
8.2 KiB
TypeScript
import {defHttp} from "@/utils/http/axios";
|
|
import {useMessage} from '/@/hooks/web/useMessage';
|
|
import dayjs from "dayjs";
|
|
|
|
enum Api {
|
|
// 运行流程(需要保存)
|
|
run = '/airag/flow/run',
|
|
// 调试流程(不需要保存)
|
|
debug = '/airag/flow/debug',
|
|
// invoke = '/airag/flow/invoke',
|
|
// 获取流程列表
|
|
list = '/airag/flow/list',
|
|
// 添加流程
|
|
add = '/airag/flow/add',
|
|
// 编辑流程(不包含design数据)
|
|
edit = '/airag/flow/edit',
|
|
// 仅保存流程设计
|
|
designSave = '/airag/flow/design/save',
|
|
// 通过id删除流程
|
|
deleteById = '/airag/flow/delete',
|
|
// 批量删除流程
|
|
deleteBatch = '/airag/flow/deleteBatch',
|
|
|
|
// 获取子流程列表(包括入参)
|
|
subflowList = '/airag/flow/subflowList',
|
|
// 根据ID获取单个子流程
|
|
querySubflowById = '/airag/flow/querySubflowById',
|
|
}
|
|
|
|
const {createMessage: $message} = useMessage();
|
|
|
|
// 运行流程超时时间(5分钟)
|
|
const runTimeout = 5 * 60 * 1000;
|
|
|
|
/**
|
|
* 阻塞式运行流程
|
|
* @param flowId
|
|
* @param inputParams
|
|
*/
|
|
export async function blockRun(flowId: string, inputParams: Recordable) {
|
|
return defHttp.post({
|
|
url: Api.run,
|
|
params: {
|
|
flowId: flowId,
|
|
inputParams: inputParams,
|
|
responseMode: 'blocking'
|
|
},
|
|
timeout: runTimeout,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 流式运行流程
|
|
// * @param flowId NY5LzSY2VW1BSthYSnJArCFqbgwtZqSuyPQ/OD1n1twWJGU2RN/wkzf+kBVO5DztN85Ca9keeuaRAiwcatr8N0M15+Wv2SmRw82lMwawE2naX5tpJMpkxrUhUUcjnC+BSBL4+PV2JUXBFW8/oOG8HLqYvmPxoP7MMBhMi9D7lRY=
|
|
* @param flowRecord
|
|
* @param inputParams
|
|
*/
|
|
// export function createStreamRun(flowId: string, inputParams: Recordable) {
|
|
export function createStreamRun(/*flowId: string, */ flowRecord: Recordable, inputParams: Recordable) {
|
|
type RunEventType = 'FLOW_STARTED' | 'NODE_STARTED' | 'NODE_FINISHED' | 'FLOW_FINISHED' | 'MESSAGE';
|
|
|
|
const logColor = (text: string, color: string) => [`%c${text}`, `color: ${color};`];
|
|
const lcInfo = () => logColor('[INFO]', '#2196F3');
|
|
const lcWarn = () => logColor('[WARN]', '#FFC107');
|
|
const lcError = () => logColor('[ERRO]', '#F44336');
|
|
|
|
const colorTextRun = logColor('[stream-run]', '#999999');
|
|
const debugStreamRun = (lcFn: Fn, ...args: any[]) => {
|
|
const colorTextType: string[] = lcFn();
|
|
const colorText = [
|
|
`${colorTextRun[0]} ${colorTextType[0]}`,
|
|
colorTextRun[1], colorTextType[1],
|
|
];
|
|
const dateText = dayjs().format('HH:mm:ss.SSS');
|
|
console.debug(...colorText, `[${dateText}]`, ...args);
|
|
};
|
|
|
|
// 记录解析失败的数据
|
|
let failChunkText = '';
|
|
|
|
// 发送请求
|
|
async function send() {
|
|
const readableStream = await defHttp.post({
|
|
url: Api.debug,
|
|
params: {
|
|
// flowId: flowId,
|
|
flow: flowRecord,
|
|
inputParams: inputParams,
|
|
responseMode: 'streaming',
|
|
},
|
|
adapter: 'fetch',
|
|
responseType: 'stream',
|
|
timeout: runTimeout,
|
|
}, {
|
|
isTransformResponse: false,
|
|
});
|
|
|
|
const reader = readableStream.getReader();
|
|
const decoder = new TextDecoder();
|
|
|
|
failChunkText = '';
|
|
|
|
while (true) {
|
|
const {done, value} = await reader.read();
|
|
if (done) {
|
|
break;
|
|
}
|
|
const chunkText = decoder.decode(value, {stream: true});
|
|
try {
|
|
debugStreamRun(lcInfo, `收到 chunkText:`, {chunkText});
|
|
handleChunkText(chunkText);
|
|
} catch (error) {
|
|
console.error('Error parsing update:', error);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 处理 chunkText
|
|
* @param chunkText
|
|
*/
|
|
function handleChunkText(chunkText: string) {
|
|
if (!chunkText) {
|
|
debugStreamRun(lcError, 'chunkText 为空:', {chunkText});
|
|
return;
|
|
}
|
|
// 如果包含解析失败的数据,则合并再解析
|
|
const hasFailChunkText = failChunkText.length > 0;
|
|
if (hasFailChunkText) {
|
|
chunkText = failChunkText + chunkText;
|
|
debugStreamRun(lcInfo, '合并解析失败的数据:', {chunkText});
|
|
failChunkText = '';
|
|
}
|
|
let hasFailChunk = false;
|
|
const chunks = chunkText.split('\n').flatMap((chunk: string) => {
|
|
chunk = chunk ? chunk.trim() : '';
|
|
if (!chunk) {
|
|
return []
|
|
}
|
|
if (chunk.startsWith('data:')) {
|
|
chunk = chunk.slice(5);
|
|
}
|
|
if (!chunk) {
|
|
debugStreamRun(lcError, 'chunk 为空:', {chunk, chunkText});
|
|
return [];
|
|
}
|
|
try {
|
|
return [
|
|
JSON.parse(chunk)
|
|
];
|
|
} catch (e) {
|
|
hasFailChunk = true;
|
|
debugStreamRun(lcError, 'chunk 解析失败:', {chunk, chunkText});
|
|
console.error(e);
|
|
}
|
|
return [];
|
|
});
|
|
if (hasFailChunk) {
|
|
// 解析失败是因为数据量太大,一次性传输的 chunk 不完整,需要合并再解析
|
|
// 记录解析失败的数据
|
|
failChunkText += chunkText;
|
|
}
|
|
chunks.forEach(handleChunkData);
|
|
}
|
|
|
|
const cbMap = new Map<RunEventType, Fn>();
|
|
const chunkHandled = new Set<string>();
|
|
|
|
function handleChunkData(data: {
|
|
data: Recordable,
|
|
event: RunEventType,
|
|
flowId?: string,
|
|
requestId: string,
|
|
success?: boolean,
|
|
message?: string,
|
|
}) {
|
|
//update-begin---author:wangshuai---date:2025-03-25---for:【QQYUN-11724】调试流程时,如果直接失败,调试界面会卡主---
|
|
if (data.success == false) {
|
|
let cb = cbMap.get("FLOW_FINISHED");
|
|
if (typeof cb === 'function') {
|
|
cb(data);
|
|
return;
|
|
}
|
|
}
|
|
//update-end---author:wangshuai---date:2025-03-25---for:【QQYUN-11724】调试流程时,如果直接失败,调试界面会卡主---
|
|
const key = `${data.event}-${data.data?.id || data.data?.fromNodeId || ''}`;
|
|
if (chunkHandled.has(key)) {
|
|
debugStreamRun(lcWarn, 'chunk 重复执行:', {key, data});
|
|
return;
|
|
}
|
|
chunkHandled.add(key);
|
|
const cb = cbMap.get(data.event);
|
|
if (typeof cb === 'function') {
|
|
debugStreamRun(lcInfo, ` ------ 处理 ${data.event} 事件:`, {key, data});
|
|
cb(data.data, data);
|
|
} else {
|
|
debugStreamRun(lcWarn, `${data.event} 事件对应的回调不存在:`, {key, data});
|
|
}
|
|
}
|
|
|
|
function setCB(type: RunEventType, cb: Fn) {
|
|
cbMap.set(type, cb);
|
|
}
|
|
|
|
return {
|
|
run: send,
|
|
onFlowStarted: (cb: Fn) => setCB('FLOW_STARTED', cb),
|
|
onFlowFinished: (cb: Fn) => setCB('FLOW_FINISHED', cb),
|
|
onNodeStarted: (cb: Fn) => setCB('NODE_STARTED', cb),
|
|
onNodeFinished: (cb: Fn) => setCB('NODE_FINISHED', cb),
|
|
onMessage: (cb: Fn) => setCB('MESSAGE', cb),
|
|
}
|
|
}
|
|
|
|
export async function getProcessList(params?: any) {
|
|
return defHttp.get({url: Api.list, params});
|
|
}
|
|
|
|
export async function addProcess(data: Recordable, opt?: Recordable) {
|
|
const silent = opt?.silent ?? false;
|
|
return defHttp.post({url: Api.add, params: data}, {
|
|
successMessageMode: silent ? 'none' : 'success',
|
|
});
|
|
}
|
|
|
|
export async function updateProcess(data: Recordable) {
|
|
return defHttp.put({url: Api.edit, params: data});
|
|
}
|
|
|
|
// 发布流程
|
|
export async function releaseProcess(flowId: string, un = false) {
|
|
const msg = un ? '取消发布' : '发布'
|
|
const res = await defHttp.put({
|
|
url: Api.edit, params: {
|
|
id: flowId,
|
|
status: un ? 'enable' : 'release',
|
|
}
|
|
}, {successMessageMode: 'none', isTransformResponse: false});
|
|
if (res.success) {
|
|
$message.success(`${msg}成功`)
|
|
return true;
|
|
}
|
|
$message.warn(res.message || `${msg}失败`)
|
|
return false;
|
|
}
|
|
|
|
export async function updateDesign(data: Recordable, opt?: Recordable) {
|
|
const silent = opt?.silent ?? false;
|
|
return defHttp.put({
|
|
url: Api.designSave, params: {
|
|
id: data.id,
|
|
name: data.name,
|
|
chain: data.chain,
|
|
design: data.design,
|
|
}
|
|
}, {
|
|
successMessageMode: silent ? 'none' : 'success',
|
|
});
|
|
}
|
|
|
|
export async function deleteProcess(id: string) {
|
|
return defHttp.delete({
|
|
url: Api.deleteById,
|
|
data: {id}
|
|
}, {
|
|
joinParamsToUrl: true
|
|
});
|
|
}
|
|
|
|
export async function deleteBatchProcess(idList: string[]) {
|
|
return defHttp.delete({
|
|
url: Api.deleteBatch,
|
|
data: {
|
|
ids: idList.join(',')
|
|
}
|
|
}, {
|
|
joinParamsToUrl: true
|
|
});
|
|
}
|
|
|
|
export async function querySubflowList(
|
|
params: Recordable,
|
|
) {
|
|
return defHttp.get({
|
|
url: Api.subflowList,
|
|
params: params,
|
|
});
|
|
}
|
|
|
|
export async function querySubflowById(subflowId: string) {
|
|
return defHttp.get({
|
|
url: Api.querySubflowById,
|
|
params: {subflowId}
|
|
});
|
|
}
|