devsrv/laravel-scheduled-action

处理 Laravel 的定时模型操作

v3.1.0 2023-03-27 12:07 UTC

This package is auto-updated.

Last update: 2024-09-27 15:23:26 UTC


README

Latest Version on Packagist Total Downloads GitHub Tests Action Status

处理与 Eloquent 模型关联的定时任务。

create schedule poll schedules


对于任何定时任务,我们都可以直接使用 Laravel 的 队列,但如果该任务需要在执行前进行某些修改怎么办呢?

此包存储所有需要在将来日期和时间运行的定时任务,并在任务计划运行的当天执行每个任务,这样我们就有机会在任务执行前修改它。

它使用 Laravel 的定时任务 调度 来确定和处理需要当天在指定时间运行的定时任务,并将任务负载发送到你的应用程序的 接收类可配置的)。所以如何执行任务完全取决于你。

💡 工作原理

  • 此包创建一个名为 model_actions 的表
  • 每个任务有 4 种状态 PENDING FINISHED CANCELLED DISPATCHED
  • scheduledaction:poll 艺术家命令轮询当天需要执行的 PENDING 任务,并将任务负载传递给你的接收类。
  • 设置轮询的频率和传递给您的接收器的任务数量(上面的 示例 显示每小时 10 个)
  • PENDING 任务在指定日期和时间运行,请根据处理方式将任务标记为 FINISHEDCANCELLED检查示例)。
  • 很可能是您会使用队列在指定时间运行任务,所以将任务调度到队列工作后,您可能想要将状态设置为 DISPATCHED

安装

composer require devsrv/laravel-scheduled-action

设置

✔️ 发布迁移

php artisan vendor:publish --provider="Devsrv\ScheduledAction\ScheduledActionServiceProvider" --tag="migrations"

✔️ 运行迁移

php artisan migrate

✔️ 发布配置

php artisan vendor:publish --provider="Devsrv\ScheduledAction\ScheduledActionServiceProvider" --tag="config"
// the class that takes care of how to perform the task, the payload will be passed to this invokable class
return [
    'receiver' => \App\Http\AutoAction\ScheduledActionReceiver::class, 👈 needs to be invokable
];

✔️ 将定时任务添加到 app/Console/Kernel.php

$schedule->command('scheduledaction:poll --tasks=10')->hourly();  // poll pending tasks (10 tasks every hour & sends payload to your receiver, customize as per your app)

✔️ 在您的模型中使用 HasScheduledAction 特性

use Devsrv\ScheduledAction\Traits\HasScheduledAction;

class Candidate extends Model
{
    use HasFactory, HasScheduledAction;
    
    ...
}

有许多流畅的方法可以与定时任务交互

获取

use Devsrv\ScheduledAction\Models\ModelAction;
use Devsrv\ScheduledAction\Facades\Action;

$model = \App\Models\Candidate::find(10);

$model->scheduledActions()->get();
$model->scheduledActions()->finished()->get();
$model->scheduledActions()->cancelled()->first();
$model->scheduledActions()->pending()->get();
$model->scheduledActions()->dispatched()->paginate();
$model->scheduledActions()->pending()->toActBetweenTime(Carbon::createFromTimeString('14:00:00'), Carbon::createFromTimeString('16:30:00'))->get();
$model->scheduledActions()->pending()->whereExtraProperty('prefers', 'mail')->get();
$model->scheduledActions()->whereExtraProperty('channel', 'slack')->get();
$model->scheduledActions()->whereExtraProperties(['prefers' => 'mail', 'applicant' => 27])->get();
$model->scheduledActions()->wherePropertyContains('languages', ['en', 'de'])->get();
$model->scheduledActions()->first()->isPending();
$model->scheduledActions()->first()->getExtraProperty('customProperty');

$task = ModelAction::find(1);

$task->getExtraProperty('customProperty');
$task->act_time;
$task->action;
$task->actionable; 	// associated model

$task->isPending;	    // bool
$task->isFinished; 	    // bool
$task->isDispatched; 	    // bool
$task->isCancelled; 	    // bool
$task->isRecurring; 	    // bool

ModelAction::finished()->get();
ModelAction::forModel($modlel)->pending()->get();
ModelAction::forClass(Candidate::class)->get();
ModelAction::whereAction('EMAIL')->get();
ModelAction::forClass(Candidate::class)->modelId(10)->get();
ModelAction::modelIdIn([10, 11, 12])->get();
ModelAction::forModel($modlel)->whereProperty('mailable', \App\Mail\RejectMail::class)->get();
ModelAction::forModel($modlel)->whereProperties(['type' => 'info', 'applicant' => 27])->get();
ModelAction::wherePropertyContains('languages', ['en', 'de'])->get();
ModelAction::where('properties->type', 'success')->get();

\Devsrv\ScheduledAction\Facades\Action::needsToRunOn(now()->tomorrow(), 10, 'asc');
\Devsrv\ScheduledAction\Facades\Action::needsToRunToday(3);

创建

$action = $model->scheduledActions()->create([
    'action' => 'EMAIL',
    'properties' => ['color' => 'silver'],
    'status' => \Devsrv\ScheduledAction\Enums\Status::PENDING,
    'act_date' => now(),
    'act_time' => now()->setHour(11),
]);

$model->scheduledActions()->createMany([]);

ModelAction::for(Candidate::find(10))
    ->actWith('EMAIL')
    ->actAt(Carbon::tomorrow()->setHour(11))    // date + time will be set together
    ->setExtraProperties(['foo' => 'bar'])
    ->createSchedule();

ModelAction::for($model)
    ->actWith('EMAIL')
    ->actDate($actDate)
    ->actTime(Carbon::createFromTimeString('20:00:00'))
    ->setExtraProperties(['foo' => 'bar'])
    ->createSchedule();

ModelAction::for($model)->actWith('EMAIL')->actTime($carbon)->asDispatched()->createSchedule();

更新

$action->setPending()->save();
$action->setCancelled()->save();
$action->setDispatched()->save();
$action->setFinished()->save();							// default sets finished_at as now()
$action->setFinished($carbon)->save();						// set a finished_at time
$action->mergeExtraProperties(['key' => 'val'])->save();  			// merges extra properties
$action->withExtraProperties([])->save();                   			// overwrites existing
$action->setPending()->setActAt($actAt)->save();
$action->setActAt($carbon)->save();						// date and time will be extracted
$action->setActDate($carbon)->save();						// only date will be used
$action->setActTime($carbon)->save();						// only time will be used

示例

想象一个工作门户网站,申请在申请后的 3 天内自动批准,除非它被机器人的一些资格检查自动拒绝,在此期间,管理员可以覆盖申请状态或防止自动发送邮件或修改邮件内容。

步骤 - 1

发生了某些事件,并创建了一个在未来日期和时间执行的任务
ModelAction::for($application)
->actWith('MAIL')
->actAt($inThreeDays)
->setExtraProperties([
  'mailable' => ApproveApplication::class,
  'template' => $template
])
->createSchedule();

步骤 - 2

管理员决定修改任务
public function modifyScheduledTask() {
    $this->validate();

    $this->task
        ->setActDate(Carbon::createFromFormat('m/d/Y', $this->act_date))
        ->setActTime(Carbon::createFromTimeString($this->act_time))
        ->mergeExtraProperties([
            'template' => $this->templateid,
            'extra_data' => $this->role
        ])
        ->save();

    $this->info('schedule updated');
}
    
public function cancelSchedule() {
    $this->task->setCancelled()->save();
    $this->info('schedule cancelled');
}

步骤 - 3

接收类获取任务负载,并根据任务操作将任务传递到类(对于此示例发送电子邮件)
<?php

namespace App\Http\AutoAction;

use Facades\App\Http\Services\AutoAction\Mailer;

class ScheduledActionReceiver
{
    public function __invoke($tasks)
    {
        foreach ($tasks as $task) {
            match($task->action) {
                'MAIL'    => MailTaskHandler::handle($task),
                ...

                default   => activity()->log('auto action task unhandled')
            };
        }
    }
}

步骤 - 4

通过之前的接收类接收发送电子邮件的任务负载,并发送邮件
class MailTaskHandler
{
	public function handle($task) {
        [$user, $mailable, $extras, $time] = $this->extractData($task);

        Mail::to($user)
        ->later(
            Carbon::now()->setTimeFromTimeString($time),
            new $mailable($user, $extras)
        );

        $task->setFinished()->save();	// 👈 marking the task finished here though it is actually not, because for mail there is no easy way to execute this code after that mail is actually sent
    }

    private function extractData($task) {
        $model = $task->actionable;
        $user = null;

        $mailable = $task->getExtraProperty('mailable');
        $template = $task->getExtraProperty('template');

        $extras = [];

        if($mailable === ApproveApplication::class) {
            $extras['role'] = $task->getExtraProperty('role');

            $user = $model->applicant;
        }

        $time = $task->act_time;

        return [$user, $mailable, $template, $extras, $time];
    }
}

更新日志

有关最近更改的更多信息,请参阅 更新日志

许可

MIT 许可协议。请参阅许可文件以获取更多信息。

👋🏼 打个招呼!

如果你觉得这个包很有用,请留下一个⭐,别忘了在Twitter上告诉我