Files
saasshop/app/Http/Controllers/Admin/PlatformBatchController.php

166 lines
7.0 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Concerns\ResolvesPlatformAdminContext;
use App\Http\Controllers\Controller;
use App\Models\PlatformOrder;
use App\Support\BackUrl;
use Illuminate\Http\Request;
use Illuminate\View\View;
class PlatformBatchController extends Controller
{
use ResolvesPlatformAdminContext;
public function show(Request $request): View
{
$this->ensurePlatformAdmin($request);
$type = trim((string) $request->query('type', ''));
$runId = trim((string) $request->query('run_id', ''));
$type = $type === 'bmpa' ? 'bmpa' : ($type === 'bas' ? 'bas' : '');
$incomingBack = (string) $request->query('back', '');
$safeBackForLinks = BackUrl::sanitizeForLinks($incomingBack);
if ($type === '' || $runId === '') {
return view('admin.platform_batches.show', [
'type' => $type,
'runId' => $runId,
'safeBackForLinks' => $safeBackForLinks,
'error' => '参数不完整:请提供 typebas/bmpa与 run_id。',
'summary' => null,
'fallbackCounts' => null,
'governanceLinks' => [],
]);
}
// 基于订单 meta 的扁平字段回捞 last_result冗余写入策略下可行
// 注意:为避免全表扫描,这里先按 run_id 过滤再取最近一条。
$keyPrefix = $type === 'bas' ? '$.batch_activation' : '$.batch_mark_paid_and_activate';
$query = PlatformOrder::query();
$driver = $query->getQuery()->getConnection()->getDriverName();
if ($driver === 'sqlite') {
$query->whereRaw("JSON_EXTRACT(meta, '{$keyPrefix}.run_id') = ?", [$runId]);
} else {
$query->whereRaw("JSON_UNQUOTE(JSON_EXTRACT(meta, '{$keyPrefix}.run_id')) = ?", [$runId]);
}
$order = $query->orderByDesc('id')->first(['id', 'meta']);
$summary = null;
if ($order) {
$summary = data_get($order->meta, $type === 'bas'
? 'batch_activation.last_result'
: 'batch_mark_paid_and_activate.last_result');
}
// 兜底:若 last_result 未写入(例如批量 job 尚未跑完,或历史数据未补齐),
// 则基于同批次订单粗略统计matched/processed/failed失败口径BAS=subscription_activation_errorBMPA=batch_mark_paid_and_activate_error
// 注意:这里尽量不做重计算 success因为“成功”的定义可能随业务变动仅用于 UI 提示。
$fallbackCounts = null;
if ($summary === null) {
$baseQuery = PlatformOrder::query();
$driver2 = $baseQuery->getQuery()->getConnection()->getDriverName();
if ($driver2 === 'sqlite') {
$baseQuery->whereRaw("JSON_EXTRACT(meta, '{$keyPrefix}.run_id') = ?", [$runId]);
} else {
$baseQuery->whereRaw("JSON_UNQUOTE(JSON_EXTRACT(meta, '{$keyPrefix}.run_id')) = ?", [$runId]);
}
$matched = (int) (clone $baseQuery)->count();
$failed = 0;
if ($type === 'bas') {
$failed = (int) (clone $baseQuery)
->whereRaw("JSON_EXTRACT(meta, '$.subscription_activation_error.message') IS NOT NULL")
->count();
}
if ($type === 'bmpa') {
$failed = (int) (clone $baseQuery)
->whereRaw("JSON_EXTRACT(meta, '$.batch_mark_paid_and_activate_error.message') IS NOT NULL")
->count();
}
$fallbackCounts = [
'matched' => $matched,
'failed' => $failed,
];
}
// 治理入口:全部/失败/按Top原因/可重试
$governanceLinks = [];
if ($type === 'bas') {
$governanceLinks['all'] = BackUrl::withBack('/admin/platform-orders?' . \Illuminate\Support\Arr::query([
'batch_activation_run_id' => $runId,
]), $safeBackForLinks);
$governanceLinks['failed'] = BackUrl::withBack('/admin/platform-orders?' . \Illuminate\Support\Arr::query([
'batch_activation_run_id' => $runId,
'sync_status' => 'failed',
]), $safeBackForLinks);
$governanceLinks['retry_syncable'] = BackUrl::withBack('/admin/platform-orders?' . \Illuminate\Support\Arr::query([
'batch_activation_run_id' => $runId,
'sync_status' => 'unsynced',
'syncable_only' => '1',
]), $safeBackForLinks);
$topReason = (string) (data_get($summary, 'top_reasons.0.reason') ?? '');
$maxLen = (int) config('saasshop.platform_orders.sync_error_keyword_link_max_len', 200);
$maxLen = max(50, min(1000, $maxLen));
if ($topReason !== '' && mb_strlen($topReason) <= $maxLen) {
$governanceLinks['top_reason'] = BackUrl::withBack('/admin/platform-orders?' . \Illuminate\Support\Arr::query([
'batch_activation_run_id' => $runId,
'sync_status' => 'failed',
'sync_error_keyword' => $topReason,
]), $safeBackForLinks);
}
}
if ($type === 'bmpa') {
$governanceLinks['all'] = BackUrl::withBack('/admin/platform-orders?' . \Illuminate\Support\Arr::query([
'batch_bmpa_run_id' => $runId,
]), $safeBackForLinks);
$governanceLinks['failed'] = BackUrl::withBack('/admin/platform-orders?' . \Illuminate\Support\Arr::query([
'batch_bmpa_run_id' => $runId,
'bmpa_failed_only' => '1',
]), $safeBackForLinks);
$governanceLinks['retry_processable'] = BackUrl::withBack('/admin/platform-orders?' . \Illuminate\Support\Arr::query([
'batch_bmpa_run_id' => $runId,
'status' => 'pending',
'payment_status' => 'unpaid',
]), $safeBackForLinks);
$topReason = (string) (data_get($summary, 'top_reasons.0.reason') ?? '');
$maxLen = (int) config('saasshop.platform_orders.sync_error_keyword_link_max_len', 200);
$maxLen = max(50, min(1000, $maxLen));
if ($topReason !== '' && mb_strlen($topReason) <= $maxLen) {
$governanceLinks['top_reason'] = BackUrl::withBack('/admin/platform-orders?' . \Illuminate\Support\Arr::query([
'batch_bmpa_run_id' => $runId,
'bmpa_failed_only' => '1',
'bmpa_error_keyword' => $topReason,
]), $safeBackForLinks);
}
}
return view('admin.platform_batches.show', [
'type' => $type,
'runId' => $runId,
'safeBackForLinks' => $safeBackForLinks,
'error' => '',
'summary' => $summary,
'fallbackCounts' => $fallbackCounts,
'governanceLinks' => $governanceLinks,
]);
}
}