SpringBootとActiviti7によるワークフロー実装ガイド

SpringBootとActiviti7の連携

<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-spring-boot-starter</artifactId>
    <version>7.1.0.M4</version>
</dependency>
<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-engine</artifactId>
    <version>7.1.0.M4</version>
</dependency>

アプリケーション起動後にデータベーステーブルが生成されない場合、JDBC接続URLに以下の設定を追加してください:nullCatalogMeansCurrent=true

MySQL Connector 8.x以上を使用する場合、nullCatalogMeansCurrent=trueパラメータの追加が必要です。このパラメータがないと、MyBatisがテーブルに対応するXMLなどを生成する際に、特定のデータベースだけでなくサーバー内の全データベースをスキャンしてしまいます。これにより、Activitiは現在のデータベースに存在しないテーブルを他のデータベースで見つけてしまったと誤認識することがあります。

API呼び出し時にフィールド不足エラーが発生する場合、Activiti自身の問題により以下のSQLを実行する必要があります。

ALTER TABLE ACT_RE_DEPLOYMENT ADD COLUMN PROJECT_RELEASE_VERSION_ VARCHAR(255) DEFAULT NULL;
ALTER TABLE ACT_RE_DEPLOYMENT ADD COLUMN VERSION_ VARCHAR(255) DEFAULT NULL;

プロセス関連コンポーネント

プロセスデプロイメント:act_ge_bytearray、act_re_deploymentテーブルに格納
プロセス定義:act_re_procdefテーブルに格納
実行中プロセスインスタンス:act_ru_executionテーブルに格納

タスク関連コンポーネント

タスク:act_ru_taskテーブルに格納
実行中参加者ノード:act_ru_identitylinkテーブルに格納
プロセス変数:act_ru_variableテーブルに格納

履歴関連コンポーネント

履歴記録:act_hi_*テーブル群に格納

プロセスゲートウェイ

排他ゲートウェイ (x) — 一つのパスのみが選択されます。
・プロセスがこのゲートウェイに到達すると、出力フローの順に各条件を評価し、最初にtrueとなった条件のフローを実行します。複数の条件がtrueの場合、最初にtrueとなったフローのみが実行されます。全ての条件がfalseの場合、エンジンは例外をスローします。
・排他ゲートウェイは条件付きシーケンスフローと組み合わせて使用し、default属性でデフォルトのシーケンスフローを指定できます。全ての条件が満たされない場合、デフォルトのシーケンスフローが実行されます。

並列ゲートウェイ (+) — 全てのパスが同時に選択されます。
・分岐 — 全ての出力シーケンスフローを並行して実行し、各シーケンスフローに対して並列実行ラインを作成します。
・結合 — 並列ゲートウェイから分岐した全ての実行ラインが完了するまで待機し、全てのラインが完了した後にのみ次の処理に進みます。

包括ゲートウェイ (+) — 複数のラインを同時に実行でき、ゲートウェイ上に条件を設定することも可能です。
・分岐 — 各ラインの式を評価し、式の評価結果がtrueの場合、並列ラインを作成して実行を継続します。
・結合 — 包括ゲートウェイから分岐した全ての実行ラインが完了するまで待機し、全てのラインが完了した後にのみ次の処理に進みます。

イベントゲートウェイ (+) — 中間キャッチイベント専用に設計されており、複数の出力フローを異なる中間キャッチイベントに設定できます。プロセスがイベントゲートウェイに到達すると、プロセスは待機状態になり、イベントが発生するのを待って待機状態からアクティブ状態に移行します。

新機能API

// Spring Securityログインと対応するロールが必要
@PreAuthorize("hasRole('ACTIVITI_USER')")
@PreAuthorize("hasRole('ACTIVITI_ADMIN')")
ProcessRuntime processRuntime;
ProcessAdminRuntime processAdminRuntime;

SpringSecurityの無効化

@SpringBootApplication(
        exclude = {
                org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class,
                org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration.class
        }
)
=====
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * Activiti7で未処理タスクを取得する際、複数の候補者に対するクエリにこのUserDetailsServiceが必要です
 * 実行者の定義にはuserIdが使用されます
 */
@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        List<GrantedAuthority> authorities = new ArrayList<>();
        // プロセスロールを追加
        return new User(username, "", authorities);
    }
}

例外処理

@Slf4j
@ControllerAdvice
public class WorkflowExceptionHandler {

    @ExceptionHandler({ActivitiException.class})
    @ResponseBody
    public ResponseEntity<?> handleActivitiException(HttpServletRequest request, ActivitiException e) {
        log.error("リクエストパス: {}, ワークフロー例外: {}", request.getRequestURI(), e.getMessage());
        String errorMessage = e.getMessage().replace("Exception while invoking TaskListener: Exception while invoking TaskListener: ", "");
        return ResponseEntity.badRequest().body(errorMessage);
    }
}

リスナー

実行リスナー(ExecutionListener):プロセスインスタンス関連のデータを取得できます。例:プロセス変数の読み取り・保存、業務情報の処理、プロセス処理時間の記録

タスクリスナー(TaskListener):タスク関連のデータを取得できます。例:実行者の指定、タスク処理時間の記録、通知の送信

フィールドインジェクション

// 設定と同じ名前のフィールドをインジェクト
private Expression taskStatus;
log.info("インジェクトされたフィールド値: {}", taskStatus.getValue(execution));

タイマーイベント

act_ru_timer_jobテーブルに関連します

・指定日時にプロセスインスタンスを開始(M4バージョンではエラーが発生する可能性があるため、中間イベントの使用を推奨)
・未処理タスクのタイムアウト通知(非中断タイマー:新規タスクを生成)
・タイムアウトタスクの処理者変更(例:3日間承認されない場合に自動完了または他の担当者に転送)(中断タイマー:タスクをスキップして転送)

タイマーイベントの種類

Time date:特定の日時にトリガー(例:2021-08-10T10:23:30)
Time duration:指定時間経過後にトリガー
Time cycle:繰り返しトリガー

シグナルイベント

スローイベント:シグナルを発行します
キャッチイベント:シグナルをキャッチして後続プロセスを開始します

複数プロセスの調整によく使用されます。プロセス1がシグナルを発行し、プロセス2がそのシグナルをキャッチして開始します。

メッセージイベント

act_ru_event_subscrテーブルに関連します

メッセージイベントはポイントツーポイント通信です。
シグナルイベントはブロードキャスト通信です。

例:タスクの取り消し処理

注意:タスクリスナーを使用して取り消し可能なノード名とメッセージ名を記録する必要があります。

エラーイベント

例:通常、サービスタスクで例外が発生した場合の処理に使用されます

// コードによるトリガー
throw new BpmnError("ErrorCode_1x7y9z");

エラー開始イベントはイベントサブプロセス内でのみ定義できます。

補償イベント

M4バージョンでは現在使用できません

補償イベントの特徴:タスク到達時にアクティブ化;プロセスインスタンス終了時に中断;サブプロセスは補償イベントにタスクが到達して初めてアクティブ化

キャンセルイベント

M4バージョンでは現在使用できません

その他のタスクタイプ

手動タスク:何も行わない空のノード。システム外で処理が必要だが、システム上でプロセスとして表現する必要がある場合に使用します。
サービスタスク:自動実行タスクで、実行クラスが必要です。org.activiti.engine.delegate.JavaDelegateを実装します。例:SMS通知
スクリプトタスク:スクリプトを実行するタスク
業務ルールタスク:ビジネスルールエンジンを実行するタスク
受信タスク:外部からのシグナルを待機するタスク
メールタスク:バージョン7では軽量化のため削除されました。サービスタスクでの代替実装を推奨します。

サブプロセス

埋め込みサブプロセス:メインプロセス内に定義されるサブプロセス
呼び出しサブプロセス:既にデプロイされている別のプロセスを呼び出すサブプロセス

マルチインスタンス

シリアルマルチインスタンス:例:動的な順次承認。M4バージョンでは使用不可、バージョン6では使用可能
パラレルマルチインスタンス:例:会議承認、複数サブプロセス業務

マルチインスタンスタスクの完了条件:

総インスタンス数:nrOfInstances
未完了インスタンス数:nrOfActiveInstances
完了インスタンス数:nrOfCompletedInstances

完了条件設定:${nrOfCompletedInstances/nrOfInstances > 0.5}

タグ: Activiti7 SpringBoot ワークフロー BPMN プロセス管理

7月20日 16:24 投稿