184 lines
8.8 KiB
PHP
184 lines
8.8 KiB
PHP
@extends('admin.layouts.app')
|
||
|
||
@section('title', '批次详情')
|
||
@section('page_title', '批次详情')
|
||
|
||
@section('content')
|
||
@php
|
||
$typeText = $type === 'bas' ? 'BAS(批量同步订阅)' : ($type === 'bmpa' ? 'BMPA(批量标记支付并生效)' : '未知');
|
||
|
||
$selfWithoutBack = \App\Support\BackUrl::selfWithoutBack();
|
||
$backToListUrl = $safeBackForLinks !== '' ? $safeBackForLinks : '/admin/platform-orders';
|
||
|
||
$hasSummary = $summary !== null;
|
||
|
||
$success = (int) (data_get($summary, 'success') ?? 0);
|
||
$failed = (int) (data_get($summary, 'failed') ?? 0);
|
||
$matched = (int) (data_get($summary, 'matched') ?? 0);
|
||
$processed = (int) (data_get($summary, 'processed') ?? 0);
|
||
$at = (string) (data_get($summary, 'at') ?? '');
|
||
$topReasons = (array) (data_get($summary, 'top_reasons', []) ?? []);
|
||
|
||
$fallbackMatched = (int) (data_get($fallbackCounts, 'matched') ?? 0);
|
||
$fallbackFailed = (int) (data_get($fallbackCounts, 'failed') ?? 0);
|
||
|
||
$matchedForRatio = $hasSummary ? $matched : $fallbackMatched;
|
||
$failedForRatio = $hasSummary ? $failed : $fallbackFailed;
|
||
$failedRatioPct = $matchedForRatio > 0 ? round(($failedForRatio / $matchedForRatio) * 100, 1) : 0.0;
|
||
|
||
$SYNC_ERROR_KEYWORD_LINK_MAX_LEN = (int) config('saasshop.platform_orders.sync_error_keyword_link_max_len', 200);
|
||
$SYNC_ERROR_KEYWORD_LINK_MAX_LEN = max(50, min(1000, $SYNC_ERROR_KEYWORD_LINK_MAX_LEN));
|
||
$SYNC_FAILED_REASON_TRUNCATE_LEN = (int) config('saasshop.platform_orders.sync_failed_reason_display_truncate_len', 60);
|
||
$SYNC_FAILED_REASON_TRUNCATE_LEN = max(20, min(200, $SYNC_FAILED_REASON_TRUNCATE_LEN));
|
||
@endphp
|
||
|
||
<div class="card mb-20">
|
||
<div class="actions-spread">
|
||
<div>
|
||
<h2 class="mb-6">{{ $typeText }} 批次详情</h2>
|
||
<div class="muted">run_id:<strong>{{ $runId ?: '-' }}</strong></div>
|
||
@if($at !== '')
|
||
<div class="muted muted-xs">更新时间:{{ $at }}</div>
|
||
@endif
|
||
</div>
|
||
<div class="actions gap-10">
|
||
<a class="btn btn-secondary btn-sm" href="{{ $backToListUrl }}">返回上一页</a>
|
||
@if($runId !== '')
|
||
<button type="button" class="btn btn-secondary btn-sm" data-action="copy-run-id" data-run-id="{{ $runId }}">复制 run_id</button>
|
||
@endif
|
||
<a class="btn btn-secondary btn-sm" href="/admin/platform-batches/show?type={{ $type }}&run_id={{ urlencode($runId) }}&back={{ urlencode($selfWithoutBack) }}">刷新</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
@if(($error ?? '') !== '')
|
||
<div class="warning">{{ $error }}</div>
|
||
@else
|
||
@if(! $hasSummary)
|
||
<div class="warning mb-20">
|
||
本批次尚未生成 last_result 汇总(可能队列任务仍在执行,或历史数据未补齐)。
|
||
当前页面展示的是基于同批次订单的<strong>粗略统计</strong>,供运营先行定位。
|
||
</div>
|
||
@endif
|
||
|
||
<div class="grid-3 mb-20">
|
||
<div class="card">
|
||
<h3>命中</h3>
|
||
<div class="metric-number">
|
||
@if(($governanceLinks['all'] ?? '') !== '')
|
||
<a class="link" data-role="batch-matched-link" href="{{ $governanceLinks['all'] }}">{{ $hasSummary ? $matched : $fallbackMatched }}</a>
|
||
@else
|
||
{{ $hasSummary ? $matched : $fallbackMatched }}
|
||
@endif
|
||
</div>
|
||
</div>
|
||
<div class="card">
|
||
<h3>本次处理</h3>
|
||
<div class="metric-number">
|
||
@if($hasSummary && ($governanceLinks['all'] ?? '') !== '')
|
||
<a class="link" data-role="batch-processed-link" href="{{ $governanceLinks['all'] }}">{{ $processed }}</a>
|
||
@else
|
||
{{ $hasSummary ? $processed : '-' }}
|
||
@endif
|
||
</div>
|
||
</div>
|
||
<div class="card">
|
||
<h3>成功 / 失败</h3>
|
||
<div class="metric-number">{{ $hasSummary ? ($success . ' / ' . $failed) : ('- / ' . $fallbackFailed) }}</div>
|
||
@if(($governanceLinks['failed'] ?? '') !== '' && $matchedForRatio > 0)
|
||
<div class="muted muted-xs">失败占比:
|
||
<a class="link" data-role="batch-failed-ratio-link" href="{{ $governanceLinks['failed'] }}">{{ $failedRatioPct }}%</a>
|
||
<span class="muted">({{ $failedForRatio }}/{{ $matchedForRatio }})</span>
|
||
</div>
|
||
@endif
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card mb-20">
|
||
<h3>一键治理入口</h3>
|
||
<div class="inline-links">
|
||
@if(($governanceLinks['all'] ?? '') !== '')
|
||
<a class="link" href="{{ $governanceLinks['all'] }}">本批次全部</a>
|
||
@endif
|
||
@if(($governanceLinks['failed'] ?? '') !== '')
|
||
<span class="muted">|</span>
|
||
<a class="link" href="{{ $governanceLinks['failed'] }}">本批次失败</a>
|
||
@endif
|
||
@if(($governanceLinks['top_reason'] ?? '') !== '')
|
||
<span class="muted">|</span>
|
||
<a class="link" href="{{ $governanceLinks['top_reason'] }}">按Top原因</a>
|
||
@endif
|
||
@if(($governanceLinks['retry_syncable'] ?? '') !== '')
|
||
<span class="muted">|</span>
|
||
<a class="link" href="{{ $governanceLinks['retry_syncable'] }}">本批次可同步重试</a>
|
||
@endif
|
||
@if(($governanceLinks['retry_processable'] ?? '') !== '')
|
||
<span class="muted">|</span>
|
||
<a class="link" href="{{ $governanceLinks['retry_processable'] }}">本批次可再次尝试(pending+unpaid)</a>
|
||
@endif
|
||
</div>
|
||
<div class="muted muted-xs mt-6">说明:当前批次为“冗余写入到每条订单 meta.last_result”的模式;后续可演进为独立批次表。</div>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<h3>Top 失败原因</h3>
|
||
@if(! $hasSummary)
|
||
<div class="muted">暂无(last_result 未写入时不做原因聚合,以免在页面侧引入重查询)。</div>
|
||
@elseif(count($topReasons) === 0)
|
||
<div class="muted">暂无。</div>
|
||
@else
|
||
<table class="table">
|
||
<thead>
|
||
<tr>
|
||
<th>原因</th>
|
||
<th style="width: 120px;">次数</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
@foreach($topReasons as $r)
|
||
@php
|
||
$reason = (string) (data_get($r, 'reason') ?? '');
|
||
$cnt = (int) (data_get($r, 'count') ?? 0);
|
||
|
||
$reasonTooLong = mb_strlen($reason) > $SYNC_ERROR_KEYWORD_LINK_MAX_LEN;
|
||
$reasonDisplay = $reasonTooLong ? (mb_substr($reason, 0, $SYNC_FAILED_REASON_TRUNCATE_LEN) . '...') : $reason;
|
||
|
||
$reasonGovUrl = '';
|
||
if (! $reasonTooLong && $reason !== '') {
|
||
if ($type === 'bas') {
|
||
$reasonGovUrl = \App\Support\BackUrl::withBackAndFragment('/admin/platform-orders?' . \Illuminate\Support\Arr::query([
|
||
'batch_activation_run_id' => $runId,
|
||
'sync_status' => 'failed',
|
||
'sync_error_keyword' => $reason,
|
||
]), $safeBackForLinks, 'filters');
|
||
}
|
||
if ($type === 'bmpa') {
|
||
$reasonGovUrl = \App\Support\BackUrl::withBackAndFragment('/admin/platform-orders?' . \Illuminate\Support\Arr::query([
|
||
'batch_bmpa_run_id' => $runId,
|
||
'bmpa_failed_only' => '1',
|
||
'bmpa_error_keyword' => $reason,
|
||
]), $safeBackForLinks, 'filters');
|
||
}
|
||
}
|
||
@endphp
|
||
<tr>
|
||
<td>
|
||
@if($reasonGovUrl !== '')
|
||
<a class="link" data-role="batch-top-reason-link" href="{{ $reasonGovUrl }}" title="按该原因筛选">{{ $reasonDisplay }}</a>
|
||
@else
|
||
<span title="{{ $reason }}">{{ $reasonDisplay !== '' ? $reasonDisplay : '-' }}</span>
|
||
@if($reasonTooLong)
|
||
<div class="muted muted-xs text-danger">原因过长,请复制到列表页筛选框</div>
|
||
@endif
|
||
@endif
|
||
</td>
|
||
<td>{{ $cnt }}</td>
|
||
</tr>
|
||
@endforeach
|
||
</tbody>
|
||
</table>
|
||
@endif
|
||
</div>
|
||
@endif
|
||
@endsection
|