
# PHP API

Queuety 中的所有功能都可通过 `Queuety\Queuety` 静态类访问。它是派发作业、构建工作流、驱动状态机、管理队列以及访问内部子系统的统一入口。

```php
use Queuety\Queuety;
```

## 独立访问核心功能

Queuety 的数据库支持运行时可在 WordPress 之外初始化。

这条独立运行路径适用于编排核心：

- 作业和工作器（工作器池还需要 `DB_HOST`、`DB_NAME`、`DB_USER` 和 `DB_PASSWORD` 常量，见下文）
- 工作流和信号
- 状态机和事件
- 工件、日志、指标和计划

它不能替代 WordPress 适配器。`Queuety::on_action()`、
`replace_wp_cron()`、插件引导程序以及真正的 `wp queuety ...`
注册流程仍需要 WordPress。

### `Queuety::init( Connection $conn )`

使用直接连接数据库的方式初始化 Queuety。

```php
use Queuety\Connection;
use Queuety\Queuety;

$conn = new Connection(
    host: '127.0.0.1:3306',
    dbname: 'wordpress',
    user: 'root',
    password: 'secret',
    prefix: 'wp_',
);

Queuety::init( $conn );
```

可选的 `table_prefix` 参数会按连接更改运行时
表名中的 Queuety 部分：

```php
$conn = new Connection(
    host: '127.0.0.1:3306',
    dbname: 'wordpress',
    user: 'root',
    password: 'secret',
    prefix: 'wp_',
    table_prefix: 'themequeue_',
);
```

这会将诸如 `queuety_jobs` 的表解析为 `wp_themequeue_jobs`。

### `Queuety::ensure_schema()`

确保当前连接对应的 Queuety 运行时表已存在。

```php
Queuety::init( $conn );
Queuety::ensure_schema();
```

有关完整模型和 WordPress 边界，请参阅 [独立使用](https://queuety.inline0.dev/zh/docs/features/standalone-use)。

## 作业

### `Queuety::dispatch( $handler, $payload )`

派发作业。接受处理程序名称字符串或 `Contracts\Job` 实例。返回 `PendingJob` 构建器，以便链式设置选项。

```php
// Handler name + payload (original API)
Queuety::dispatch( 'send_email', [ 'to' => 'user@example.com' ] );

// Contracts\Job instance (v0.6.0+)
Queuety::dispatch( new SendEmailJob( to: 'user@example.com' ) );

// With options
Queuety::dispatch( 'send_email', [ 'to' => 'user@example.com' ] )
    ->on_queue( 'emails' )
    ->with_priority( Priority::High )
    ->delay( 300 )
    ->max_attempts( 5 )
    ->rate_limit( 10, 60 )
    ->unique()
    ->after( $other_job_id );

// Get the job ID
$job_id = Queuety::dispatch( 'send_email', $payload )->id();
```

传入 `Contracts\Job` 实例时，序列化器会提取其 FQCN 作为处理程序名称，并将公共属性作为负载。当 `$handler` 是 `Job` 实例时，`$payload` 参数会被忽略。

### `PendingJob` 方法

| 方法 | 说明 |
|---|---|
| `on_queue( string $queue )` | 设置目标队列 |
| `with_priority( Priority $priority )` | 设置优先级 |
| `delay( int $seconds )` | 设置作业可用前的延迟时间 |
| `max_attempts( int $max )` | 最大重试次数 |
| `rate_limit( int $max, int $window )` | 速率限制：每个时间窗口内的最大执行次数 |
| `unique()` | 防止重复派发 |
| `after( int $job_id )` | 等待另一个作业先完成 |
| `concurrency_group( string $group, int $limit )` | 将作业加入共享工作器准入组 |
| `cost_units( int $units )` | 为派发附加相对执行成本元数据 |
| `id()` | 强制派发并返回作业 ID |

`concurrency_group()` 和 `cost_units()` 属于更广泛的[资源管理](https://queuety.inline0.dev/zh/docs/features/resource-management)模型，因此同一次派发可参与基于数量的并发限制、加权队列或提供方预算，以及工作器准入策略。

### `Queuety::dispatch_sync( $handler, $payload )`

在当前进程中同步执行作业，不将其加入队列。

```php
// With a Job instance
Queuety::dispatch_sync( new SendEmailJob( to: 'user@example.com', subject: 'Hi', body: 'Hello' ) );

// With a handler name and payload
Queuety::dispatch_sync( 'send_email', [ 'to' => 'user@example.com' ] );
```

会直接调用作业的 `handle()` 方法。不会创建数据库行，也不会使用工作器，且不会应用中间件。

### `Queuety::batch( $jobs )`

通过一条多行 INSERT 派发多个作业。

```php
$job_ids = Queuety::batch( [
    [ 'handler' => 'send_email', 'payload' => [ 'to' => 'a@example.com' ], 'queue' => 'emails' ],
    [ 'handler' => 'send_email', 'payload' => [ 'to' => 'b@example.com' ], 'priority' => 2 ],
] );
```

### `Queuety::create_batch( $jobs )`

创建 `BatchBuilder`，用于派发一组作业并跟踪回调和进度。完整详情请参阅[批处理](https://queuety.inline0.dev/zh/docs/features/batching)。

```php
$batch = Queuety::create_batch( [
    new ImportUsersJob( $chunk_1 ),
    new ImportUsersJob( $chunk_2 ),
] )
    ->name( 'Import users' )
    ->then( ImportCompleteHandler::class )
    ->catch( ImportFailedHandler::class )
    ->finally( ImportCleanupHandler::class )
    ->allow_failures()
    ->on_queue( 'imports' )
    ->dispatch();
```

### `BatchBuilder` 方法

| 方法 | 返回值 | 说明 |
|---|---|---|
| `name( string $name )` | `self` | 设置批次名称 |
| `on_queue( string $queue )` | `self` | 设置批次中所有作业的队列 |
| `then( string $handler_class )` | `self` | 批次成功完成时调用的处理程序 |
| `catch( string $handler_class )` | `self` | 批次中的首个作业失败时调用的处理程序 |
| `finally( string $handler_class )` | `self` | 批次结束时调用的处理程序（成功或失败） |
| `allow_failures()` | `self` | 即使存在失败，也允许触发 `then` 回调 |
| `dispatch()` | `Batch` | 创建批次行和所有作业，并返回 `Batch` 值对象 |

### `Queuety::find_batch( $id )`

按 ID 查找批次。返回 `Batch` 值对象或 `null`。

```php
$batch = Queuety::find_batch( $batch_id );

if ( null !== $batch ) {
    echo $batch->progress() . '% complete';
}
```

## 工作器控制

### `Queuety::run_worker( $queue = 'default', $once = false )`

直接运行一个工作器循环。

```php
Queuety::run_worker( 'default' );
Queuety::run_worker( 'critical,default', true );
```

### `Queuety::run_worker_pool( $workers, $queue = 'default' )`

直接运行固定大小的工作器池。需要 `pcntl` 和 `posix` 扩展。工作器池会读取 WordPress、`DB_HOST`、`DB_NAME`、`DB_USER` 和 `DB_PASSWORD` 常量来创建子连接（仅表前缀取自已初始化的连接），因此在 WordPress 之外运行工作器池时，请定义这些常量（[#5](https://github.com/inline0/queuety/issues/5)）。

```php
Queuety::run_worker_pool( 4, 'providers' );
```

### `Queuety::run_auto_scaling_worker_pool( $min_workers, $max_workers, $queue = 'default' )`

直接运行自适应工作器池。要求与 `run_worker_pool()` 相同。

```php
Queuety::run_auto_scaling_worker_pool( 2, 6, 'providers' );
```

### `Batch` 值对象

| 方法/属性 | 类型 | 说明 |
|---|---|---|
| `$id` | `int` | 批次 ID |
| `$name` | `string\|null` | 可选的批次名称 |
| `$total_jobs` | `int` | 批次中的作业总数 |
| `$pending_jobs` | `int` | 仍待处理的作业数 |
| `$failed_jobs` | `int` | 失败的作业数 |
| `$failed_job_ids` | `array` | 失败作业的 ID |
| `$created_at` | `DateTimeImmutable` | 批次创建时间 |
| `$finished_at` | `DateTimeImmutable\|null` | 批次完成时间 |
| `$cancelled_at` | `DateTimeImmutable\|null` | 批次取消时间 |
| `progress()` | `int` | 完成百分比（0-100） |
| `finished()` | `bool` | 批次是否已完成 |
| `has_failures()` | `bool` | 是否有作业失败 |
| `cancelled()` | `bool` | 批次是否已取消 |

### `Queuety::chain( $jobs )`

创建 `ChainBuilder` 以便按顺序执行作业。每个作业都依赖前一个作业成功完成。

```php
$first_job_id = Queuety::chain( [
    new FetchDataJob( $url ),
    new ProcessDataJob(),
    new NotifyCompleteJob(),
] )
    ->on_queue( 'pipeline' )
    ->catch( ChainFailedHandler::class )
    ->dispatch();
```

### `ChainBuilder` 方法

| 方法 | 返回值 | 说明 |
|---|---|---|
| `on_queue( string $queue )` | `self` | 设置链中所有作业的队列 |
| `catch( string $handler_class )` | `self` | 链中的作业被永久埋藏时调用的处理程序 |
| `dispatch()` | `int` | 创建所有作业并设置顺序依赖关系，返回首个作业 ID |

### `Queuety::retry( $job_id )`

重试指定作业。将其状态重置为 `pending`。

```php
Queuety::retry( 42 );
```

### `Queuety::retry_buried()`

重试所有已埋藏的作业。返回已重试的作业数。

```php
$count = Queuety::retry_buried();
```

### `Queuety::purge( $older_than_days )`

清除已完成的作业。返回已清除的作业数。

```php
$count = Queuety::purge();     // uses QUEUETY_RETENTION_DAYS
$count = Queuety::purge( 30 ); // purge jobs older than 30 days
```

## 队列

### `Queuety::stats( $queue )`

按状态分组获取作业数量。

```php
$stats = Queuety::stats();          // all queues
$stats = Queuety::stats( 'emails' ); // specific queue
// ['pending' => 12, 'processing' => 3, 'completed' => 150, 'failed' => 2, 'buried' => 1]
```

### `Queuety::buried( $queue )`

获取所有已埋藏的作业。

```php
$jobs = Queuety::buried();
$jobs = Queuety::buried( 'emails' );
```

### `Queuety::pause( $queue )`

暂停队列，使工作器跳过该队列。

```php
Queuety::pause( 'emails' );
```

### `Queuety::resume( $queue )`

恢复已暂停的队列。

```php
Queuety::resume( 'emails' );
```

### `Queuety::is_paused( $queue )`

检查队列是否已暂停。

```php
if ( Queuety::is_paused( 'emails' ) ) {
    // Queue is paused
}
```

## 工作流

### `Queuety::workflow( $name )`

开始构建工作流。返回 `WorkflowBuilder`。

```php
$workflow_id = Queuety::workflow( 'generate_report' )
    ->then( FetchDataHandler::class )
    ->then( CallLLMHandler::class )
    ->then( FormatOutputHandler::class )
    ->dispatch( [ 'user_id' => 42 ] );
```

### `WorkflowBuilder` 方法

| 方法 | 说明 |
|---|---|
| `then( string $handler_class, ?string $name = null )` | 添加顺序步骤。可选名称可用于条件分支。 |
| `parallel( array $handler_classes )` | 添加并行步骤组。 |
| `for_each( string $items_key, string $handler_class, ?string $result_key = null, ForEachMode $mode = ForEachMode::All, ?int $quorum = null, ?string $reducer_class = null, ?string $name = null )` | 添加动态 for-each 步骤，根据工作流状态中的每个项目扩展一个分支作业。 |
| `repeat_while( string $target_step, ?string $state_key = null, mixed $expected = true, ?string $name = null, ?string $condition_class = null, ?int $max_iterations = null )` | 当公共状态键或 `RepeatCondition` 类匹配时，跳回较早的命名步骤。 |
| `repeat_until( string $target_step, ?string $state_key = null, mixed $expected = true, ?string $name = null, ?string $condition_class = null, ?int $max_iterations = null )` | 直到公共状态键或 `RepeatCondition` 类匹配时，跳回较早的命名步骤。 |
| `run_workflow( string $workflow_name, WorkflowBuilder $sub_builder )` | 添加运行工作流步骤。 |
| `delay( int $seconds = 0, int $minutes = 0, int $hours = 0, int $days = 0 )` | 添加持久延迟步骤。所有时长会累加。 |
| `wait_for_signal( string $name, ?string $result_key = null, ?string $step_name = null, array $match_payload = [], ?string $correlation_key = null )` | 添加等待信号步骤。可选的 `match_payload` 和 `correlation_key` 可让你忽略无关事件。 |
| `wait_for_signals( array $signal_names, WaitMode $mode = WaitMode::All, ?string $result_key = null, ?string $name = null, array $match_payload = [], ?string $correlation_key = null )` | 等待多个信号中的全部或任意一个，可选匹配负载和关联信息。 |
| `wait_for_approval( string $signal_name = 'approval', ?string $result_key = 'approval', ?string $name = null, array $match_payload = [], ?string $correlation_key = null )` | 等待人工批准信号。 |
| `wait_for_decision( string $approve_signal = 'approved', string $reject_signal = 'rejected', ?string $result_key = 'decision', ?string $name = null, array $match_payload = [], ?string $correlation_key = null )` | 等待批准或拒绝，并存储结构化决策负载。 |
| `wait_for_input( string $signal_name = 'input', ?string $result_key = 'input', ?string $name = null, array $match_payload = [], ?string $correlation_key = null )` | 等待人工输入信号。 |
| `wait_for_workflow( int\|string $workflow, ?string $result_key = null, ?string $name = null )` | 等待一个工作流 ID，或公共状态中存储的工作流 ID，完成。 |
| `wait_for_workflows( array\|string $workflows, WaitMode $mode = WaitMode::All, ?string $result_key = null, ?string $name = null, ?int $quorum = null )` | 等待多个工作流全部完成、任意一个完成或达到法定数量。 |
| `wait_for_workflow_group( string $group_key, WaitMode $mode = WaitMode::All, ?int $quorum = null, ?string $result_key = null, ?string $name = null )` | 按内部组键等待先前启动的工作流组。 |
| `start_workflows( string $items_key, WorkflowBuilder $workflow_builder, string $result_key = 'started_workflow_ids', string $payload_key = 'item', bool $inherit_state = true, ?string $name = null, ?string $group_key = null )` | 为每个运行时项目派发一个顶层工作流，也可使用持久组键标记已启动的工作流集合。 |
| `start_agents( string $items_key, WorkflowBuilder $workflow_builder, string $result_key = 'agent_workflow_ids', string $payload_key = 'agent_task', bool $inherit_state = true, ?string $name = null, ?string $group_key = null )` | 面向代理的 `start_workflows()` 别名。 |
| `wait_for_agents( array\|string $workflows = 'agent_workflow_ids', WaitMode $mode = WaitMode::All, ?string $result_key = 'agent_results', ?string $name = null, ?int $quorum = null )` | 面向代理的 `wait_for_workflows()` 别名。 |
| `wait_for_agent_group( string $group_key = 'agents', WaitMode $mode = WaitMode::All, ?int $quorum = null, ?string $result_key = 'agent_results', ?string $name = null )` | 面向代理的 `wait_for_workflow_group()` 别名。 |
| `version( string $version )` | 为工作流定义标记应用级版本，以便检查和导出。 |
| `idempotency_key( string $key )` | 使用调用方提供的键使工作流派发持久化。使用相同键再次派发时会返回原工作流 ID。 |
| `max_transitions( int $max )` | 如果完成的步骤转换超过 `$max`，则使工作流失败。 |
| `max_for_each_items( int $max )` | 如果 `for_each()` 步骤尝试展开超过 `$max` 个项目，则使工作流失败。 |
| `max_state_bytes( int $bytes )` | 如果公共状态超过 `$bytes`，则使工作流失败。 |
| `max_cost_units( int $max )` | 如果已完成步骤消耗的成本单位超过 `$max`，则使工作流失败。 |
| `max_started_workflows( int $max )` | 如果要派发的子工作流超过 `$max`，则使工作流失败。 |
| `compensate_with( string $handler_class )` | 为最近添加的步骤附加补偿处理程序。 |
| `compensate_on_failure()` | 工作流失败时自动运行已完成步骤的补偿操作。 |
| `on_queue( string $queue )` | 设置所有步骤使用的队列。 |
| `with_priority( Priority $priority )` | 设置所有步骤的优先级。 |
| `max_attempts( int $max )` | 设置每个步骤的最大重试次数。 |
| `on_cancel( string $handler_class )` | 注册用于取消操作的清理处理程序类。参阅[取消](https://queuety.inline0.dev/zh/docs/workflows/cancellation)。 |
| `prune_state_after( int $steps = 2 )` | 启用自动状态裁剪。移除早于阈值的步骤输出。参阅[状态裁剪](https://queuety.inline0.dev/zh/docs/features/state-pruning)。 |
| `must_complete_within( int $seconds = 0, int $minutes = 0, int $hours = 0, int $days = 0 )` | 设置工作流的截止时间。所有时长会累加。参阅[截止时间](https://queuety.inline0.dev/zh/docs/workflows/deadlines)。 |
| `on_deadline( string $handler_class )` | 注册工作流超过截止时间时调用的处理程序。参阅[截止时间](https://queuety.inline0.dev/zh/docs/workflows/deadlines)。 |
| `dispatch( array $initial_payload = [] )` | 派发工作流。返回工作流 ID。 |

### `Queuety::workflow_status( $workflow_id )`

获取工作流状态。返回 `WorkflowState` 对象或 `null`。

```php
$state = Queuety::workflow_status( $workflow_id );
echo $state->name;          // 'generate_report'
echo $state->status->value; // 'running'
echo $state->current_step;  // 1
echo $state->total_steps;   // 3
print_r( $state->state );   // accumulated public state array
echo $state->wait_type;     // 'signal' or 'workflow' while blocked
echo $state->wait_mode;     // 'all', 'any', or 'quorum'
echo $state->definition_version; // 'research.v2'
echo $state->definition_hash;    // '4c7a...'
echo $state->idempotency_key;    // 'brief:42:research'
echo $state->artifact_count;     // 2
print_r( $state->budget );       // configured workflow budget + current usage
print_r( $state->wait_details ); // matched/remaining blockers, result key, correlation info
print_r( $state->artifact_keys ); // ['draft', 'citations']
```

`WorkflowState` 还公开了以下内容：

- `waiting_for`：当前阻止运行的信号名称或工作流 ID
- `current_step_name`：当前步骤配置的名称（如有）
- `wait_mode`：阻塞期间的当前等待模式（`all`、`any` 或 `quorum`）
- `wait_details`：额外的检查数据，例如匹配的阻塞项与剩余阻塞项
- `definition_version`：由 `version()` 设置的值（如有）
- `definition_hash`：工作流定义的确定性哈希值
- `idempotency_key`：持久化调度键（如有）
- `budget`：配置的限制及当前用量（启用保护措施时）
- `artifact_count`：工作流存储的工件数量
- `artifact_keys`：存储的工件键，便于快速检查

### `Queuety::retry_workflow( $workflow_id )`

从失败的步骤重试失败的工作流。

```php
Queuety::retry_workflow( $workflow_id );
```

如果工作流在失败后已执行补偿，则抛出 `RuntimeException`。

### `Queuety::pause_workflow( $workflow_id )`

暂停正在运行的工作流。当前步骤已排队的工作仍可能完成，但在恢复工作流之前，不会将后续步骤加入队列。

```php
Queuety::pause_workflow( $workflow_id );
```

### `Queuety::resume_workflow( $workflow_id )`

恢复已暂停的工作流。

```php
Queuety::resume_workflow( $workflow_id );
```

### `Queuety::cancel_workflow( $workflow_id )`

取消工作流并运行所有已注册的清理处理程序。将状态设为 `cancelled`，并将该工作流所有待处理和正在处理的作业移入存档。

```php
Queuety::cancel_workflow( $workflow_id );
```

如果工作流已处于 `completed` 或 `cancelled` 状态，则抛出 `RuntimeException`。详情请参阅 [取消](https://queuety.inline0.dev/zh/docs/workflows/cancellation)。

### `Queuety::rewind_workflow( $workflow_id, $to_step )`

将工作流回退到之前的步骤，并从该处重新运行。给定步骤的状态会从事件日志 `state_after` 中恢复，然后将下一步加入队列。

```php
Queuety::rewind_workflow( $workflow_id, to_step: 1 );
```

如果目标步骤没有记录的状态，则抛出 `RuntimeException`。需要使用 [工作流事件日志](https://queuety.inline0.dev/zh/docs/workflows/event-log)。详情请参阅 [时间回溯](https://queuety.inline0.dev/zh/docs/workflows/time-travel)。

### `Queuety::fork_workflow( $workflow_id )`

将正在运行的工作流按当前状态分叉为一个独立副本。返回新工作流 ID。

```php
$forked_id = Queuety::fork_workflow( $workflow_id );
```

分叉后的工作流拥有自己的 ID，且独立于原工作流运行。详情请参阅 [分叉](https://queuety.inline0.dev/zh/docs/workflows/forking)。

### `Queuety::export_workflow( $workflow_id )`

将工作流的完整执行历史导出为可由 JSON 序列化的数组。其中包含工作流行、所有作业、工作流事件和日志条目。

```php
$data = Queuety::export_workflow( $workflow_id );
```

有关 JSON 结构和使用场景，请参阅 [导出与重放](https://queuety.inline0.dev/zh/docs/workflows/export-replay)。

### `Queuety::replay_workflow( $data )`

在当前环境中重放已导出的工作流。根据导出数据创建一个新工作流，并返回新工作流 ID。

```php
$new_id = Queuety::replay_workflow( $data );
```

详情请参阅 [导出与重放](https://queuety.inline0.dev/zh/docs/workflows/export-replay)。

### `Queuety::replace_wp_cron()`

将内置 WordPress cron 系统替换为 Queuety 的调度器。拦截 `wp_schedule_event()`、`wp_unschedule_event()` 和 `wp_get_scheduled_event()`，并通过 Queuety 工作进程路由定期事件。

```php
Queuety::replace_wp_cron();
```

仅在加载了 WordPress 时有效。详情请参阅 [WP-Cron替换](https://queuety.inline0.dev/zh/docs/features/wp-cron)。

### `Queuety::restore_wp_cron()`

移除 Queuety cron 桥接钩子，以恢复 WordPress 默认的 cron 行为。

```php
Queuety::restore_wp_cron();
```

有关限制，请参阅 [WP-Cron替换](https://queuety.inline0.dev/zh/docs/features/wp-cron)。

### `Queuety::signal( $workflow_id, $name, $data )`

向工作流发送信号。如果工作流当前正在等待此信号，它会立即恢复运行。否则，信号会被存储起来，并在工作流到达对应的 `wait_for_signal` 步骤时获取。

```php
Queuety::signal( $workflow_id, 'approved' );

// With data merged into workflow state
Queuety::signal( $workflow_id, 'approved', [
    'approved_by' => 'admin@example.com',
] );
```

详情请参阅 [工作流信号](https://queuety.inline0.dev/zh/docs/workflows/signals)。

### `Queuety::approve_workflow( $workflow_id, $data = [], $signal_name = 'approval' )`

用于审批流程的语义包装器，包装 `signal()`。

```php
Queuety::approve_workflow( $workflow_id, [
    'reviewer' => 'editor@example.com',
] );
```

### `Queuety::reject_workflow( $workflow_id, $data = [], $signal_name = 'rejected' )`

用于拒绝流程的语义包装器，包装 `signal()`。

```php
Queuety::reject_workflow( $workflow_id, [
    'reason' => 'needs citations',
] );
```

### `Queuety::submit_workflow_input( $workflow_id, $data = [], $signal_name = 'input' )`

用于结构化人工输入的语义包装器，包装 `signal()`。

```php
Queuety::submit_workflow_input( $workflow_id, [
    'notes' => 'Ship after legal review',
] );
```

### `Queuety::put_artifact( $workflow_id, $artifact_key, $content, $kind = 'json', $step_index = null, $metadata = [] )`

为工作流存储一个持久化工件。

```php
Queuety::put_artifact(
    $workflow_id,
    'research_brief',
    [ 'summary' => 'done' ],
    'json',
    metadata: [ 'source' => 'agent' ],
);
```

### `Queuety::put_current_artifact( $artifact_key, $content, $kind = 'json', $metadata = [] )`

为当前正在执行的工作流步骤存储一个工件。

```php
Queuety::put_current_artifact(
    'draft',
    [ 'status' => 'ready' ],
    'json',
);
```

### `Queuety::workflow_artifact( $workflow_id, $artifact_key )`

获取工作流中存储的一个工件。

```php
$artifact = Queuety::workflow_artifact( $workflow_id, 'draft' );
```

### `Queuety::workflow_artifacts( $workflow_id, $include_content = false )`

列出工作流的工件。

```php
$artifacts = Queuety::workflow_artifacts( $workflow_id );
```

### `Queuety::delete_workflow_artifact( $workflow_id, $artifact_key )`

删除工作流中的一个工件。

```php
Queuety::delete_workflow_artifact( $workflow_id, 'draft' );
```

## 状态机

### `Queuety::machine( $name )`

开始构建状态机。返回一个 `StateMachineBuilder`。

```php
use Queuety\Enums\StateMachineStatus;

$machine_id = Queuety::machine( 'agent_session' )
    ->state( 'awaiting_user' )
    ->on( 'user_message', 'planning' )
    ->state( 'planning' )
    ->action( PlanSessionAction::class )
    ->on( 'planned', 'completed' )
    ->state( 'completed', StateMachineStatus::Completed )
    ->dispatch( [ 'thread_id' => 42 ] );
```

### `StateMachineBuilder` 方法

| 方法 | 说明 |
|---|---|
| `initial( string $state_name )` | 显式设置初始状态。 |
| `state( string $state_name, ?StateMachineStatus $terminal_status = null )` | 开始配置一个状态。 |
| `action( string\|array $action_class )` | 为当前状态注册一个排队的进入动作。传入类名，或包含 `class`、`payload` 和 `config` 的结构化定义；`payload` 会以 `$payload` 的形式传递给 `StateAction::handle()`。 |
| `on( string $event, string $target, string\|array\|null $guard_class = null, ?string $name = null )` | 为当前状态注册一个事件转换。守卫接受与 `action()` 相同的结构化形式。 |
| `on_queue( string $queue )` | 设置用于排队状态动作的队列。 |
| `with_priority( Priority $priority )` | 设置排队状态动作的优先级。 |
| `max_attempts( int $max_attempts )` | 设置排队状态动作的重试限制。 |
| `version( string $version )` | 为状态机定义添加应用级版本标签。 |
| `idempotency_key( string $key )` | 使用调用方提供的键，使调度具有持久性。使用相同的键再次调度时，会返回原状态机 ID。 |
| `dispatch( array $initial_state = [] )` | 调度状态机并返回状态机 ID。 |

状态进入动作类还可以提供可选的 `config()` 方法，其资源策略键与工作流步骤支持的相同：`concurrency_group`、`concurrency_limit` 和 `cost_units`。

### `Queuety::dispatch_state_machine_definition( $definition, $initial_state = [], $options = [] )`

调度序列化的状态机定义包。大多数应用代码应优先使用 `machine()->dispatch()`。此低级方法供适配器使用，使其无需直接依赖构建器即可启动状态机。

```php
$machine_id = Queuety::dispatch_state_machine_definition(
    $definition,
    [ 'thread_id' => 42 ],
    [ 'idempotency_key' => 'thread:42' ],
);
```

### `Queuety::machine_status( $machine_id )`

获取当前状态机状态。返回一个 `StateMachineState` 对象或 `null`。

```php
$status = Queuety::machine_status( $machine_id );

echo $status->name;               // 'agent_session'
echo $status->status->value;      // 'waiting_event'
echo $status->current_state;      // 'awaiting_review'
print_r( $status->state );        // public machine state
print_r( $status->available_events ); // valid incoming events in the current state
```

`StateMachineState` 还提供：

- `definition_version`
- `definition_hash`
- `idempotency_key`
- `error_message`
- `current_action`
- `terminal_status`

### `Queuety::machine_event( $machine_id, $event_name, $payload = [] )`

向等待中的状态机发送一个外部事件。

```php
Queuety::machine_event(
    $machine_id,
    'approve',
    [
        'approved' => true,
        'reviewer' => 'editor@example.com',
    ]
);
```

载荷可供转换守卫使用；事件被接受后，载荷会合并到公开的状态机状态中。

### `Queuety::list_machines( $limit = 50, $status = null )`

列出已持久化的状态机行以供检查。

```php
$machines = Queuety::list_machines();
$waiting  = Queuety::list_machines( status: 'waiting_event' );
```

每行包含状态机 id、名称、状态、当前状态、定义版本、定义哈希值、幂等键和生命周期时间戳。

### `Queuety::machine_timeline( $machine_id, $limit = 100, $offset = 0 )`

获取一个状态机的持久化事件时间线。

```php
$timeline = Queuety::machine_timeline( $machine_id );
$page_two = Queuety::machine_timeline( $machine_id, 100, 100 );
```

每个事件行都包含：`id`、`machine_id`、`job_id`、`state_name`、`handler`、`event`、`event_name`、`transition_name`、`queue`、`attempt`、`input`、`output`、`state_before`、`state_after`、`context`、`artifacts`、`chunks`、`error`、`duration_ms`、`created_at`。

### `Queuety::machine_trace( $machine_id )`

获取供状态机调试器使用的标准化跟踪包 UI。

```php
$trace = Queuety::machine_trace( $machine_id );
```

跟踪包含 `machine`、分组后的 `states`、原始 `events`、关联的动作 `jobs`、`logs`、`artifacts` 和 `chunks`。

### `Queuety::machine_state_at( $machine_id, $event_id )`

获取特定状态机跟踪事件后的公开状态；如果未找到，则返回 `null`。

```php
$state = Queuety::machine_state_at( $machine_id, $event_id );
```

有关生命周期模型和使用模式，请参阅 [状态机](https://queuety.inline0.dev/zh/docs/state-machines)。

## 工作流模板

### `Queuety::define_workflow( $name )`

创建一个工作流构建器，用作模板。

```php
$builder = Queuety::define_workflow( 'onboarding' )
    ->then( CreateAccountHandler::class )
    ->then( SendWelcomeEmailHandler::class );
```

### `Queuety::register_workflow_template( $builder )`

注册工作流模板。

```php
Queuety::register_workflow_template( $builder );
```

### `Queuety::run_workflow( $name, $payload, $options = [] )`

按名称调度已注册的工作流模板。返回工作流 ID。

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

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

如果模板尚未注册，则抛出 `RuntimeException`。

### `Queuety::dispatch_workflow_definition( $definition, $payload, $options = [] )`

根据运行时定义包调度工作流。

大多数应用代码应优先使用 `run_workflow()` 或 `workflow()->dispatch()`。此低级方法供可复用适配器使用，使其无需依赖内部工作流管理器方法即可调度序列化的工作流定义。

```php
$workflow_id = Queuety::dispatch_workflow_definition(
    $definition,
    [ 'email' => 'user@example.com' ],
    [ 'idempotency_key' => 'onboarding:user@example.com' ],
);
```

### `Queuety::on_action( $hook, $workflow, $map = null, $when = null, $idempotency_key = null, $priority = 10, $accepted_args = null )`

注册一个用于调度工作流的 WordPress 动作。

```php
Queuety::on_action(
    'save_post',
    workflow: 'content_review',
    map: static fn ( int $post_id, object $post, bool $update ): array => [
        'post_id' => $post_id,
        'post_type' => $post->post_type,
        'update' => $update,
    ],
    when: static fn ( int $post_id, object $post ): bool => 'post' === $post->post_type,
    idempotency_key: static fn ( int $post_id ): string => "save_post:{$post_id}",
);
```

`$workflow` 可以是以下任一项：

- 已注册的工作流模板名称
- 内联的 `WorkflowBuilder`

`map`、`when` 和 `idempotency_key` 会在当前 WordPress 请求中同步运行。只有工作流步骤本身会在调度后异步运行。

完整指南请参阅 [动作触发器](https://queuety.inline0.dev/zh/docs/features/action-triggers)。

### `Queuety::workflow_templates()`

获取工作流模板注册表。

```php
$registry = Queuety::workflow_templates();
$template = $registry->get( 'onboarding' ); // WorkflowTemplate or null
```

### `Queuety::load_workflows( $directory, $recursive )`

从目录中加载并注册所有工作流文件。每个 `.php` 文件都应返回一个 `WorkflowBuilder`。返回已注册的工作流数量。

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

### `Queuety::load_workflow_file( $file_path )`

加载并注册单个工作流文件。如果文件未返回构建器，则返回 `WorkflowTemplate` 或 `null`。

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

## 调度

### `Queuety::schedule( $handler, $payload )`

创建定期调度。返回一个 `PendingSchedule` 构建器。

```php
Queuety::schedule( 'cleanup_handler' )
    ->every( '1 hour' )
    ->on_queue( 'maintenance' );

Queuety::schedule( 'nightly_report' )
    ->cron( '0 3 * * *' )
    ->on_queue( 'reports' );
```

### `PendingSchedule` 方法

| 方法 | 说明 |
|---|---|
| `every( string $interval )` | 设置间隔表达式（e.g、`1 hour`、`30 minutes`） |
| `cron( string $expression )` | 设置 5 字段的 cron 表达式 |
| `overlap( OverlapPolicy $policy )` | 设置重叠策略（默认值：`Allow`）。请参阅 [调度](https://queuety.inline0.dev/zh/docs/features/scheduling)。 |
| `on_queue( string $queue )` | 设置目标队列（默认值：`default`） |
| `id()` | 强制调度并返回调度 ID |

### `Queuety::scheduler()`

获取内部的 `Scheduler` 实例。

```php
$scheduler = Queuety::scheduler();
$schedules = $scheduler->list();
$scheduler->remove( 'handler_name' );
$count = $scheduler->tick(); // manually trigger
```

## 处理程序注册

### `Queuety::register( $name, $class )`

按名称注册处理程序类。

```php
Queuety::register( 'send_email', SendEmailHandler::class );
```

### `Queuety::discover_handlers( $directory, $namespace )`

从目录中自动发现并注册处理程序类。返回已注册的处理程序数量。

```php
$count = Queuety::discover_handlers( __DIR__ . '/handlers', 'MyPlugin\\Handlers' );
```

### `Queuety::registry()`

获取处理程序注册表。

```php
$registry = Queuety::registry();
```

## 可观测性

### `Queuety::logger()`

获取用于查询日志条目的 `Logger` 实例。

```php
$logger = Queuety::logger();
$logs   = $logger->for_job( 42 );
$logs   = $logger->for_workflow( 7 );
$logs   = $logger->for_handler( 'send_email', 50 );
$logs   = $logger->for_event( LogEvent::Failed, 50 );
$logs   = $logger->since( new \DateTimeImmutable( '-1 hour' ), 100 );
$count  = $logger->purge( 30 );
```

### `Queuety::metrics()`

获取 `Metrics` 实例。

```php
$stats = Queuety::metrics()->handler_stats( 60 ); // last 60 minutes
```

### `Queuety::webhook_notifier()`

获取 `WebhookNotifier` 实例。

```php
$notifier = Queuety::webhook_notifier();
$id       = $notifier->register( 'job.buried', 'https://hooks.slack.com/...' );
$webhooks = $notifier->list();
$notifier->remove( $id );
```

### `Queuety::rate_limiter()`

获取 `RateLimiter` 实例。

```php
$limiter = Queuety::rate_limiter();
$limiter->register( 'call_openai', 60, 60 );
```

## 缓存

### `Queuety::cache()`

获取当前活动的缓存后端实例。

```php
$cache = Queuety::cache();
$cache->set( 'my_key', 'my_value', 60 );
$value = $cache->get( 'my_key' );
```

### `Queuety::set_cache( $cache )`

覆盖缓存后端。若要使用自定义实现，请在调用 `Queuety::init()` 之前执行此操作。

```php
Queuety::set_cache( new RedisCache() );
Queuety::init( $connection );
```

### `QUEUETY_CACHE_TTL`

默认缓存 TTL（秒）（默认值：`5`）。在 `wp-config.php` 中设置此常量，可更改内部缓存操作使用的默认 TTL。

```php
define( 'QUEUETY_CACHE_TTL', 10 );
```

有关后端和自动检测的详情，请参阅 [缓存](https://queuety.inline0.dev/zh/docs/features/caching)。

## 状态机事件日志

### `Queuety::machine_events()`

获取 `StateMachineEventLog` 实例。

```php
$log = Queuety::machine_events();
```

## 工作流事件日志

### `Queuety::workflow_events()`

获取 `WorkflowEventLog` 实例。

```php
$log = Queuety::workflow_events();
```

### `Queuety::workflow_timeline( $workflow_id, $limit = 100, $offset = 0 )`

获取工作流的完整事件时间线。返回按 ID 排序的事件行数组。

```php
$events = Queuety::workflow_timeline( $workflow_id );
$next   = Queuety::workflow_timeline( $workflow_id, 100, 100 );
```

每个事件行都包含：`id`、`workflow_id`、`job_id`、`parent_event_id`、`step_index`、`step_name`、`step_type`、`handler`、`event`、`queue`、`attempt`、`input`、`output`、`state_before`、`state_after`、`context`、`artifacts`、`chunks`、`error`、`error_message`（从 `error.message` 复制而来）、`duration_ms` 和 `created_at`。

### `Queuety::workflow_trace( $workflow_id )`

获取供工作流调试器使用的标准化跟踪包 UI。

```php
$trace = Queuety::workflow_trace( $workflow_id );
```

跟踪包含 `workflow`、分组后的 `steps`、原始 `events`、`jobs`、`logs`、`artifacts`、`chunks`、`signals` 和 `wait_dependencies`。

### `Queuety::trace_input( $input )`

为当前正在执行的工作流步骤设置应用级输入。

### `Queuety::trace_output( $output )`

为当前正在执行的工作流步骤设置应用级输出。

### `Queuety::trace_context( array $context )`

将应用级上下文合并到当前正在执行的工作流步骤中。

### `Queuety::workflow_state_at( $workflow_id, $step_index )`

获取特定工作流步骤完成或恢复运行后的公开状态；如果未找到，则返回 `null`。

```php
$state = Queuety::workflow_state_at( $workflow_id, 2 );
```

有关使用场景和 CLI 命令，请参阅 [工作流事件日志](https://queuety.inline0.dev/zh/docs/workflows/event-log)。

## 内部实例

这些方法会返回供高级使用场景使用的内部实例。

| 方法 | 返回值 | 说明 |
|---|---|---|
| `Queuety::queue()` | `Queue` | 队列操作（认领、释放、归档） |
| `Queuety::worker()` | `Worker` | 工作进程控制 |
| `Queuety::workflow_manager()` | `Workflow` | 工作流编排 |
| `Queuety::machines()` | `StateMachine` | 状态机生命周期管理 |
| `Queuety::connection()` | `Connection` | 数据库连接 |
| `Queuety::batch_manager()` | `BatchManager` | 批处理生命周期管理（取消、清理） |
| `Queuety::chunk_store()` | `ChunkStore` | 流式处理步骤的分块持久化 |
| `Queuety::machine_events()` | `StateMachineEventLog` | 状态机跟踪事件和标准化跟踪包 |
| `Queuety::workflow_events()` | `WorkflowEventLog` | 工作流跟踪事件和标准化跟踪包 |
| `Queuety::resource_manager()` | `ResourceManager` | 工作进程准入配置、加权预算和系统内存检查 |
| `Queuety::cache()` | `Cache` | 缓存后端实例 |

## 测试

### `Queuety::fake()`

用内存中的伪对象替换队列以进行测试。返回一个 `QueueFake` 实例，该实例会记录已调度的作业，而不访问数据库。`create_batch()` 还会记录伪批处理元数据；链式调用通过其调度的各个已推送作业进行断言。

```php
$fake = Queuety::fake();
SendEmailJob::dispatch( 'user@example.com', 'Hello', 'Hi!' );
$fake->assert_pushed( SendEmailJob::class );
```

### `Queuety::reset()`

重置所有内部状态，包括队列伪对象。在 `tearDown()` 中调用。

```php
Queuety::reset();
```

### `QueueFake` 方法

| 方法 | 说明 |
|---|---|
| `assert_pushed( string $class, ?Closure $callback )` | 断言已推送某个作业类。可选回调会根据作业数据进行筛选。 |
| `assert_pushed_times( string $class, int $count )` | 断言某个作业恰好推送了 N 次。 |
| `assert_not_pushed( string $class )` | 断言某个作业从未被推送。 |
| `assert_nothing_pushed()` | 断言没有推送任何作业。 |
| `assert_batched( ?Closure $callback )` | 断言已调度一个批处理。可选回调会根据批处理数据进行筛选。 |
| `pushed( string $class )` | 获取某个类的所有已记录调度。返回一个数组。 |
| `batches()` | 获取所有已记录的批处理。返回一个数组。 |
| `reset()` | 清除所有已记录的作业和批处理。 |

完整示例请参阅 [测试](https://queuety.inline0.dev/zh/docs/features/testing)。

## 接口

### `Handler`

简单作业处理程序的接口。

```php
interface Handler {
    public function handle( array $payload ): void;
    public function config(): array;
}
```

`config()` 可以返回 `queue`、`max_attempts`、`backoff`、`rate_limit`、`concurrency_group`、`concurrency_limit` 和 `cost_units`。

### `Step`

工作流步骤处理程序的接口。

```php
interface Step {
    public function handle( array $state ): array;
    public function config(): array;
}
```

`config()` 可以返回 `max_attempts`、`backoff`、`concurrency_group`、`concurrency_limit` 和 `cost_units`。

### `Contracts\ForEachHandler`

动态 for-each 分支处理程序的接口。

```php
namespace Queuety\Contracts;

interface ForEachHandler {
    public function handle_item( array $state, mixed $item, int $index ): array;
    public function config(): array;
}
```

`config()` 可以返回 `max_attempts`、`backoff`、`rate_limit`、`concurrency_group`、`concurrency_limit` 和 `cost_units`。

### `Contracts\ForEachReducer`

for-each 结果的可选归约器。

```php
namespace Queuety\Contracts;

interface ForEachReducer {
    public function reduce( array $state, array $for_each ): array;
}
```

### `Contracts\Compensation`

步骤补偿处理程序的接口。

```php
namespace Queuety\Contracts;

interface Compensation {
    public function handle( array $state ): void;
}
```

### `Contracts\RepeatCondition`

当状态键比较过于有限时，供 `repeat_until()` 和 `repeat_while()` 使用的循环谓词接口。

```php
namespace Queuety\Contracts;

interface RepeatCondition {
    public function matches( array $state ): bool;
}
```

### `Contracts\StateAction`

排队状态进入动作的接口。

```php
namespace Queuety\Contracts;

interface StateAction {
    public function handle( array $state, ?string $event = null, array $event_payload = [], array $payload = [] ): array|string;
}
```

如果状态机定义中包含结构化的 `payload`，`$payload` 会携带该定义。返回公开状态更新。如果动作应立即转换状态，请包含 `_event`，并可选地包含 `_event_payload`。

状态动作还可以定义一个可选的 `config()` 方法。进入动作排队时，Queuety 会从该方法读取 `concurrency_group`、`concurrency_limit` 和 `cost_units`。

### `Contracts\StateGuard`

状态机转换守卫的接口。

```php
namespace Queuety\Contracts;

interface StateGuard {
    public function allows( array $state, array $event_payload, string $event, array $payload = [] ): bool;
}
```

### `Contracts\Job`

自包含、可调度作业类的接口。作业类将载荷封装为公共属性，并将执行逻辑放在 `handle()` 中。

```php
namespace Queuety\Contracts;

interface Job {
    public function handle(): void;
}
```

作业类可以选择定义一个 `middleware()` 方法，该方法返回一个包含多个 `Contracts\Middleware` 实例的数组。

### `Contracts\Cache`

缓存后端的接口。有关内置实现和自定义后端，请参阅 [缓存](https://queuety.inline0.dev/zh/docs/features/caching)。

```php
namespace Queuety\Contracts;

interface Cache {
    public function get( string $key ): mixed;
    public function set( string $key, mixed $value, int $ttl = 0 ): void;
    public function delete( string $key ): void;
    public function has( string $key ): bool;
    public function add( string $key, mixed $value, int $ttl = 0 ): bool;
    public function flush(): void;
}
```

| 方法 | 返回值 | 说明 |
|---|---|---|
| `get( $key )` | `mixed` | 获取一个值。如果未找到或已过期，则返回 `null`。 |
| `set( $key, $value, $ttl )` | `void` | 存储一个值。TTL 为 0 表示永不过期。 |
| `delete( $key )` | `void` | 移除一个值。 |
| `has( $key )` | `bool` | 检查键是否存在且未过期。 |
| `add( $key, $value, $ttl )` | `bool` | 原子性的“仅当键不存在时设置”。如果设置了该值，则返回 `true`。 |
| `flush()` | `void` | 从缓存中移除所有项目。 |

### `WorkflowEventLog`

记录工作流步骤转换及完整的跟踪上下文。通过 `Queuety::workflow_events()` 访问。

| 方法 | 返回值 | 说明 |
|---|---|---|
| `record_event( array $event )` | `void` | 记录原始跟踪事件 |
| `record_step_started( ... )` | `void` | 记录 step_started 事件，以及执行前的输入和状态 |
| `record_step_completed( ... )` | `void` | 记录 step_completed 事件，以及输入、输出、执行前状态和执行后状态 |
| `record_step_failed( ... )` | `void` | 记录 step_failed 事件，以及跟踪上下文和结构化错误详情 |
| `get_timeline( int $workflow_id, ?int $limit = 100, int $offset = 0 )` | `array` | 按顺序获取工作流跟踪事件，可选分页 |
| `get_trace( int $workflow_id )` | `array` | 获取标准化跟踪包 |
| `get_state_at_step( int $workflow_id, int $step_index )` | `array\|null` | 获取特定步骤完成或恢复运行后的公开状态 |
| `prune( int $older_than_days )` | `int` | 删除早于指定天数的事件。返回删除的行数。 |

### `StateMachineEventLog`

记录状态机生命周期、动作、转换和守卫事件，所用的跟踪字段与工作流事件相同。

| 方法 | 返回值 | 说明 |
|---|---|---|
| `record_event( array $event )` | `void` | 记录一条原始机器跟踪事件 |
| `get_timeline( int $machine_id, ?int $limit = 100, int $offset = 0 )` | `array` | 按顺序获取机器跟踪事件，可选择分页 |
| `get_trace( int $machine_id )` | `array` | 获取规范化的机器跟踪数据包 |
| `get_state_at_event( int $machine_id, int $event_id )` | `array\|null` | 获取特定机器跟踪事件之后的公开状态 |
| `prune( int $older_than_days )` | `int` | 删除早于指定天数的事件。返回已删除的行数。 |

### `Contracts\StreamingStep`

用于生成数据块流的工作流步骤处理器接口。每个生成的值都会立即持久化到数据库。请参阅 [流式步骤](https://queuety.inline0.dev/zh/docs/workflows/streaming)。

```php
namespace Queuety\Contracts;

interface StreamingStep {
    public function stream( array $state, array $existing_chunks = [] ): \Generator;
    public function on_complete( array $chunks, array $state ): array;
    public function config(): array;
}
```

| 方法 | 说明 |
|---|---|
| `stream( $state, $existing_chunks )` | 生成字符串数据块。每次生成都会立即写入数据库。重试时，`$existing_chunks` 包含之前已持久化的数据块。 |
| `on_complete( $chunks, $state )` | 流结束后调用。返回要合并到工作流状态中的数据。 |
| `config()` | 可选配置。支持 `max_attempts`、`backoff`、`concurrency_group`、`concurrency_limit` 和 `cost_units`。 |

### `ChunkStore`

管理流式步骤的数据块持久化。通过 `Queuety::chunk_store()` 访问。

| 方法 | 返回值 | 说明 |
|---|---|---|
| `get_chunks( int $job_id )` | `string[]` | 获取某个作业的所有数据块，并按数据块索引排序 |
| `append_chunk( int $job_id, int $chunk_index, string $content, ?int $workflow_id, ?int $step_index )` | `void` | 追加单个数据块 |
| `clear_chunks( int $job_id )` | `void` | 删除某个作业的所有数据块 |
| `chunk_count( int $job_id )` | `int` | 统计某个作业的数据块数量 |
| `get_accumulated( int $job_id )` | `string` | 将所有数据块拼接成一个字符串 |

### `Heartbeat`

用于在步骤处理器和流式步骤中发送活动心跳的静态辅助类。请参阅 [心跳](https://queuety.inline0.dev/zh/docs/features/heartbeats)。

| 方法 | 说明 |
|---|---|
| `Heartbeat::init( int $job_id, Connection $conn )` | 设置作业上下文。由工作进程自动调用。 |
| `Heartbeat::beat( array $progress = [] )` | 发送心跳。更新 `reserved_at`，并可选择将进度数据存储在 `heartbeat_data` 中。 |
| `Heartbeat::clear()` | 清除作业上下文。由工作进程自动调用。 |
| `Heartbeat::current_job_id()` | 获取当前作业 ID（用于测试）。 |

### `Contracts\Middleware`

作业中间件接口。中间件以洋葱式管道包装作业执行过程。

```php
namespace Queuety\Contracts;

interface Middleware {
    public function handle( object $job, \Closure $next ): void;
}
```

## 特征

### `Dispatchable`

在实现 `Contracts\Job` 的作业类上提供静态分发方法。构造函数参数会传入以创建作业实例。

```php
use Queuety\Contracts\Job;
use Queuety\Dispatchable;

class SendEmailJob implements Job {
    use Dispatchable;
    // ...
}

// Dispatches a new SendEmailJob instance
SendEmailJob::dispatch( 'user@example.com', 'Subject', 'Body' );
```

| 方法 | 返回值 | 说明 |
|---|---|---|
| `dispatch( ...$args )` | `PendingJob` | 创建实例并分发作业 |
| `dispatch_if( bool $condition, ...$args )` | `PendingJob\|null` | 仅当 `$condition` 为 true 时分发作业 |
| `dispatch_unless( bool $condition, ...$args )` | `PendingJob\|null` | 仅当 `$condition` 为 false 时分发作业 |
| `with_chain( array $jobs )` | `ChainBuilder` | 从此作业类启动一个链 |

```php
// Conditional dispatch
SendEmailJob::dispatch_if( $user->wants_email, $user->email, 'Hello', 'Hi!' );
SendEmailJob::dispatch_unless( $maintenance_mode, $user->email, 'Hello', 'Hi!' );

// Chain from a job class
FetchDataJob::with_chain( [
    new ProcessDataJob(),
    new NotifyCompleteJob(),
] )->dispatch();
```

### 作业属性

作业类可以声明公共属性，工作进程会通过反射读取这些属性，以覆盖重试和超时行为。

| 属性 | 类型 | 说明 |
|---|---|---|
| `$tries` | `int` | 作业被搁置前允许尝试的最大次数 |
| `$timeout` | `int` | 最大执行时间，单位为秒（需要 `pcntl`） |
| `$backoff` | `array` | 每次重试的递增延迟值，单位为秒 |

```php
class ImportProductsJob implements Job {
    use Dispatchable;

    public int $tries = 5;
    public int $timeout = 120;
    public array $backoff = [ 10, 30, 60, 120 ];

    // ...
}
```

### `failed()` 钩子

作业耗尽所有重试次数而被搁置后，如果作业实例定义了 `failed()` 方法，工作进程就会调用该方法。

```php
public function failed( \Throwable $exception ): void {
    error_log( "Job failed: {$exception->getMessage()}" );
}
```

## 内置中间件

所有中间件类都实现 `Contracts\Middleware`，并位于 `Queuety\Middleware` 命名空间中。

| 类 | 说明 |
|---|---|
| `RateLimited( int $max, int $window )` | 限制速率：每 `$window` 秒最多执行 `$max` 次 |
| `Timeout( int $seconds )` | `$seconds` 秒后终止作业（需要 `pcntl`） |
| `UniqueJob( ?string $key )` | 通过数据库锁防止并发执行 |
| `WithoutOverlapping( string $key, int $release_after )` | 通过自动过期的锁防止执行重叠 |
| `ThrottlesExceptions( int $max_attempts, int $decay_minutes )` | 外部服务宕机时限制异常频率，防止作业激增 |

有关用法和示例，请参阅 [中间件](https://queuety.inline0.dev/zh/docs/features/middleware)。

## 枚举

### `Priority`

```php
enum Priority: int {
    case Low    = 0;
    case Normal = 1;
    case High   = 2;
    case Urgent = 3;
}
```

### `JobStatus`

```php
enum JobStatus: string {
    case Pending    = 'pending';
    case Processing = 'processing';
    case Completed  = 'completed';
    case Failed     = 'failed';
    case Buried     = 'buried';
}
```

### `WorkflowStatus`

```php
enum WorkflowStatus: string {
    case Running        = 'running';
    case Completed      = 'completed';
    case Failed         = 'failed';
    case Paused         = 'paused';
    case WaitingForSignal  = 'waiting_for_signal';
    case WaitingForWorkflows = 'waiting_for_workflows';
    case Cancelled      = 'cancelled';
}
```

### `StateMachineStatus`

```php
enum StateMachineStatus: string {
    case Running = 'running';
    case WaitingEvent = 'waiting_event';
    case Completed = 'completed';
    case Failed = 'failed';
    case Paused = 'paused';
    case Cancelled = 'cancelled';
}
```

### `BackoffStrategy`

```php
enum BackoffStrategy: string {
    case Fixed       = 'fixed';
    case Linear      = 'linear';
    case Exponential = 'exponential';
}
```

### `ForEachMode`

```php
enum ForEachMode: string {
    case All = 'all';
    case FirstSuccess = 'first_success';
    case Quorum = 'quorum';
}
```

### `WaitMode`

```php
enum WaitMode: string {
    case All    = 'all';
    case Any    = 'any';
    case Quorum = 'quorum';
}
```

### `LogEvent`

```php
enum LogEvent: string {
    case Started                  = 'started';
    case Completed                = 'completed';
    case Failed                   = 'failed';
    case Buried                   = 'buried';
    case Retried                  = 'retried';
    case WorkflowStarted          = 'workflow_started';
    case WorkflowCompleted        = 'workflow_completed';
    case WorkflowFailed           = 'workflow_failed';
    case WorkflowPaused           = 'workflow_paused';
    case WorkflowResumed          = 'workflow_resumed';
    case WorkflowCancelled        = 'workflow_cancelled';
    case WorkflowRewound          = 'workflow_rewound';
    case WorkflowForked           = 'workflow_forked';
    case WorkflowDeadlineExceeded = 'workflow_deadline_exceeded';
    case Debug                    = 'debug';
}
```

### `OverlapPolicy`

```php
enum OverlapPolicy: string {
    case Allow  = 'allow';
    case Skip   = 'skip';
    case Buffer = 'buffer';
}
```

有关各项策略的工作方式，请参阅 [调度](https://queuety.inline0.dev/zh/docs/features/scheduling)。

## 属性

### `#[QueuetyHandler]`

用于自动注册的 PHP 8 属性。请参阅 [PHP 属性](https://queuety.inline0.dev/zh/docs/features/attributes)。

```php
use Queuety\Attributes\QueuetyHandler;

#[QueuetyHandler( name: 'send_email', queue: 'emails', max_attempts: 5 )]
class SendEmailHandler implements Handler {
    // ...
}
```

## CLI 桥接

### `Queuety::cli_command()`

返回已发布的 WP-CLI 根命令名称。

默认根命令为 `queuety`。如果要以不同名称发布命令，请在 Queuety 启动之前定义 `QUEUETY_CLI_COMMAND`。

### `Queuety::cli_command_map()`

返回可序列化的 Queuety CLI 目录，供测试框架了解已发布的命令范围，而无需扫描 WP-CLI 类。

每个条目包括：
- 稳定的操作 ID
- 相对 CLI 路径
- WP-CLI 处理器类和方法
- 可能的执行目标
- 用于将原始 CLI 参数规范化为执行计划的适配器

### `Queuety::resolve_cli_command( $path, $args, $assoc_args )`

将解析后的 CLI 调用转换为测试框架应运行的执行计划。

```php
$plan = Queuety::resolve_cli_command(
    [ 'workflow', 'export' ],
    [ '42' ],
    [ 'output' => '/tmp/workflow.json' ]
);

echo $plan['callable']; // Queuety\Queuety::export_workflow_to_file
print_r( $plan['arguments'] );
```

执行计划会有意考虑传输方式：
- 命令会解析为带有规范化参数的公共 PHP 可调用对象
- 目录会预先列出所有可能的执行目标，而 `callable` 和 `transport` 会标识解析输入所选择的分支

这是代理测试框架使用 Queuety 的标准方式：使用稳定的操作 ID、公共 API 目标，并由适配器负责规范化仅与 CLI 有关的事项，例如队列默认值、JSON 负载解码、工作进程池分支和工作流导出文件处理。
