
## 单文件工作流

一个工作流可以完整地封装在单个 PHP 文件中。将步骤类和构建器一起定义，把文件放入某个目录，然后使用 `Queuety::load_workflows()` 加载该目录（如下所示）。Queuety 不会自行扫描任何目录。

```php
// workflows/onboard-user.php
use Queuety\Queuety;

class CreateAccount implements \Queuety\Step {
    public function handle(array $state): array {
        $user = create_user($state['email']);
        return ['user_id' => $user->id, 'user_name' => $user->name];
    }
    public function config(): array {
        return ['max_attempts' => 3];
    }
}

class SendWelcome implements \Queuety\Step {
    public function handle(array $state): array {
        wp_mail($state['email'], 'Welcome ' . $state['user_name'], '...');
        return ['welcomed' => true];
    }
    public function config(): array {
        return [];
    }
}

return Queuety::define_workflow('onboard_user')
    ->then(CreateAccount::class, 'create_account')
    ->then(SendWelcome::class, 'send_welcome');
```

该文件会返回一个 `WorkflowBuilder`。每个步骤类都在文件中内联定义。加载文件后，这些类即可使用，工作流也会注册为模板。

## 加载工作流

从目录加载所有工作流文件：

```php
// Load all .php files from the workflows directory
$count = Queuety::load_workflows(__DIR__ . '/workflows/');
```

或者加载单个文件：

```php
$template = Queuety::load_workflow_file(__DIR__ . '/workflows/onboard-user.php');
```

然后按名称分派：

```php
$workflow_id = Queuety::run_workflow('onboard_user', ['email' => 'user@example.com']);
```

## 递归加载

将 `true` 作为第二个参数传入，以扫描子目录：

```php
Queuety::load_workflows(__DIR__ . '/workflows/', recursive: true);
```

这样便可以将工作流整理到不同文件夹中：

```
workflows/
├── onboarding/
│   ├── new-user.php
│   └── trial-upgrade.php
├── reports/
│   ├── daily-summary.php
│   └── weekly-digest.php
└── notifications/
    └── digest-email.php
```

## 使用所有工作流功能

文件工作流支持构建器支持的所有功能，但 `idempotency_key()` 除外，因为它不会存储在模板中；请改用 `Queuety::run_workflow( $name, $payload, [ 'idempotency_key' => '...' ] )`，在每次分派时传入。延迟、信号、并行步骤、条件分支、运行工作流、取消处理程序和状态清理均可使用：

```php
// workflows/approval-flow.php
use Queuety\Queuety;

class SubmitRequest implements \Queuety\Step {
    public function handle(array $state): array {
        return ['submitted_at' => date('Y-m-d H:i:s')];
    }
    public function config(): array { return []; }
}

class ProcessApproval implements \Queuety\Step {
    public function handle(array $state): array {
        return [
            'approved_by' => $state['approved_by'],
            'processed' => true,
        ];
    }
    public function config(): array { return []; }
}

class CleanupRequest implements \Queuety\Handler {
    public function handle(array $payload): void {
        delete_pending_request($payload['request_id']);
    }
    public function config(): array { return []; }
}

return Queuety::define_workflow('approval_flow')
    ->then(SubmitRequest::class)
    ->delay(hours: 24)
    ->wait_for_signal('approved')
    ->then(ProcessApproval::class)
    ->on_cancel(CleanupRequest::class)
    ->prune_state_after(2);
```

## 何时使用文件工作流

以下情况适合使用文件工作流：

- LLM 或代码生成器以编程方式创建工作流
- 工作流足够简单，可以放在单个文件中
- 你希望步骤逻辑紧邻工作流定义
- 你正在制作原型，希望快速迭代

对于较大的工作流，如果包含共享步骤类、复杂中间件，或多个工作流会重复使用某些步骤，那么采用分别存放类文件并使用 PSR-4 自动加载的标准方式更易于维护。

## 工作原理

在底层，文件工作流使用与构建器 API 完全相同的引擎。每个步骤都是数据库中的独立作业。状态通过 `queuety_workflows` 表流转。这里没有重放，也不要求确定性，性能也没有差异。

每次调用 load `require` 都会 `require_once` 该文件，因此每个请求中每个文件只加载一次；重复加载会重新声明其中的类并导致失败。返回的 `WorkflowBuilder` 会转换为 `WorkflowTemplate` 并存储在 `WorkflowRegistry` 中。调度和执行方式与任何其他工作流完全相同。
