PHP code example of wiseadmin / wise-workflow

1. Go to this page and download the library: Download wiseadmin/wise-workflow library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

wiseadmin / wise-workflow example snippets


use wise\workflow\WorkflowEngine;
use wise\workflow\DatabaseMarkingStore;

$engine = new WorkflowEngine(new DatabaseMarkingStore());

use wise\workflow\Workflow;
use wise\workflow\Place;
use wise\workflow\Transition;

// 创建一个订单审批工作流
$workflow = new Workflow('order_approval');

// 添加状态节点(Place)
$workflow->addPlace(new Place('draft'));       // 草稿
$workflow->addPlace(new Place('submitted'));   // 已提交
$workflow->addPlace(new Place('reviewing'));   // 审核中
$workflow->addPlace(new Place('approved'));    // 已通过
$workflow->addPlace(new Place('rejected'));    // 已拒绝

// 设置初始状态
$workflow->setMetadata(['initial_place' => 'draft']);

// 添加转换规则(Transition)
// 格式:new Transition(name, from, to)
$workflow->addTransition(new Transition('submit', 'draft', 'submitted'));
$workflow->addTransition(new Transition('assign', 'submitted', 'reviewing'));
$workflow->addTransition(new Transition('approve', 'reviewing', 'approved'));
$workflow->addTransition(new Transition('reject', 'reviewing', 'rejected'));
$workflow->addTransition(new Transition('resubmit', 'rejected', 'draft'));

use wise\workflow\WorkflowRegistry;

WorkflowRegistry::add($workflow);

use wise\workflow\WorkflowEngine;
use wise\workflow\WorkflowRegistry;
use wise\workflow\DatabaseMarkingStore;

// 创建引擎实例
$store = new DatabaseMarkingStore();
$engine = new WorkflowEngine($store);

// 获取工作流
$workflow = WorkflowRegistry::get('order_approval');

// 1. 初始化工作流状态(将订单设为初始状态)
$engine->init($workflow, 'order:1001');

// 2. 检查是否可以提交
if ($engine->can($workflow, 'order:1001', 'submit')) {
    // 3. 执行提交转换
    $engine->apply($workflow, 'order:1001', 'submit', [
        'operator' => 'admin',
        'remark' => '提交审批',
    ]);
    echo "提交成功!\n";
}

// 4. 获取当前状态
$marking = $engine->getMarking('order:1001');
print_r($marking); // 输出:['submitted' => 1]

// 5. 获取可用的转换
$enabled = $engine->getEnabledTransitions($workflow, 'order:1001');
foreach ($enabled as $transition) {
    echo $transition->getName() . "\n"; // 输出:assign
}

namespace wise\workflow;

class Place
{
    /**
     * @param string $name 状态名称(唯一标识)
     */
    public function __construct(string $name);

    public function getName(): string;
}

// 基本用法
$place = new Place('draft');

// 批量添加状态
$workflow->addPlace(new Place('draft'));
$workflow->addPlace(new Place('submitted'));
$workflow->addPlace(new Place('approved'));

namespace wise\workflow;

class Transition
{
    /**
     * @param string $name 转换名称(唯一标识)
     * @param string|array $from 源状态(单个或多个)
     * @param string|array $to 目标状态(单个或多个)
     * @param callable|null $guard 守卫条件回调(可选)
     */
    public function __construct(string $name, $from, $to, ?callable $guard = null);

    public function getName(): string;
    public function getFrom(): array;
    public function getTo(): array;
    public function getGuard(): ?callable;
}

// 基本转换:从 draft 到 submitted
$transition = new Transition('submit', 'draft', 'submitted');

// 多源状态:draft 或 rejected 都可以提交
$transition = new Transition('submit', ['draft', 'rejected'], 'submitted');

// 带守卫条件:只有金额小于 1000 的订单才能自动通过
$transition = new Transition('auto_approve', 'reviewing', 'approved', function ($marking, $context) {
    return $context['amount'] < 1000;
});

namespace wise\workflow;

class Workflow
{
    /**
     * @param string $name 工作流名称(全局唯一)
     * @param array $places Place 对象数组
     * @param array $transitions Transition 对象数组
     * @param string|null $initialPlace 初始状态
     */
    public function __construct(string $name, array $places = [], array $transitions = [], $initialPlace = null);

    public function addPlace(Place $place): void;
    public function addTransition(Transition $transition): void;
    public function setMetadata(array $metadata): void;
    public function getName(): string;
    public function getPlaces(): array;
    public function getTransitions(): array;
    public function getInitialPlace(): ?string;
    public function getMetadata(): array;
}

namespace wise\workflow;

class WorkflowRegistry
{
    /** 注册一个工作流 */
    public static function add(Workflow $workflow): void;

    /** 获取指定名称的工作流 */
    public static function get(string $name): Workflow;

    /** 获取所有已注册的工作流 */
    public static function all(): array;

    /** 判断工作流是否存在 */
    public static function has(string $name): bool;

    /** 清除所有已注册的工作流 */
    public static function clear(): void;

    /** 从数据库加载所有启用的工作流 */
    public static function loadFromDatabase(): void;
}

// 方式一:代码静态注册
$workflow = new Workflow('order_approval');
// ... 添加 places 和 transitions
WorkflowRegistry::add($workflow);

// 方式二:数据库动态注册
// 先在 workflow_definition 表中插入配置,然后:
WorkflowRegistry::loadFromDatabase();

namespace wise\workflow;

class WorkflowEngine
{
    /**
     * @param MarkingStoreInterface $markingStore 状态存储(必须)
     * @param mixed $eventService 事件服务(可选)
     * @param mixed $permissionService 权限服务(可选)
     * @param mixed $historyModel 历史记录模型(可选)
     */
    public function __construct(
        MarkingStoreInterface $markingStore,
        $eventService = null,
        $permissionService = null,
        $historyModel = null
    );
}

// 订单
$subject = 'order:1001';

// 工单
$subject = 'ticket:W-2024-001';

// 也可以自定义格式
$subject = 'document_approval_42';

use app\common\facade\Workflow;

use app\common\facade\Workflow;

// 创建订单后初始化工作流
$order = Order::create([...]);
Workflow::init('order_approval', $order);

// 在控制器中处理审核
public function approve($orderId)
{
    $order = Order::find($orderId);

    // 检查权限(自动通过 GuardListener 处理)
    if (!Workflow::can('order_approval', $order, 'approve')) {
        return $this->error('无权执行此操作或当前状态不允许');
    }

    // 执行转换
    Workflow::apply('order_approval', $order, 'approve', [
        'operator' => session('admin_id'),
        'remark' => '审核通过',
    ]);

    // 获取当前可执行操作
    $transitions = Workflow::transitions('order_approval', $order);

    // 判断状态
    $isApproved = Workflow::is('order_approval', $order, 'approved');

    return $this->success('审核完成', compact('transitions', 'isApproved'));
}

namespace wise\workflow;

class GuardListener
{
    /**
     * @param mixed $permissionService 权限服务(如 PermissionService)
     * @param bool $enabled 是否启用权限检查
     */
    public function __construct($permissionService = null, bool $enabled = true);
}

// 定义转换时指定权限标识
$transition = new Transition(
    'approve',           // 转换名称
    'reviewing',         // 源状态
    'approved',          // 目标状态
    function ($marking, $context) use ($permissionService) {
        // 此处传入权限检查回调
        return $permissionService->check('order.approve');
    }
);

Workflow::apply('order_approval', $order, 'approve', [
    'user_id' => session('admin_id'),           // 操作人ID
    'guard' => 'workflow.order.approve',        // 需要检查的权限标识
    'remark' => '审核通过',
]);

use think\facade\Event;

// 监听转换后事件 - 发送通知
Event::listen('workflow.after_transition', function ($params) {
    $workflow   = $params['workflow'];
    $transition = $params['transition'];
    $subject    = $params['subject'];
    $context    = $params['context'];

    // 只处理特定工作流
    if ($workflow->getName() !== 'order_approval') {
        return;
    }

    switch ($transition->getName()) {
        case 'submit':
            // 订单提交后通知审核员
            Notifier::send('reviewer', "新订单 {$subject} 等待审核");
            break;

        case 'approve':
            // 审核通过后更新订单状态
            Order::where('order_no', $subject)
                ->update(['status' => 'approved', 'approved_at' => date('Y-m-d H:i:s')]);
            break;

        case 'reject':
            // 审核拒绝后通知提交人
            Notifier::send($context['submitter_id'], "订单 {$subject} 审核被拒绝");
            break;
    }
});

// 监听特定转换事件
Event::listen('workflow.order_approval.approve', function ($params) {
    // 只针对 order_approval 工作流的 approve 转换
    Log::record("订单 {$params['subject']} 已被审核通过", 'workflow');
});

// 在 before_transition 事件中进行业务校验
Event::listen('workflow.before_transition', function ($params) {
    $subject  = $params['subject'];
    $context  = $params['context'];

    // 业务校验:审核通过前检查金额
    if ($params['transition']->getName() === 'approve') {
        $order = Order::find($subject);
        if ($order->amount > 100000 && empty($context['manager_approved'])) {
            throw new \Exception('大额订单需要经理审批');
        }
    }
});

// 通过引擎
$history = $engine->getHistory('order:1001', 20);

// 通过 Facade
$history = Workflow::history('order_approval', $order, 20);

// 通过模型直接查询
$history = \wise\workflow\model\WorkflowHistory::where('subject', 'order:1001')
    ->order('id', 'desc')
    ->select();

[
    'id'              => 42,                    // 记录ID
    'subject'         => 'order:1001',          // 流程实例标识
    'transition_name' => 'approve',             // 转换名称
    'from_place'      => 'reviewing',           // 源状态
    'to_place'        => 'approved',            // 目标状态
    'context'         => '{"remark":"审核通过"}', // 上下文(JSON)
    'user_id'         => 1,                     // 操作用户ID
    'ip'              => '192.168.1.100',       // 操作IP
    'user_agent'      => 'Mozilla/5.0...',      // User-Agent
    'create_time'      => '2024-01-15 14:30:00', // 创建时间
]


namespace plugin\myplugin;

use wise\workflow\Workflow;
use wise\workflow\Place;
use wise\workflow\Transition;

class Plugin
{
    /**
     * 获取工作流定义
     *
     * @return Workflow[]
     */
    public static function getWorkflowDefinitions(): array
    {
        $workflow = new Workflow('my_approval');
        $workflow->addPlace(new Place('draft'));
        $workflow->addPlace(new Place('submitted'));
        $workflow->addPlace(new Place('approved'));
        $workflow->addPlace(new Place('rejected'));

        $workflow->addTransition(new Transition('submit', 'draft', 'submitted'));
        $workflow->addTransition(new Transition('approve', 'submitted', 'approved'));
        $workflow->addTransition(new Transition('reject', 'submitted', 'rejected'));

        $workflow->setMetadata([
            'title'       => '我的审批流程',
            'description' => '自定义审批工作流',
            'initial_place' => 'draft',
        ]);

        return [$workflow];
    }
}


namespace plugin\myplugin;

use app\common\facade\Workflow;

class BootServiceProvider
{
    public function boot(): void
    {
        // 注册工作流
        $definitions = Plugin::getWorkflowDefinitions();
        foreach ($definitions as $workflow) {
            Workflow::register($workflow);
        }

        // 监听工作流事件
        \think\facade\Event::listen('workflow.after_transition', function ($params) {
            if ($params['workflow']->getName() === 'my_approval') {
                // 自定义逻辑:发送通知、更新数据等
            }
        });
    }
}


namespace plugin\myplugin\controller;

use app\common\controller\BaseController;
use app\common\facade\Workflow;

class ApprovalController extends BaseController
{
    /**
     * 提交审批
     */
    public function submit()
    {
        $id = $this->request->param('id');
        $record = MyModel::find($id);

        if (!$record) {
            return $this->error('记录不存在');
        }

        try {
            // 检查权限
            if (!Workflow::can('my_approval', $record, 'submit')) {
                return $this->error('当前状态不允许提交');
            }

            // 执行转换
            Workflow::apply('my_approval', $record, 'submit', [
                'user_id' => session('admin_id'),
            ]);

            return $this->success('提交成功');
        } catch (\Exception $e) {
            return $this->error($e->getMessage());
        }
    }

    /**
     * 获取审批详情(含可用操作)
     */
    public function detail()
    {
        $id = $this->request->param('id');
        $record = MyModel::find($id);

        // 获取当前状态和可用操作
        $marking = Workflow::marking('my_approval', $record);
        $transitions = Workflow::transitions('my_approval', $record);
        $history = Workflow::history('my_approval', $record, 10);

        $availableActions = [];
        foreach ($transitions as $trans) {
            $availableActions[] = [
                'name'  => $trans->getName(),
                'label' => $trans->getName(), // 可从 metadata 获取显示名称
                'can'   => Workflow::can('my_approval', $record, $trans->getName()),
            ];
        }

        return $this->success('获取成功', [
            'record'           => $record,
            'current_place'    => $marking,
            'available_actions' => $availableActions,
            'history'          => $history,
        ]);
    }
}

use wise\workflow\MarkingStoreInterface;
use wise\workflow\Workflow;

class RedisMarkingStore implements MarkingStoreInterface
{
    protected $redis;

    public function __construct($redis)
    {
        $this->redis = $redis;
    }

    public function getMarking(string $subject): array
    {
        $data = $this->redis->get('workflow:marking:' . $subject);
        return $data ? json_decode($data, true) : [];
    }

    public function setMarking(string $subject, array $marking, array $context = []): void
    {
        $this->redis->set('workflow:marking:' . $subject, json_encode($marking));
    }

    public function recordHistory(string $subject, string $transitionName, string $fromPlace, string $toPlace, array $context = []): void
    {
        // 历史记录仍可写入数据库或 Redis List
    }
}

// 在 ServiceProvider 中替换绑定
$this->app->bind(MarkingStoreInterface::class, function () {
    return new RedisMarkingStore(redis());
});

// 条件守卫:根据业务数据判断
$transition = new Transition(
    'auto_approve',
    'reviewing',
    'approved',
    function ($marking, $context) {
        // 金额小于 1000 的订单自动通过
        if (isset($context['amount']) && $context['amount'] < 1000) {
            return true;
        }
        // 否则需要手动审批权限
        return PermissionService::check('order.approve');
    }
);

// 方式一:直接插入数据库
\think\facade\Db::name('workflow_definition')->insert([
    'name'          => 'order_approval',
    'title'         => '订单审批流程',
    'description'   => '标准订单审批工作流',
    'places'        => json_encode(['draft', 'submitted', 'reviewing', 'approved', 'rejected']),
    'transitions'   => json_encode([
        ['name' => 'submit',  'from' => ['draft'],     'to' => ['submitted']],
        ['name' => 'assign',  'from' => ['submitted'],  'to' => ['reviewing']],
        ['name' => 'approve', 'from' => ['reviewing'],  'to' => ['approved']],
        ['name' => 'reject',  'from' => ['reviewing'],  'to' => ['rejected']],
    ]),
    'initial_place' => 'draft',
    'status'        => 1,
    'create_time'   => date('Y-m-d H:i:s'),
    'update_time'   => date('Y-m-d H:i:s'),
]);

// 方式二:通过 Facade 注册(动态创建定义)
$workflow = (new Workflow('my_approval'))
    ->addPlace(new Place('draft'))
    ->addPlace(new Place('approved'))
    ->addTransition(new Transition('approve', 'draft', 'approved'));
$workflow->setMetadata([
    'title'       => '我的审批流',
    'description' => '测试',
    'initial_place' => 'draft',
]);
Workflow::register($workflow);

// 监听 after_transition 事件同步更新业务表
Event::listen('workflow.after_transition', function ($params) {
    $subject = $params['subject'];
    $newPlaces = $params['workflow']->getPlaces(); // 结合 marking 获取当前状态

    // 更新业务表的状态字段
    $orderId = str_replace('order:', '', $subject);
    \think\facade\Db::name('orders')
        ->where('id', $orderId)
        ->update(['wf_status' => implode(',', array_keys($newPlaces))]);
});

// 使用 Facade
if (Workflow::is('order_approval', $order, 'draft')) {
    echo '当前为草稿状态';
}

// 使用引擎
$marking = $engine->getMarking('order:1001');
if (isset($marking['draft'])) {
    echo '当前为草稿状态';
}

$transitions = Workflow::transitions('order_approval', $order);
foreach ($transitions as $transition) {
    echo $transition->getName(); // submit, approve, reject...
}

$transition = new Transition('approve', 'reviewing', 'approved', function ($marking, $context) {
    // 自定义权限逻辑
    return $context['user_role'] === 'manager' && $context['amount'] < 10000;
});

$engine->init($approvalWorkflow, 'order:1001');    // 审批工作流
$engine->init($paymentWorkflow, 'order:1001');      // 支付工作流(共享同一 subject)

// 从已通过回退到审核中
$workflow->addTransition(new Transition('rollback', 'approved', 'reviewing'));

// 在 database.php 中配置
'connections' => [
    'mysql' => [
        'prefix' => 'ws_',  // 表前缀,如 ws_workflow_marking
    ],
],
bash
# 第一步:复制迁移文件到框架的 database/migrations/ 目录
php think wise:workflow:publish

# 第二步:执行迁移创建数据库表
php think migrate:run

plugins/workflowdemo/
├── Plugin.php                          # 插件主类(工作流定义)
├── manifest.json                       # 插件清单(菜单+权限定义)
├── BootServiceProvider.php             # 启动服务提供者(注册工作流+事件)
├── controller/
│   ├── Base.php                        # 控制器基类
│   └── Order.php                       # 订单控制器(工作流操作)
├── model/
│   └── WorkflowOrder.php               # 订单模型
├── route/
│   └── plugin.php                       # 后端路由
├── database/migrations/
│   └── 20260606000001_create_workflow_orders_table.php
└── view/order/
    ├── index.html                       # 订单列表页
    ├── form.html                        # 订单创建/编辑页
    └── detail.html                      # 订单详情页(工作流操作按钮)
bash
# 工作流初始化:发布配置文件和迁移文件到框架对应目录
php think wise:workflow:publish

# 然后执行 ThinkPHP 系统迁移命令创建数据库表
php think migrate:run