logologo
Начало
Руководство
Разработка
Плагины
API
English
简体中文
日本語
한국어
Deutsch
Français
Español
Português
Русский
Italiano
Türkçe
Українська
Tiếng Việt
Bahasa Indonesia
ไทย
Polski
Nederlands
Čeština
العربية
עברית
हिन्दी
Svenska
Начало
Руководство
Разработка
Плагины
API
logologo
Рабочие процессы
Обзор
Быстрый старт

Триггер

Обзор
Событие таблицы данных
Задача по расписанию
Событие перед действием
Событие после действия
Пользовательское событие действия
Согласование
Webhook

Узел

Обзор

Искусственный интеллект (AI)

Большая языковая модель (LLM)

Управление потоком

Условие
Ветвление по условиям
Цикл
Переменная
Параллельное ветвление
Вызов рабочего процесса
Вывод потока
Сопоставление переменных JSON
Задержка
Завершение

Вычисление

Вычисление
Вычисление даты
Вычисление JSON

Операции с данными

Добавление данных
Обновление данных
Запрос данных
Удаление данных
Операция SQL

Ручная обработка

Ручная обработка
Согласование
Копия (CC)

Расширенные типы

HTTP-запрос
Скрипт JavaScript
Уведомление
Отправка email
Ответ
Сообщение ответа
Переменная
Журнал выполнения
Управление версиями
Расширенные параметры

Разработка расширений

Обзор
Расширенные типы триггеров
Расширенные типы узлов
Справочник API
Previous PageРасширенные типы триггеров
Next PageСправочник API
Уведомление о переводе ИИ

Эта документация была автоматически переведена ИИ.

#Расширение типов узлов

Тип узла, по сути, является операционной инструкцией. Различные инструкции представляют собой разные операции, выполняемые в рабочем процессе.

Аналогично триггерам, расширение типов узлов также делится на две части: серверную и клиентскую. Серверная часть должна реализовать логику для зарегистрированной инструкции, а клиентская — предоставить конфигурацию интерфейса для параметров узла, где находится эта инструкция.

#Серверная часть

#Простейшая инструкция узла

Основное содержимое инструкции — это функция, то есть метод run в классе инструкции должен быть реализован для выполнения логики инструкции. В этой функции можно выполнять любые необходимые операции, например, операции с базой данных, файловые операции, вызовы сторонних API и так далее.

Все инструкции должны быть унаследованы от базового класса Instruction. Простейшая инструкция требует лишь реализации функции run:

import { Instruction, JOB_STATUS } from '@nocobase/plugin-workflow';

export class MyInstruction extends Instruction {
  run(node, input, processor) {
    console.log('my instruction runs!');
    return {
      status: JOB_STATUS.RESOVLED,
    };
  }
}

И зарегистрируйте эту инструкцию в плагине рабочего процесса:

export default class MyPlugin extends Plugin {
  load() {
    // get workflow plugin instance
    const workflowPlugin = this.app.getPlugin<WorkflowPlugin>(WorkflowPlugin);

    // register instruction
    workflowPlugin.registerInstruction('my-instruction', MyInstruction);
  }
}

Значение статуса (status) в возвращаемом объекте инструкции является обязательным и должно быть одним из значений константы JOB_STATUS. Это значение определяет дальнейшее направление обработки данного узла в рабочем процессе. Обычно используется JOB_STATUS.RESOVLED, что означает успешное выполнение узла и продолжение выполнения последующих узлов. Если необходимо заранее сохранить результирующее значение, вы также можете вызвать метод processor.saveJob и вернуть его возвращаемый объект. Исполнитель сгенерирует запись о результате выполнения на основе этого объекта.

#Результирующее значение узла

Если имеется конкретный результат выполнения, особенно данные, подготовленные для использования последующими узлами, его можно вернуть через свойство result и сохранить в объекте задачи узла:

import { Instruction, JOB_STATUS } from '@nocobase/plugin-workflow';

export class RandomStringInstruction extends Instruction {
  run(node, input, processor) {
    // customized config from node
    const { digit = 1 } = node.config;
    const result = `${Math.round(10 ** digit * Math.random())}`.padStart(
      digit,
      '0',
    );
    return {
      status: JOB_STATUS.RESOVLED,
      result,
    };
  },
};

Здесь node.config — это элемент конфигурации узла, который может быть любым необходимым значением. Он будет сохранен как поле типа JSON в соответствующей записи узла в базе данных.

#Обработка ошибок инструкции

Если в процессе выполнения могут возникнуть исключения, вы можете перехватить их заранее и вернуть статус ошибки:

import { JOB_STATUS } from '@nocobase/plugin-workflow';

export const errorInstruction = {
  run(node, input, processor) {
    try {
      throw new Error('exception');
    } catch (error) {
      return {
        status: JOB_STATUS.ERROR,
        result: error,
      };
    }
  },
};

Если предсказуемые исключения не будут перехвачены, движок рабочего процесса автоматически перехватит их и вернет статус ошибки, чтобы предотвратить сбой программы из-за необработанных исключений.

#Асинхронные узлы

Когда требуется управление потоком или асинхронные (трудоемкие) операции ввода-вывода, метод run может вернуть объект со статусом JOB_STATUS.PENDING, предписывая исполнителю ожидать (приостановить выполнение) до завершения некоторой внешней асинхронной операции, а затем уведомить движок рабочего процесса о продолжении выполнения. Если функция run возвращает значение статуса "ожидание", то данная инструкция должна реализовать метод resume, иначе выполнение рабочего процесса не может быть возобновлено:

import { Instruction, JOB_STATUS } from '@nocobase/plugin-workflow';

export class PayInstruction extends Instruction {
  async run(node, input, processor) {
    // job could be create first via processor
    const job = await processor.saveJob({
      status: JOB_STATUS.PENDING,
    });

    const { workflow } = processor;
    // do payment asynchronously
    paymentService.pay(node.config, (result) => {
      // notify processor to resume the job
      return workflow.resume(job.id, result);
    });

    // return created job instance
    return job;
  }

  resume(node, job, processor) {
    // check payment status
    job.set('status', job.result.status === 'ok' ? JOB_STATUS.RESOVLED : JOB_STATUS.REJECTED);
    return job;
  },
};

Здесь paymentService обозначает некий платежный сервис. В колбэке сервиса запускается возобновление выполнения соответствующей задачи рабочего процесса, а текущий процесс сначала завершается. Затем движок рабочего процесса создает новый процессор и передает его методу resume узла, чтобы продолжить выполнение ранее приостановленного узла.

Примечание

Здесь "асинхронная операция" относится не к async функциям в JavaScript, а к операциям, которые не возвращают результат немедленно при взаимодействии с другими внешними системами, например, платежный сервис, которому требуется дождаться другого уведомления, чтобы узнать результат.

#Статус результата узла

Статус выполнения узла влияет на успех или неудачу всего рабочего процесса. Обычно, при отсутствии ветвлений, сбой одного узла напрямую приводит к сбою всего рабочего процесса. Наиболее распространенный сценарий: если узел успешно выполнен, он переходит к следующему узлу в таблице узлов, пока не останется последующих узлов, после чего весь рабочий процесс завершается со статусом успеха.

Если в процессе выполнения какой-либо узел возвращает статус сбоя, движок будет обрабатывать это по-разному в зависимости от следующих двух ситуаций:

  1. Узел, возвращающий статус сбоя, находится в основном рабочем процессе, то есть не находится ни в одном из ветвящихся процессов, запущенных вышестоящими узлами. В этом случае весь основной рабочий процесс будет считаться неудачным, и процесс завершится.

  2. Узел, возвращающий статус сбоя, находится внутри ветвящегося процесса. В этом случае ответственность за определение следующего состояния процесса передается узлу, который открыл ветвь. Внутренняя логика этого узла определяет состояние последующего процесса, и это решение рекурсивно распространяется вверх до основного рабочего процесса.

В конечном итоге следующее состояние всего рабочего процесса определяется на узлах основного рабочего процесса. Если узел в основном рабочем процессе возвращает сбой, то весь рабочий процесс завершается со статусом сбоя.

Если какой-либо узел после выполнения возвращает статус "ожидание", то весь процесс выполнения будет временно прерван и приостановлен, ожидая события, определенного соответствующим узлом, для возобновления выполнения процесса. Например, узел "Ручной ввод": при достижении этого узла процесс приостанавливается со статусом "ожидание", ожидая ручного вмешательства для принятия решения о продолжении. Если введенный вручную статус — "одобрено", то выполнение продолжается по последующим узлам процесса; в противном случае обрабатывается согласно предыдущей логике сбоя.

Дополнительные статусы возврата инструкций можно найти в разделе "Справочник по API рабочих процессов".

#Досрочный выход

В некоторых особых рабочих процессах может потребоваться завершить процесс непосредственно в определенном узле. Вы можете вернуть null, чтобы указать на выход из текущего процесса, и последующие узлы не будут выполняться.

Такая ситуация часто встречается в узлах управления потоком, например, в узле параллельного ветвления (ссылка на код), где процесс текущего узла завершается, но для каждой дочерней ветви запускаются новые процессы, которые продолжают выполнение.

:::warn{title=Предупреждение} Планирование ветвящихся рабочих процессов с помощью расширенных узлов имеет определенную сложность и требует тщательной обработки и всестороннего тестирования. :::

#Узнать больше

Определения различных параметров для типов узлов см. в разделе "Справочник по API рабочих процессов".

#Клиентская часть

Аналогично триггерам, форма конфигурации для инструкции (типа узла) должна быть реализована на клиентской части.

#Простейшая инструкция узла

Все инструкции должны быть унаследованы от базового класса Instruction. Связанные свойства и методы используются для настройки и использования узла.

Например, если нам нужно предоставить интерфейс конфигурации для узла типа "случайная числовая строка" (randomString), определенного выше на серверной части, который имеет параметр digit, представляющий количество цифр в случайном числе, мы будем использовать поле ввода числа в форме конфигурации для получения пользовательского ввода.

import WorkflowPlugin, { Instruction, VariableOption } from '@nocobase/workflow/client';

class MyInstruction extends Instruction {
  title = 'Random number string';
  type = 'randomString';
  group = 'extended';
  fieldset = {
    'digit': {
      type: 'number',
      title: 'Digit',
      name: 'digit',
      'x-decorator': 'FormItem',
      'x-component': 'InputNumber',
      'x-component-props': {
        min: 1,
        max: 10,
      },
      default: 6,
    },
  };
  useVariables(node, options): VariableOption {
    return {
      value: node.key,
      label: node.title,
    };
  }
}

export default class MyPlugin extends Plugin {
  load() {
    // get workflow plugin instance
    const workflowPlugin = this.app.getPlugin<WorkflowPlugin>(WorkflowPlugin);

    // register instruction
    workflowPlugin.registerInstruction('randomString', MyInstruction);
  }
}
Примечание

Идентификатор типа узла, зарегистрированный на клиентской части, должен совпадать с идентификатором на серверной части, иначе это приведет к ошибкам.

#Предоставление результатов узла в качестве переменных

Вы могли заметить метод useVariables в приведенном выше примере. Если вам нужно использовать результат узла (часть result) в качестве переменной для последующих узлов, вам необходимо реализовать этот метод в унаследованном классе инструкции и вернуть объект, соответствующий типу VariableOption. Этот объект служит структурным описанием результата выполнения узла, предоставляя сопоставление имен переменных для выбора и использования в последующих узлах.

Тип VariableOption определяется следующим образом:

export type VariableOption = {
  value?: string;
  label?: string;
  children?: VariableOption[] | null;
  [key: string]: any;
};

Ключевым является свойство value, которое представляет собой сегментированное значение пути имени переменной. label используется для отображения в интерфейсе, а children — для представления многоуровневой структуры переменной, что применяется, когда результат узла является глубоко вложенным объектом.

Используемая переменная внутри системы представляется в виде строкового шаблона пути, разделенного точками, например, {{jobsMapByNodeKey.2dw92cdf.abc}}. Здесь jobsMapByNodeKey представляет собой набор результатов всех узлов (внутренне определен, не требует обработки), 2dw92cdf — это key узла, а abc — это пользовательское свойство в объекте результата узла.

Кроме того, поскольку результат узла также может быть простым значением, при предоставлении переменных узла первый уровень обязательно должен быть описанием самого узла:

{
  value: node.key,
  label: node.title,
}

То есть, первый уровень — это key и заголовок узла. Например, в ссылке на код для узла вычисления, при использовании результата этого узла, параметры интерфейса будут следующими:

运算节点的结果

Когда результат узла представляет собой сложный объект, вы можете использовать children для дальнейшего описания вложенных свойств. Например, пользовательская инструкция может возвращать следующие данные JSON:

{
  "message": "ok",
  "data": {
    "id": 1,
    "name": "test",
  }
}

Тогда его можно вернуть с помощью метода useVariables следующим образом:

useVariables(node, options): VariableOption {
  return {
    value: node.key,
    label: node.title,
    children: [
      {
        value: 'message',
        label: 'Message',
      },
      {
        value: 'data',
        label: 'Data',
        children: [
          {
            value: 'id',
            label: 'ID',
          },
          {
            value: 'name',
            label: 'Name',
          },
        ],
      },
    ],
  };
}

Таким образом, в последующих узлах вы сможете использовать следующий интерфейс для выбора переменных:

映射后的结果变量

Примечание

Когда структура в результате представляет собой массив глубоко вложенных объектов, вы также можете использовать children для описания пути, но он не может содержать индексы массива. Это связано с тем, что при обработке переменных в рабочем процессе NocoBase описание пути переменной для массива объектов автоматически преобразуется в плоский массив глубоких значений при использовании, и вы не можете получить доступ к конкретному значению по его индексу.

#Доступность узла

По умолчанию в рабочий процесс можно добавлять любые узлы. Однако в некоторых случаях узел может быть неприменим в определенных типах рабочих процессов или ветвей. В таких ситуациях вы можете настроить доступность узла с помощью isAvailable:

// 类型定义
export abstract class Instruction {
  isAvailable?(ctx: NodeAvailableContext): boolean;
}

export type NodeAvailableContext = {
  // 工作流插件实例
  engine: WorkflowPlugin;
  // 工作流实例
  workflow: object;
  // 上游节点
  upstream: object;
  // 是否是分支节点(分支编号)
  branchIndex: number;
};

Метод isAvailable возвращает true, если узел доступен, и false, если он недоступен. Параметр ctx содержит контекстную информацию текущего узла, которую можно использовать для определения его доступности.

При отсутствии особых требований реализовывать метод isAvailable не нужно, так как узлы по умолчанию доступны. Наиболее распространенный сценарий, требующий настройки, — это когда узел может быть ресурсоемкой операцией и не подходит для выполнения в синхронном рабочем процессе. Вы можете использовать метод isAvailable для ограничения его использования. Например:

isAvailable({ engine, workflow, upstream, branchIndex }) {
  return !engine.isWorkflowSync(workflow);
}

#Узнать больше

Определения различных параметров для типов узлов см. в разделе "Справочник по API рабочих процессов".