diff --git a/app/Http/Controllers/Admin/PlatformOrderController.php b/app/Http/Controllers/Admin/PlatformOrderController.php index c1d0491..a1de5f6 100644 --- a/app/Http/Controllers/Admin/PlatformOrderController.php +++ b/app/Http/Controllers/Admin/PlatformOrderController.php @@ -143,6 +143,8 @@ class PlatformOrderController extends Controller 'keyword' => trim((string) $request->query('keyword', '')), // 同步失败原因关键词:用于快速定位同原因失败订单(可治理) 'sync_error_keyword' => trim((string) $request->query('sync_error_keyword', '')), + // 批量“标记支付并生效”失败原因关键词:用于定位同原因失败订单(可治理) + 'bmpa_error_keyword' => trim((string) $request->query('bmpa_error_keyword', '')), // 只看“可同步订阅”的订单:已支付 + 已生效 + 未同步(用于运营快速处理) 'syncable_only' => (string) $request->query('syncable_only', ''), // 只看最近 24 小时批量同步过的订单(可治理追踪) @@ -189,6 +191,15 @@ class PlatformOrderController extends Controller ->limit($topN) ->get(); + // 批量“标记支付并生效”失败原因聚合(Top 5) + $bmpaFailedReasonRows = (clone $baseQuery) + ->whereRaw("JSON_EXTRACT(meta, '$.batch_mark_paid_and_activate_error.message') IS NOT NULL") + ->selectRaw("JSON_EXTRACT(meta, '$.batch_mark_paid_and_activate_error.message') as reason, count(*) as cnt") + ->groupBy('reason') + ->orderByDesc('cnt') + ->limit($topN) + ->get(); + $failedReasonStats = $failedReasonRows->map(function ($row) { $reason = (string) ($row->reason ?? ''); $reason = trim($reason, "\" "); @@ -199,6 +210,16 @@ class PlatformOrderController extends Controller ]; })->values()->all(); + $bmpaFailedReasonStats = $bmpaFailedReasonRows->map(function ($row) { + $reason = (string) ($row->reason ?? ''); + $reason = trim($reason, "\" "); + + return [ + 'reason' => $reason !== '' ? $reason : '(空)', + 'count' => (int) ($row->cnt ?? 0), + ]; + })->values()->all(); + // 运营摘要中的 meta 汇总(refund/payment)需要遍历订单 meta。 // 为避免重复 get(),在当前筛选范围内一次性拉取所需字段。 $metaOrders = (clone $baseQuery)->get(['id', 'paid_amount', 'meta']); @@ -342,6 +363,7 @@ class PlatformOrderController extends Controller })(), ], 'failedReasonStats' => $failedReasonStats, + 'bmpaFailedReasonStats' => $bmpaFailedReasonStats, ]); } @@ -802,6 +824,8 @@ class PlatformOrderController extends Controller 'keyword' => trim((string) $request->query('keyword', '')), // 同步失败原因关键词:用于快速定位同原因失败订单(可治理) 'sync_error_keyword' => trim((string) $request->query('sync_error_keyword', '')), + // 批量“标记支付并生效”失败原因关键词:用于定位同原因失败订单(可治理) + 'bmpa_error_keyword' => trim((string) $request->query('bmpa_error_keyword', '')), // 只看“可同步订阅”的订单:已支付 + 已生效 + 未同步(用于运营快速处理) 'syncable_only' => (string) $request->query('syncable_only', ''), // 只看最近 24 小时批量同步过的订单(可治理追踪) @@ -1563,6 +1587,20 @@ class PlatformOrderController extends Controller $builder->whereRaw("JSON_UNQUOTE(JSON_EXTRACT(meta, '$.subscription_activation_error.message')) LIKE ?", ['%' . $kw . '%']); } }) + ->when(($filters['bmpa_error_keyword'] ?? '') !== '', function (Builder $builder) use ($filters) { + // 批量“标记支付并生效”失败原因关键词:batch_mark_paid_and_activate_error.message like + $kw = trim((string) ($filters['bmpa_error_keyword'] ?? '')); + if ($kw === '') { + return; + } + + $driver = $builder->getQuery()->getConnection()->getDriverName(); + if ($driver === 'sqlite') { + $builder->whereRaw("JSON_EXTRACT(meta, '$.batch_mark_paid_and_activate_error.message') LIKE ?", ['%' . $kw . '%']); + } else { + $builder->whereRaw("JSON_UNQUOTE(JSON_EXTRACT(meta, '$.batch_mark_paid_and_activate_error.message')) LIKE ?", ['%' . $kw . '%']); + } + }) ->when(($filters['syncable_only'] ?? '') !== '', function (Builder $builder) { // 只看可同步:已支付 + 已生效 + 尚未写入 subscription_activation.subscription_id $builder->where('payment_status', 'paid') diff --git a/resources/views/admin/platform_orders/index.blade.php b/resources/views/admin/platform_orders/index.blade.php index 49871fa..2a0be52 100644 --- a/resources/views/admin/platform_orders/index.blade.php +++ b/resources/views/admin/platform_orders/index.blade.php @@ -118,7 +118,8 @@ 只看退款不一致(状态 vs 退款总额) - + +