当前位置: 首页 > 工具软件 > ProcessMaker > 使用案例 >

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

丁高峯
2023-12-01

上一篇文章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',
                ],
            ]
        ]);
    }

这篇文章里我们已经知道如何启动流程,如何进行任务流转,下一篇我们为大家讲述数据库是如何设计,流程与表单之间是如何协同工作的,我们下期见!

 类似资料: