承接 adamczykpiotr/laravel-dag-workflows 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

adamczykpiotr/laravel-dag-workflows

Composer 安装命令:

composer require adamczykpiotr/laravel-dag-workflows

包简介

This is my package dag-workflows

README 文档

README

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

A lightweight library to define and dispatch directed acyclic graph (DAG) based workflows composed of Tasks and TaskGroups. Each Task can contain one or more jobs and may declare dependencies on other tasks. This package helps model, persist and execute complex multi-step workflows in Laravel applications.

Key features:

  • Expressive workflow definitions using Workflow, Task and TaskGroup building blocks
  • Support for single and grouped tasks
  • Task dependencies and ordering
  • Easy dispatching and inspection via Eloquent models
  • Per-step progress reporting with built-in debounce
  • rollbackStep() hook to undo a failed attempt's leftovers before a retry
  • Pausable tasks for manual intervention (anomaly detection, user approval, etc.)
  • Events for workflow state changes (paused, resumed, cancelled)

Installation

Install the package via Composer and run migrations:

composer require adamczykpiotr/laravel-dag-workflows
php artisan migrate

Migrations ship with the package and run in place. If you want to customise the schema, publish them first: php artisan vendor:publish --tag="dag-workflows-migrations".

Usage

Below is a concise example showing how to define and dispatch a workflow. This example mirrors the structure of the included tinker snippet but models an "Image Import Pipeline":

<?php

use AdamczykPiotr\DagWorkflows\Definitions\Task;
use AdamczykPiotr\DagWorkflows\Definitions\TaskGroup;
use AdamczykPiotr\DagWorkflows\Definitions\Workflow;

const TASK_FETCH_FEEDS = 'fetch_feeds';
const TASK_PARSE_CATALOGS = 'parse_catalogs';
const TASK_PARSE_ALBUMS = 'parse_albums';
const TASK_PARSE_IMAGES = 'parse_images';
const TASK_PARSE_METADATA = 'parse_metadata';

const TASK_SYNC_CATALOG_ALBUM_RELATIONS = 'sync_catalog_album_relations';
const TASK_SYNC_ALBUM_IMAGE_RELATIONS = 'sync_album_image_relations';
const TASK_SYNC_IMAGE_METADATA_RELATIONS = 'sync_image_metadata_relations';

$workflow = new Workflow(
    name: 'Image Import Pipeline',
    tasks: [
        new Task(
            name: TASK_FETCH_FEEDS,
            jobs: new DownloadFeedsJob(),
        ),

        new TaskGroup(
            tasks: [
                new Task(
                    name: TASK_PARSE_CATALOGS,
                    jobs: [
                        new ParseCatalogsJob('source-a'),
                        new ParseCatalogsJob('source-b'),
                    ],
                ),

                new Task(
                    name: TASK_PARSE_ALBUMS,
                    jobs: new ParseAlbumsJob(),
                ),

                new Task(
                    name: TASK_PARSE_IMAGES,
                    jobs: new ParseImagesJob(),
                ),

                new Task(
                    name: TASK_PARSE_METADATA,
                    jobs: new ParseImageMetadataJob(),
                ),
            ],
            dependsOn: TASK_FETCH_FEEDS,
        ),

        new Task(
            name: TASK_SYNC_CATALOG_ALBUM_RELATIONS,
            jobs: new SyncCatalogAlbumRelationsJob(),
            dependsOn: [TASK_PARSE_CATALOGS, TASK_PARSE_ALBUMS],
        ),

        new Task(
            name: TASK_SYNC_ALBUM_IMAGE_RELATIONS,
            jobs: new SyncAlbumImageRelationsJob(),
            dependsOn: [TASK_PARSE_ALBUMS, TASK_SYNC_CATALOG_ALBUM_RELATIONS],
        ),

        new Task(
            name: TASK_SYNC_IMAGE_METADATA_RELATIONS,
            jobs: new SyncImageMetadataRelationsJob(),
            dependsOn: [TASK_PARSE_IMAGES, TASK_SYNC_ALBUM_IMAGE_RELATIONS],
        ),
    ],
);

$model = $workflow->dispatch();
dump($model->id);

Reporting progress

Jobs using HasWorkflowTracking can call $this->progress(int $percentage) (0–100). Writes are debounced against the step row's updated_at (30s window); 100 and progress(..., force: true) always write.

Rolling back failed attempts

When a step is retried, the previous attempt may have left things behind — a downloaded file, rows from queries that already ran. Override rollbackStep() to undo them; it runs right before handle(), but only when a previous attempt of the step already ran. First attempts skip it, and the default is a no-op, so existing jobs are unaffected. A throwing rollbackStep() fails the step like a throwing handle().

use AdamczykPiotr\DagWorkflows\Traits\HasWorkflowTracking;

class BuildDatasetJob implements ShouldQueue {
    use HasWorkflowTracking;

    public function rollbackStep(): void {
        // Undo whatever the failed attempt left behind.
        File::deleteDirectory($this->workDirectory());
    }

    public function handle(): void {
        // ... always starts from a clean slate
    }
}

This also covers steps re-run because an upstream step was retried: if the step already ran, it rolls back first; if it never ran, it starts clean.

Pausable Tasks

Workflows, tasks, and steps can be paused for manual intervention. This is useful when:

  • An anomaly is detected that requires human review
  • User approval is needed before continuing
  • External validation is required
use AdamczykPiotr\DagWorkflows\Models\Workflow;

$workflow = Workflow::find($id);

// Pause the entire workflow
$workflow->pause('Anomaly detected - awaiting manual review');

// Resume when ready
$workflow->resume();

// Or cancel if not recoverable
$workflow->cancel();

The package dispatches events (WorkflowPaused, WorkflowResumed, WorkflowCancelled) that you can listen to for notifications and integrations.

For comprehensive documentation including job examples, event handling, and best practices, see docs/PAUSABLE_TASKS.md.

Early Task Completion

A job can complete its whole task early when the remaining steps are known to be unnecessary — for example when a freshly downloaded source file is byte-identical to the one already processed:

use AdamczykPiotr\DagWorkflows\Traits\HasWorkflowTracking;

class ScrapeDatasetJob implements ShouldQueue {
    use HasWorkflowTracking;

    public function handle(): void {
        $file = $this->download();

        if ($this->isUnchanged($file)) {
            $this->completeTaskEarly('source file unchanged'); // never returns
        }

        // ... continue as usual
    }
}

The current step is marked COMPLETED, all remaining steps of the task are marked SKIPPED (a terminal, non-failing status), and the task completes as if every step had run — dependant tasks are dispatched and the workflow status is finalized as usual.

Workflow endpoint formats

WorkflowController::show defaults to a lightweight summary — no dependency wiring, no estimator:

{
    "id": 134,
    "name": "Sync POIs",
    "status": "RUNNING",
    "durationSeconds": 512,
    "taskStatuses": { "COMPLETED": 7, "RUNNING": 2, "PENDING": 105 },
    "stepStatuses": { "COMPLETED": 21, "RUNNING": 2, "PENDING": 211 },
    "taskCompletionPercentage": 6.14,
    "stepCompletionPercentage": 8.97,
    "stepProgressPercentage": 9.42
}

SKIPPED counts as done in the percentages (it is the terminal, non-failing status of steps bypassed by an early task completion), and stepProgressPercentage additionally credits running steps with their self-reported progress. Append ?format=full for the previous behaviour: the complete tasks/steps tree with dependencies and timing estimates.

Dynamic dependencies (waiting for a ResolvableTask's spawned tasks)

A ResolvableTask completes once its resolver has spawned the child tasks — depending on it by name therefore does NOT wait for the children. A dependency ending with * is a prefix glob: it gates the task on every OTHER task whose name starts with the prefix, including tasks that only come into existence at runtime:

new Task(name: 'Import: Customers', jobs: new ImportCustomersJob()),
new ResolvableTask(
    name: 'Import: Orders',
    items: fn() => $regions,
    jobs: fn(string $region) => new ImportOrdersJob($region),
),
new Task(
    name: 'Import: Summary',
    jobs: new BuildImportSummaryJob(),
    dependsOn: 'Import: *', // waits for Import: Customers, Import: Orders and every Import: Orders:<region> task
),

The declaring task never matches its own wildcard, so the aggregate may live in the namespace it waits for. At least one other task must match the glob at definition time — that anchor keeps the dependant parked until runtime-spawned matches (which are wired in while their resolver is still running) have been attached.

* is reserved for this syntax — task names containing it are rejected with a WorkflowTaskReservedCharacterException.

Limiting ResolvableTask items per environment

config/dag-workflows.php points at a middleware applied to the items before tasks are materialised. The default is PassthroughMiddleware although for testing purposes there's also handy implementation of TakeFirstMiddleware.

Custom middlewares have to implement WorkflowResolvableItemsMiddleware::handle(iterable $items): iterable interface.

Testing

Run the package and application tests:

composer test
composer analyse

Contributing

Contributions are welcome. Please read CONTRIBUTING.md in the repository for guidelines.

Security

If you discover a security vulnerability, please follow the repository's security policy to report it.

Credits

  • Piotr Adamczyk (maintainer)
  • All contributors

License

The MIT License (MIT). Please see License File for more information.

adamczykpiotr/laravel-dag-workflows 适用场景与选型建议

adamczykpiotr/laravel-dag-workflows 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 523 次下载、GitHub Stars 达 0, 最近一次更新时间为 2025 年 11 月 10 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「laravel」 「adamczykpiotr」 「dag-workflows」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 adamczykpiotr/laravel-dag-workflows 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 adamczykpiotr/laravel-dag-workflows 我们能提供哪些服务?
定制开发 / 二次开发

基于 adamczykpiotr/laravel-dag-workflows 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 523
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 20
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2025-11-10