0

我有一个在 Laravel 中创建的网络应用程序,它接受信用卡付款。

每天我创建的一个预定命令都会运行以接收“今天的”付款(基本上它会为每个待处理的付款提交一个 http 请求到支付网关)。

现在我需要允许通过仪表板中的按钮触发此付款提交过程。

该命令需要随机长时间来处理(取决于要处理的付款数量),因此我认为从控制器调用它不是一个选项。

我正在考虑重构它:将所有命令代码移动到“中间人”类,这样我就可以在命令和控制器上调用这个类。

PaymentsSubmissionHelper::submit()

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

但是,该命令会显示一个进度条和预计的处理时间,我还需要在 html 界面中显示一个进度条。在 Web 界面中,我需要向服务器发出 ajax 请求以获取当前进度,但在命令中以完全不同的方式跟踪此进度,使用:

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

对于每笔已处理的付款:

$bar->advance();

如何创建对命令和控制器的进度的跟踪?

任何帮助将不胜感激。

提前致谢!

4

3 回答 3

1

正如在另一个答案中已经指出的那样,Laravel 的排队事件侦听器是在前端处理长时间运行的进程的方式。您根本不需要重构控制台命令。

至于在前端显示进度,一个简单的解决方案是设置一些 AJAX 轮询。每隔几秒钟,AJAX 就会向控制器方法发出请求,该方法只查看今天的付款,计算处理了多少(假设您有某种status字段可以显示正在运行的作业是否已处理它),并且返回一个代表已完成百分比的数字。然后,AJAXsuccess处理程序将更新页面上的进度跟踪器。

// 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);
            }
        }
    });
}

以某种方式确保民意调查不会溢出可能是明智之举,也许您想调整民意调查的频率。

于 2019-02-13T12:58:46.837 回答
1

我建议在这个用例中使用排队事件侦听器。您将在控制器中分派一个事件,并拥有一个可以触发命令的侦听器。通过对侦听器进行排队,您可以避免较长的响应时间。无需重构命令本身!

关于进度条,您可以有一个静态进度条,在页面加载时更新,您可以从数据库中读取状态并显示它,类似于亚马逊在任何时候显示您的订单有多远。

对于实时更新的进度条,我建议实现 Web 套接字。Socket.io似乎很棒。

于 2019-02-13T12:16:52.207 回答
0

当您使用进度条并推进它时,您将在 ajax 中执行相同的操作,但进度逻辑将在偏离过程中有所不同。

这两种情况的共同点是处理每张卡的付款。所以我会说创建单独的类或服务,例如PaymentProcess,处理卡支付实例并在成功或失败时返回。

然后在命令中你可以做(​​psuedocode):

公共函数句柄() { $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 响应应该为您提供存储在某处的当前进程的 id。然后,您将以 1 秒的间隔继续发送另一个 ajx 请求并获取当前进度,直到达到 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);

}

于 2019-02-13T12:29:25.997 回答