Команда Laravel, которая теперь должна быть "доступна" через http

У меня есть веб-приложение, созданное в Laravel, которое принимает платежи по кредитным картам.

Каждый день созданная мной запланированная команда выполняет "сегодняшние" платежи (в основном она отправляет один http-запрос на каждый ожидающий платеж в платежный шлюз).

Теперь мне нужно разрешить запускать процесс отправки платежа с помощью кнопки на панели инструментов.

Обработка команды занимает много времени (в зависимости от количества обрабатываемых платежей), поэтому вызывать ее из контроллера я думаю, что это не вариант.

Я думаю просто изменить его: переместить весь код команды в класс "middleman", чтобы я мог вызывать этот класс как для команды, так и для контроллера.

PaymentsSubmissionHelper::submit()

PaymentsSubmissionCommand: PaymentsSubmissionHelper::submit()
PaymentsSubmissionController: PaymentsSubmissionHelper::submit()

Тем не менее, команда показывает индикатор выполнения и приблизительное время обработки, и мне нужно будет также показать индикатор выполнения в html-интерфейсе. В веб-интерфейсе мне нужно будет сделать ajax-запросы к серверу, чтобы получить текущий прогресс, но в команде этот прогресс отслеживается совершенно по-другому, используя:

$bar = $this->output->createProgressBar($totalPayments);
$bar->setFormat(' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %message%');

и за каждый обработанный платеж:

$bar->advance();

Как я могу следить за ходом выполнения команды и контроллера?

Любая помощь будет оценена.

Заранее спасибо!

3 ответа

Как уже указывалось в другом ответе, прослушиватели событий Laravel, находящиеся в очереди, являются способом обработки длительных процессов во внешнем интерфейсе. Вам вообще не нужно реорганизовывать консольную команду.

Что касается показа прогресса на внешнем интерфейсе, одним простым решением было бы установить некоторый опрос AJAX. Каждые несколько секунд AJAX запускает запрос к методу контроллера, который просто просматривает сегодняшние платежи, подсчитывает, сколько их обработано (возможно, у вас есть какой-то status поле, которое покажет вам, обработано ли уже выполненное задание или нет), и вернет число, представляющее процент выполненных работ. AJAX success Затем обработчик обновит ваш трекер прогресса на странице.

// Check status every 2s
var timer = setInterval(function() {
    pollStatus();
}, 2000);

pollStatus = function() {
    $.ajax({
        url: 'somewhere/jobStatus',
        success: function(resp) {
            $('#progress').html(resp . '%');

            if (resp === 100) {
                // We've reached 100%, no need to keep polling now
                clearInterval(timer);
            }
        }
    });
}

Возможно, было бы разумно как-то убедиться, что опросы не переполнены, и, возможно, вы захотите изменить частоту опроса.

Я бы предложил использовать прослушиватели событий в очереди в этом случае использования. Вы отправили бы событие в свой контроллер и получили бы прослушиватель, который мог бы запустить команду. Ставя слушателя в очередь, вы избегаете длительного времени ответа. Не нужно рефакторинг самой команды!

Что касается индикатора выполнения, у вас может быть статическая индикаторная строка, которая обновляется при загрузке страницы, где вы будете считывать статус из вашей БД и отображать его аналогично тому, как Amazon отображает, как далеко находится ваш заказ в любой момент.

Для обновления индикатора в реальном времени я предлагаю использовать веб-сокеты. https://socket.io/ кажется отличным.

Поскольку вы используете индикатор прогресса и продвигаете его, вы будете делать то же самое в ajax, но логика прогресса будет отличаться от курса.

Общей частью в обоих случаях является обработка каждой платежной карты. Поэтому я скажу создать отдельный класс или услугу, которая принимает экземпляр платежной карты, например PaymentProcess, обрабатывает его и возвращает в случае успеха или неудачи.

Затем в команде вы можете сделать (psuedocode):

public function handle () {$ pendingPayments = Payment:: where ('status', 'pending');

$bar = $this->output->createProgressBar($pendingPayments->count());

$pendingPayments->chunk(10, function($payments) use($bar){

    $payments->each(function($payment) use ($bar){

        $process = (new PaymentProcess($payment))->process();

        $bar->advance();

    });
});

$bar->finish();

}

Теперь, если вы запускаете это из внешнего интерфейса, ответ ajax должен дать вам идентификатор текущего процесса, хранящегося где-то. Затем вы будете продолжать посылать другие ajx-запросы с интервалом, скажем, 1 секунду, и получать текущий прогресс, пока он не достигнет 100%. (Если вы используете XMLHttpRequest2, то логика будет отличаться)

Для этого вы можете создать другую таблицу для хранения результатов и затем обновлять ее.

Теперь аналогичным образом вы можете использовать PaymentProcess внутри контроллера.:

public function processPendingPayments (Request $ request) {// Авторизовать запрос $ this-> authorize ('processPendingPayments', Payment:: class);

$pendingPayments = Payment::where('status', 'pending');

// Create a progress entry
$progress = PaymentProgress::create([
    'reference' => str_random('6')
    'total' => $pendingPayments->count(),
    'completed' => 0
]);

$pendingPayments->chunk(10, function($payments) use($bar){

    $payments->each(function($payment) use ($bar){

        $process = (new PaymentProcess($payment))->process();

        // Update a progress entry
        $progress->update([
            'completed' => $progress->completed + 1;
        ]);

    });
});

return response()->json([
    'progress_reference' => $progress->reference
], 200);

}

Теперь еще одна конечная точка, чтобы получить прогресс

public function getProgress(Request $request) { // Авторизовать запрос $ this-> authorize ('getProgress', Payment:: class);

$request->validate([
    'reference' => 'required|exists:payment_process,reference'
]);

$progress = PaymentProcess::where('reference', $request->reference)->first();

$percentage = $progress->completed / $progress->total * 100;

 return response()->json(compact('percentage'), 200);

}

Другие вопросы по тегам