天天看點

基于ProcessMaker\Nayra引擎的工作流開發之(二)核心方法

上一篇文章https://blog.csdn.net/u011115903/article/details/100763399講到我們面臨的核心問題就是如何調用引擎觸發啟動事件,任務流轉,結束事件,以及如何擷取要持久化的資料,接下來我們會在此篇文章内詳細講解這一大家最關心的問題。

按照上一篇實作方式的方案,我們需要ProcessMaker\Nayra引擎幫助我們完成兩個核心方法:啟動流程,任務流轉。

在我們要調用引擎的方法時,要準備好他要建立的執行個體,而建立執行個體則需要首先完成引擎指定的幾個Interface,這些Interface有各自不同的作用,我們可以根據自己的需求在實作Interface時的指定方法時自定義。下面我把要需要實作的inteface的代碼提供給大家,其中需要自定義的方法我會單獨列給大家。

實作ProcessMaker\Nayra\Contracts\Engine\EngineInterface

這是核心接口,它的ProcessMaker\Nayra\Engine\EngineTrait中給出了核心方法,如流轉方法 runToNextState,建立執行個體方法createExecutionInstance,從已有資料中加載執行個體方法loadExecutionInstance

<?php

namespace app\engine;

use ProcessMaker\Nayra\Contracts\Engine\EngineInterface;
use ProcessMaker\Nayra\Contracts\Engine\EventDefinitionBusInterface;
use ProcessMaker\Nayra\Contracts\Engine\JobManagerInterface;
use ProcessMaker\Nayra\Contracts\EventBusInterface;
use ProcessMaker\Nayra\Contracts\RepositoryInterface;
use ProcessMaker\Nayra\Engine\EngineTrait;

/**
 * Test implementation for EngineInterface.
 *
 * @package ProcessMaker\Bpmn
 */
class BpmnEngine implements EngineInterface {
    use EngineTrait;

    /**
     * @var RepositoryInterface
     */
    private $repository;

    /**
     * @var EventBusInterface $dispatcher
     */
    protected $dispatcher;

    /**
     * Test engine constructor.
     *
     * @param \ProcessMaker\Nayra\Contracts\RepositoryInterface $repository
     * @param \ProcessMaker\Nayra\Contracts\EventBusInterface $dispatcher
     * @param \ProcessMaker\Nayra\Contracts\Engine\JobManagerInterface|null $jobManager
     * @param \ProcessMaker\Nayra\Contracts\Engine\EventDefinitionBusInterface|null $eventDefinitionBus
     */
    public function __construct(RepositoryInterface $repository, EventBusInterface $dispatcher, JobManagerInterface $jobManager = null, EventDefinitionBusInterface $eventDefinitionBus = null)
    {
        $this->setRepository($repository);
        $this->setDispatcher($dispatcher);
        $this->setJobManager($jobManager);
        $eventDefinitionBus ? $this->setEventDefinitionBus($eventDefinitionBus) : null;
    }

    /**
     * @return EventBusInterface
     */
    public function getDispatcher()
    {
        return $this->dispatcher;
    }

    /**
     * @param EventBusInterface $dispatcher
     *
     * @return $this
     */
    public function setDispatcher(EventBusInterface $dispatcher)
    {
        $this->dispatcher = $dispatcher;
        return $this;
    }

    /**
     * @return FactoryInterface
     */
    public function getRepository()
    {
        return $this->repository;
    }

    /**
     * @param RepositoryInterface $repository
     *
     * @return $this
     */
    public function setRepository(RepositoryInterface $repository)
    {
        $this->repository = $repository;
        return $this;
    }
}
           

實作ProcessMaker\Nayra\Contracts\RepositoryInterface

這同樣是一個核心接口,它的ProcessMaker\Nayra\RepositoryTrait主要提供了如何建立流程對象的方法,是建立最後操作執行個體的必要參數。

<?php

namespace app\engine;

use ProcessMaker\Nayra\Contracts\Repositories\StorageInterface;
use ProcessMaker\Nayra\Contracts\RepositoryInterface;
use ProcessMaker\Nayra\RepositoryTrait;
use ProcessMaker\Test\Models\CallActivity;
use ProcessMaker\Test\Models\ExecutionInstance;
use app\engine\FormalExpressionEngine;
use ProcessMaker\Test\Models\TestOneClassWithEmptyConstructor;
use ProcessMaker\Test\Models\TestTwoClassWithArgumentsConstructor;
use app\engine\ExecutionInstanceRepositoryEngine;

/**
 * Repository
 *
 * @package ProcessMaker\Test\Models
 */
class RepositoryEngine implements RepositoryInterface {

    use RepositoryTrait;

    /**
     * Create instance of FormalExpression.
     *
     * @return \ProcessMaker\Nayra\Contracts\Bpmn\FormalExpressionInterface
     */
    public function createFormalExpression()
    {
        return new FormalExpressionEngine();
    }

    /**
     * Create instance of CallActivity.
     *
     * @return \ProcessMaker\Nayra\Contracts\Bpmn\CallActivityInterface
     */
    public function createCallActivity()
    {
        return new CallActivity();
    }

    /**
     * Create a execution instance repository.
     *
     * @return \ProcessMaker\Test\Models\ExecutionInstanceRepository
     */
    public function createExecutionInstanceRepository()
    {
        return new ExecutionInstanceRepositoryEngine();
    }

    /**
     * Create a test class
     *
     * @return TestOneClassWithEmptyConstructor
     */
    public function createTestOne()
    {
        return new TestOneClassWithEmptyConstructor();
    }

    /**
     * Create a test class with parameters
     *
     * @param mixed $field1
     * @param mixed $field2
     *
     * @return TestTwoClassWithArgumentsConstructor
     */
    public function createTestTwo($field1, $field2)
    {
        return new TestTwoClassWithArgumentsConstructor($field1, $field2);
    }
}
           

實作ProcessMaker\Nayra\Contracts\EventBusInterface

這個接口可以不實作具體方法(我就什麼都沒實作),他可能提供了對事件的監聽,做消息通知使用(待确認,我沒使用到)

<?php

namespace app\engine;

use ProcessMaker\Nayra\Contracts\EventBusInterface;

class EnventEngine implements EventBusInterface {
    


    /**
     * Register an event listener with the dispatcher.
     *
     * @param  string|array $events
     * @param  mixed $listener
     *
     * @return void
     */
    public function listen($events, $listener)
    { }

    /**
     * Determine if a given event has listeners.
     *
     * @param  string $eventName
     *
     * @return bool
     */
    public function hasListeners($eventName)
    { }

    /**
     * Register an event subscriber with the dispatcher.
     *
     * @param  object|string $subscriber
     *
     * @return void
     */
    public function subscribe($subscriber)
    { }

    /**
     * Dispatch an event until the first non-null response is returned.
     *
     * @param  string|object $event
     * @param  mixed $payload
     *
     * @return array|null
     */
    public function until($event, $payload = [])
    { }

    /**
     * Dispatch an event and call the listeners.
     *
     * @param  string|object $event
     * @param  mixed $payload
     * @param  bool $halt
     *
     * @return array|null
     */
    public function dispatch($event, $payload = [], $halt = false)
    { }

    /**
     * Register an event and payload to be fired later.
     *
     * @param  string $event
     * @param  array $payload
     *
     * @return void
     */
    public function push($event, $payload = [])
    { }

    /**
     * Flush a set of pushed events.
     *
     * @param  string $event
     *
     * @return void
     */
    public function flush($event)
    { }

    /**
     * Remove a set of listeners from the dispatcher.
     *
     * @param  string $event
     *
     * @return void
     */
    public function forget($event)
    { }

    /**
     * Forget all of the queued listeners.
     *
     * @return void
     */
    public function forgetPushed()
    { }
}
           

實作ProcessMaker\Nayra\Contracts\Engine\ExecutionInstanceInterface

這個接口是核心接口,它的ProcessMaker\Nayra\Engine\ExecutionInstanceTrait提供了包括建立執行執行個體的具體方法等關于執行執行個體的其他核心方法,我們不需要實作什麼,引用它的trait即可。

<?php

namespace app\engine;

use ProcessMaker\Nayra\Contracts\Engine\ExecutionInstanceInterface;
use ProcessMaker\Nayra\Engine\ExecutionInstanceTrait;

/**
 * Execution instance for the engine.
 *
 * @package ProcessMaker\Models
 */
class ExecutionInstanceEngine implements ExecutionInstanceInterface {

    use ExecutionInstanceTrait;

    /**
     * Initialize a token class with unique id.
     *
     */
    protected function initToken()
    {
        $this->setId(uniqid());
    }
}
           

實作ProcessMaker\Nayra\Contracts\Repositories\ExecutionInstanceRepositoryInterface

這個接口提供了兩個方法,分别是啟動流程和結束流程要做出的操作,persistInstanceCreated(ExecutionInstanceInterface $instance)是啟動流程時執行的,persistInstanceCompleted(ExecutionInstanceInterface $instance)是結束流程時執行的方法,它的參數中可以擷取到bpmn檔案的processid為$instance->getProcess()->getId(),在這兩個方法中我們需要做出自己的資料庫操作,比如寫入使用者的申請,和修改使用者申請的狀态為完成。具體代碼大家自己定義就好,我就不給大家實作了。

<?php

namespace app\engine;

use ProcessMaker\Nayra\Contracts\Bpmn\ParticipantInterface;
use ProcessMaker\Nayra\Contracts\Engine\ExecutionInstanceInterface;
use ProcessMaker\Nayra\Contracts\Repositories\ExecutionInstanceRepositoryInterface;
use ProcessMaker\Nayra\Contracts\Repositories\StorageInterface;

/**
 * Execution Instance Repository.
 *
 * @package ProcessMaker\Models
 */
class ExecutionInstanceRepositoryEngine implements ExecutionInstanceRepositoryInterface {



    /**
     * Array to simulate a storage of execution instances.
     *
     * @var array $data
     */
    private static $data = [];

    /**
     * 擷取目前登陸使用者資訊
     *
     * @return array
     */
    public function getUserInfo() {

        return di()->get('userinfo');
    }

    /**
     * Load an execution instance from a persistent storage.
     *
     * @param string $uid
     * @param StorageInterface $storage
     *
     * @return null|ExecutionInstanceInterface
     */
    public function loadExecutionInstanceByUid($uid, StorageInterface $storage)
    {
        if (empty(self::$data) || empty(self::$data[$uid])) {
            return;
        }
        $data = self::$data[$uid];
        $instance = $this->createExecutionInstance();
        $process = $storage->getProcess($data['processId']);
        $process->addInstance($instance);
        $dataStore = $storage->getFactory()->createDataStore();
        $dataStore->setData($data['data']);
        $instance->setId($uid);
        $instance->setProcess($process);
        $instance->setDataStore($dataStore);
        $process->getTransitions($storage->getFactory());

        //Load tokens:
        foreach($data['tokens'] as $tokenInfo) {
            $token = $storage->getFactory()->getTokenRepository()->createTokenInstance();
            $token->setProperties($tokenInfo);
            $element = $storage->getElementInstanceById($tokenInfo['elementId']);
            $element->addToken($instance, $token);
        }
        return $instance;
    }

    /**
     * Set the test data to be loaded.
     *
     * @param array $data
     */
    public function setRawData(array $data)
    {
        self::$data = $data;
    }

    /**
     * Creates an execution instance.
     *
     * @return \ProcessMaker\Test\Models\ExecutionInstance
     */
    public function createExecutionInstance()
    {
        return new ExecutionInstanceEngine();
    }

    /**
     * Persists instance's data related to the event Process Instance Created
     *
     * @param ExecutionInstanceInterface $instance
     *
     * @return mixed
     */
    public function persistInstanceCreated(ExecutionInstanceInterface $instance)
    {
        
    }

    /**
     * Persists instance's data related to the event Process Instance Completed
     *
     * @param ExecutionInstanceInterface $instance
     *
     * @return mixed
     */
    public function persistInstanceCompleted(ExecutionInstanceInterface $instance)
    {
        
    }

    /**
     * Persists collaboration between two instances.
     *
     * @param ExecutionInstanceInterface $target Target instance
     * @param ParticipantInterface $targetParticipant Participant related to the target instance
     * @param ExecutionInstanceInterface $source Source instance
     * @param ParticipantInterface $sourceParticipant
     */
    public function persistInstanceCollaboration(ExecutionInstanceInterface $target, ParticipantInterface $targetParticipant, ExecutionInstanceInterface $source, ParticipantInterface $sourceParticipant)
    {
    }
}
           

實作ProcessMaker\Nayra\Contracts\Repositories\TokenRepositoryInterface

最後這個最為重要,也比較特殊,我沒有找到其他解決辦法,這個需要去改下引擎的trait檔案,我們需要把ProcessMaker\Nayra\RepositoryTrait裡的getTokenRepository()方法改造成傳回ProcessMaker\Nayra\Contracts\Repositories\TokenRepositoryInterface接口的實作

public function getTokenRepository()
    {
        if ($this->tokenRepo === null) {
            $this->tokenRepo = new TokenRepositoryEngine();
        }
        return $this->tokenRepo;
    }
           

接下來,我們來實作ProcessMaker\Nayra\Contracts\Repositories\TokenRepositoryInterface,這個接口裡面提供了任務流轉時各個節點持久化操作的出口,我們需要逐一把他們實作,進而定義我們自己的任務執行時各階段的執行情況,我們最後告訴引擎我們進行到流程中哪個節點時依據的就是這裡持久化的資料,是以這個方法十分重要,它是整個流程運轉的關鍵所在,接下來我給大家列出各個方法的含義,大家自行實作。

1)persistStartEventTriggered(StartEventInterface $startEvent, CollectionInterface $tokens)此方法會傳回啟動節點的資料,并且會在啟動流程時被調用,其中$startEvent->getId()為節點id,$startEvent->getName()為節點名稱。

2)persistActivityActivated(ActivityInterface $activity, TokenInterface $token)此方法會傳回下一步要執行的節點的資料,并且會在存在下一步需要執行的節點時被調用,其中$token->getStatus()為節點狀态,$activity->getId()為節點id,$activity->getName()為節點名稱。

3)persistActivityCompleted(ActivityInterface $activity, TokenInterface $token)此方法會傳回被完成的節點的資料,并且會在節點被完成時被調用,其中$token->getStatus()為節點狀态,$activity->getId()為節點id,$activity->getName()為節點名稱。

4)persistActivityClosed(ActivityInterface $activity, TokenInterface $token)此方法會傳回被關閉的節點的資料,并且會在節點被關閉時被調用,其中$token->getStatus()為節點狀态,$activity->getId()為節點id,$activity->getName()為節點名稱。

5)persistThrowEventTokenConsumed(ThrowEventInterface $endEvent, TokenInterface $token)此方法會傳回結束節點的資料,并且會在觸發結束節點時被調用,其中$endEvent->getId()為節點id,$endEvent->getName()為節點名稱。

<?php

namespace app\engine;

use app\services\ProcessService;
use ProcessMaker\Nayra\Bpmn\Collection;
use ProcessMaker\Nayra\Bpmn\Models\Token;
use ProcessMaker\Nayra\Bpmn\Models\EndEvent;
use ProcessMaker\Nayra\Contracts\Bpmn\ActivityInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\CatchEventInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\CollectionInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\EventBasedGatewayInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\GatewayInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\StartEventInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\ThrowEventInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\TokenInterface;
use ProcessMaker\Nayra\Contracts\Repositories\TokenRepositoryInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\CallActivityInterface;
use ProcessMaker\Nayra\Contracts\Bpmn\ScriptTaskInterface;

/**
 * Token Repository.
 *
 * @package ProcessMaker\Models
 */
class TokenRepositoryEngine implements TokenRepositoryInterface {

    public $persistCalls = 0;


    /**
     * 擷取目前登陸使用者資訊
     *
     * @return array
     */
    public function getUserInfo() {

        return di()->get('userinfo');
    }

    /**
     * Create a token instance.
     *
     * @return TokenInterface
     */
    public function createTokenInstance() {
        $token                           = new Token();
        return $token;
    }

    /**
     * Load a token from a persistent storage.
     *
     * @param string $uid
     *
     * @return \ProcessMaker\Nayra\Contracts\Bpmn\TokenInterface
     */
    public function loadTokenByUid($uid) {

    }

    /**
     * Create or update a activity to a persistent storage.
     *
     * @param \ProcessMaker\Nayra\Contracts\Bpmn\TokenInterface $token
     * @param bool $saveChildElements
     *
     * @return $this
     */
    public function store(TokenInterface $token, $saveChildElements = false) {

    }

    /**
     * Persists instance and token data when a token arrives to an activity
     *
     * @param ActivityInterface $activity
     * @param TokenInterface $token
     *
     * @return mixed
     */
    public function persistActivityActivated(ActivityInterface $activity, TokenInterface $token) {
    }

    /**
     * Persists instance and token data when a token within an activity change to error state
     *
     * @param ActivityInterface $activity
     * @param TokenInterface $token
     *
     * @return mixed
     */
    public function persistActivityException(ActivityInterface $activity, TokenInterface $token) {
//        echo 'Exception:' . $token->getStatus() . '->' . $activity->getId() . '<br>';
    }

    /**
     * Persists instance and token data when a token is completed within an activity
     *
     * @param ActivityInterface $activity
     * @param TokenInterface $token
     *
     * @return mixed
     */
    public function persistActivityCompleted(ActivityInterface $activity, TokenInterface $token) {
    }

    /**
     * Persists instance and token data when a token is closed by an activity
     *
     * @param ActivityInterface $activity
     * @param TokenInterface $token
     *
     * @return mixed
     */
    public function persistActivityClosed(ActivityInterface $activity, TokenInterface $token) {
    }

    /**
     * Get persist calls
     *
     * @return int
     */
    public function getPersistCalls() {
        return $this->persistCalls;
    }

    /**
     * Reset persist calls
     *
     */
    public function resetPersistCalls() {
        $this->persistCalls = 0;
    }

    /**
     * Persists instance and token data when a token arrives in a throw event
     *
     * @param ThrowEventInterface $event
     * @param TokenInterface $token
     *
     * @return mixed
     */
    public function persistThrowEventTokenArrives(ThrowEventInterface $event, TokenInterface $token) {
        $this->persistCalls++;
    }

    /**
     * Persists instance and token data when a token is consumed in a throw event
     *
     * @param ThrowEventInterface $endEvent
     * @param TokenInterface $token
     *
     * @return mixed
     */
    public function persistThrowEventTokenConsumed(ThrowEventInterface $endEvent, TokenInterface $token) {
    }

    /**
     * Persists instance and token data when a token is passed in a throw event
     *
     * @param ThrowEventInterface $endEvent
     * @param TokenInterface $token
     *
     * @return mixed
     */
    public function persistThrowEventTokenPassed(ThrowEventInterface $endEvent, TokenInterface $token) {
        $this->persistCalls++;
    }

    /**
     * Persists instance and token data when a token arrives in a gateway
     *
     * @param GatewayInterface $exclusiveGateway
     * @param TokenInterface $token
     *
     * @return mixed
     */
    public function persistGatewayTokenArrives(GatewayInterface $gateway, TokenInterface $token) {
    }

    /**
     * Persists instance and token data when a token is consumed in a gateway
     *
     * @param GatewayInterface $exclusiveGateway
     * @param TokenInterface $token
     *
     * @return mixed
     */
    public function persistGatewayTokenConsumed(GatewayInterface $exclusiveGateway, TokenInterface $token) {
    }

    /**
     * Persists instance and token data when a token is passed in a gateway
     *
     * @param GatewayInterface $exclusiveGateway
     * @param TokenInterface $token
     *
     * @return mixed
     */
    public function persistGatewayTokenPassed(GatewayInterface $exclusiveGateway, TokenInterface $token) {
        $this->persistCalls++;
    }

    /**
     * Persists instance and token data when a token arrives in a catch event
     *
     * @param CatchEventInterface $intermediateCatchEvent
     * @param TokenInterface $token
     *
     * @return mixed
     */
    public function persistCatchEventTokenArrives(CatchEventInterface $intermediateCatchEvent, TokenInterface $token) {
        $this->persistCalls++;
    }

    /**
     * Persists instance and token data when a token is consumed in a catch event
     *
     * @param CatchEventInterface $intermediateCatchEvent
     * @param TokenInterface $token
     *
     * @return mixed
     */
    public function persistCatchEventTokenConsumed(CatchEventInterface $intermediateCatchEvent, TokenInterface $token) {
        $this->persistCalls++;
    }

    /**
     * Persists instance and token data when a token is passed in a catch event
     *
     * @param CatchEventInterface $intermediateCatchEvent
     * @param Collection $consumedTokens
     *
     * @return mixed
     */
    public function persistCatchEventTokenPassed(CatchEventInterface $intermediateCatchEvent, Collection $consumedTokens) {
        $this->persistCalls++;
    }

    /**
     * Persists instance and token data when a message arrives to a catch event
     *
     * @param CatchEventInterface $intermediateCatchEvent
     * @param TokenInterface $token
     */
    public function persistCatchEventMessageArrives(CatchEventInterface $intermediateCatchEvent, TokenInterface $token) {
        $this->persistCalls++;
    }

    /**
     * Persists instance and token data when a message is consumed in a catch event
     *
     * @param CatchEventInterface $intermediateCatchEvent
     * @param TokenInterface $token
     */
    public function persistCatchEventMessageConsumed(CatchEventInterface $intermediateCatchEvent, TokenInterface $token) {
        $this->persistCalls++;
    }

    /**
     * Persists tokens that triggered a Start Event
     *
     * @param StartEventInterface $startEvent
     * @param CollectionInterface $tokens
     *
     */
    public function persistStartEventTriggered(StartEventInterface $startEvent, CollectionInterface $tokens) {
    }

    /**
     * Persists instance and token data when a token is consumed in a event based gateway
     *
     * @param \ProcessMaker\Nayra\Contracts\Bpmn\EventBasedGatewayInterface $eventBasedGateway
     * @param \ProcessMaker\Nayra\Contracts\Bpmn\TokenInterface $passedToken
     * @param \ProcessMaker\Nayra\Contracts\Bpmn\CollectionInterface $consumedTokens
     *
     * @return mixed
     */
    public function persistEventBasedGatewayActivated(EventBasedGatewayInterface $eventBasedGateway, TokenInterface $passedToken, CollectionInterface $consumedTokens) {

    }

    private function getActivityType($activity) {
        if ($activity instanceof ScriptTaskInterface)
        {
            return 'scriptTask';
        }

        if ($activity instanceof CallActivityInterface)
        {
            return 'callActivity';
        }

        if ($activity instanceof ActivityInterface)
        {
            return 'task';
        }

        return 'task';
    }

}
           

到此為止,我們已經為建立執行執行個體做好一切前置準備,接下來我們開始寫啟動流程和任務流轉兩個方法。

1.啟動流程

廢話不多說,這裡寫法是固定的,我們直接上代碼,詳見以下代碼及注釋。

<?php

/**
 * 工作流服務
 * Date: 2019年08月27日14:19:43
 */

namespace app\services;

use app\engine\BpmnEngine;
use app\engine\RepositoryEngine;
use app\engine\EnventEngine;
use ProcessMaker\Nayra\Storage\BpmnDocument;
use ProcessMaker\Nayra\Contracts\Repositories\StorageInterface;

class ProcessService {


    /**
     * 啟動流程
     * @param $bpmn //bpmn檔案内容
     * @return bool
     */
    public function startProcess($bpmn) {
        //start 擷取執行執行個體
        $this->repository = new RepositoryEngine();
        $fakeDispatcher   = new EnventEngine();
        $this->engine     = new BpmnEngine($this->repository, $fakeDispatcher);
        $bpmnRepository   = new BpmnDocument();
        $bpmnRepository->setEngine($this->engine);
        $bpmnRepository->setFactory($this->repository);
        $bpmnRepository->loadXML($bpmn);
        $process_id       = $bpmnRepository->getElementsByTagNameNS(BpmnDocument::BPMN_MODEL, $name)->item(0)->getAttribute('id');
        $process          = $bpmnRepository->getProcess($process_id);
        $dataStore        = $this->repository->createDataStore();
        $instance         = $this->engine->createExecutionInstance($process, $dataStore);//$instance為執行執行個體
        //end 擷取執行執行個體
        $start            = $process->getEvents()->item(0);//擷取啟動節點對象
        $start->start($instance);//執行啟動方法
        try
        {
            $this->engine->runToNextState();//流轉到下一步
            return TRUE;
        }
        catch (Exception $e)
        {
            return FALSE;
        }
    }
           

2.任務流轉

流轉的時候需要先從庫裡把之前執行的結果拿到,引擎才能知道該如何往下流轉,詳見以下代碼及注釋。

<?php

/**
 * 工作流服務
 * Date: 2019年08月27日14:19:43
 */

namespace app\services;

use app\engine\BpmnEngine;
use app\engine\RepositoryEngine;
use app\engine\EnventEngine;
use ProcessMaker\Nayra\Storage\BpmnDocument;
use ProcessMaker\Nayra\Contracts\Repositories\StorageInterface;

class ProcessService {

    const EXECUTIONINSTANCEID = 'executionInstanceId';//固定參數

    /**
     * 完成任務
     * @param $element_id
     * @param $bpmn 
     * @return bool
     */
    public function completeTask($element_id, $bpmn) {
        //start 擷取執行執行個體
        $bpmnRepository   = new BpmnDocument();
        $this->repository = new RepositoryEngine();
        $fakeDispatcher   = new EnventEngine();
        $this->engine     = new BpmnEngine($this->repository, $fakeDispatcher);
        $this->engine->setRepository($this->repository);
        $this->engine->setStorage($bpmnRepository);
        $bpmn_id          = $this->loadbpmn($bpmnRepository, $bpmn);
        $bpmnRepository   = new BpmnDocument();
        $bpmnRepository->setEngine($this->engine);
        $bpmnRepository->setFactory($this->repository);
        $bpmnRepository->loadXML($bpmn);
        $bpmn_id          = $bpmnRepository->getElementsByTagNameNS(BpmnDocument::BPMN_MODEL, $name)->item(0)->getAttribute('id');
        $this->prepareParallelProcessWithActivityCompleted($bpmnRepository, $bpmn_id);
        $instance         = $this->engine->loadExecutionInstance(self::EXECUTIONINSTANCEID);//此為執行執行個體
        //end 擷取執行執行個體
        $activity         = $bpmnRepository->getScriptTask($element_id);
        //擷取目前任務令牌
        $token            = $activity->getTokens($instance)->item(0);
        //執行完成任務方法
        $activity->complete($token);
        try
        {
            $this->engine->runToNextState();//流轉到下一步
            return TRUE;
        }
        catch (Exception $e)
        {
            return FALSE;
        }
    }

    /**
     * 将流程推進至指定節點
     * @param StorageInterface $repository
     * @param $bpmn_id
     */
    private function prepareParallelProcessWithActivityCompleted(StorageInterface $repository, $bpmn_id) {
        $executionInstanceRepository = $this->engine->getRepository()->createExecutionInstanceRepository($repository);
        $executionInstanceRepository->setRawData([
            self::EXECUTIONINSTANCEID => [
                'processId' => $bpmn_id,
                'data'      => [],
                'tokens'    => [//此處為從資料庫提取的持久化的節點任務執行狀态對應關系
                    'node_1' => 'CLOSED',
                    'node_3' => 'CLOSED',
                    'node_4' => 'ACTIVED',
                ],
            ]
        ]);
    }
           

這篇文章裡我們已經知道如何啟動流程,如何進行任務流轉,下一篇我們為大家講述資料庫是如何設計,流程與表單之間是如何協同工作的,我們下期見!