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 робочого процесу.