feat(admin): 订阅详情页补回执/退款汇总与对账差额(订阅维度)
This commit is contained in:
@@ -45,6 +45,62 @@ class SiteSubscriptionController extends Controller
|
|||||||
->count(),
|
->count(),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// 可治理摘要:订阅维度的回执/退款汇总(口径与平台订单列表一致:优先 summary,缺省回退 receipts)
|
||||||
|
$metaOrders = (clone $baseOrdersQuery)->get(['id', 'paid_amount', 'meta']);
|
||||||
|
|
||||||
|
$totalReceiptAmount = 0.0;
|
||||||
|
$receiptOrders = 0;
|
||||||
|
$noReceiptOrders = 0;
|
||||||
|
|
||||||
|
$totalRefundedAmount = 0.0;
|
||||||
|
$refundOrders = 0;
|
||||||
|
$noRefundOrders = 0;
|
||||||
|
|
||||||
|
foreach ($metaOrders as $o) {
|
||||||
|
$meta = $o->meta ?? [];
|
||||||
|
|
||||||
|
$receiptTotal = (float) (data_get($meta, 'payment_summary.total_amount') ?? 0);
|
||||||
|
if ($receiptTotal <= 0) {
|
||||||
|
$receipts = (array) (data_get($meta, 'payment_receipts', []) ?? []);
|
||||||
|
foreach ($receipts as $r) {
|
||||||
|
$receiptTotal += (float) (data_get($r, 'amount') ?? 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($receiptTotal > 0) {
|
||||||
|
$receiptOrders++;
|
||||||
|
$totalReceiptAmount += $receiptTotal;
|
||||||
|
} else {
|
||||||
|
$noReceiptOrders++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$refundTotal = (float) (data_get($meta, 'refund_summary.total_amount') ?? 0);
|
||||||
|
if ($refundTotal <= 0) {
|
||||||
|
$refunds = (array) (data_get($meta, 'refund_receipts', []) ?? []);
|
||||||
|
foreach ($refunds as $r) {
|
||||||
|
$refundTotal += (float) (data_get($r, 'amount') ?? 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($refundTotal > 0) {
|
||||||
|
$refundOrders++;
|
||||||
|
$totalRefundedAmount += $refundTotal;
|
||||||
|
} else {
|
||||||
|
$noRefundOrders++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$summaryStats = $summaryStats + [
|
||||||
|
'receipt_orders' => $receiptOrders,
|
||||||
|
'no_receipt_orders' => $noReceiptOrders,
|
||||||
|
'total_receipt_amount' => (float) $totalReceiptAmount,
|
||||||
|
'refund_orders' => $refundOrders,
|
||||||
|
'no_refund_orders' => $noRefundOrders,
|
||||||
|
'total_refunded_amount' => (float) $totalRefundedAmount,
|
||||||
|
// 对账差额:回执总额 - 已付总额(订阅维度)
|
||||||
|
'reconciliation_delta' => (float) ($totalReceiptAmount - (float) $metaOrders->sum('paid_amount')),
|
||||||
|
];
|
||||||
|
|
||||||
// 同步失败原因聚合(Top3):订阅维度快速判断“常见失败原因”
|
// 同步失败原因聚合(Top3):订阅维度快速判断“常见失败原因”
|
||||||
$failedReasonRows = (clone $baseOrdersQuery)
|
$failedReasonRows = (clone $baseOrdersQuery)
|
||||||
->whereRaw("JSON_EXTRACT(meta, '$.subscription_activation_error.message') IS NOT NULL")
|
->whereRaw("JSON_EXTRACT(meta, '$.subscription_activation_error.message') IS NOT NULL")
|
||||||
|
|||||||
@@ -131,6 +131,35 @@
|
|||||||
<div class="muted muted-xs">无 activation 且无 error</div>
|
<div class="muted muted-xs">无 activation 且无 error</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>有回执订单 / 回执总额</h3>
|
||||||
|
<div class="num-md">
|
||||||
|
{{ $summaryStats['receipt_orders'] ?? 0 }}
|
||||||
|
/ ¥{{ number_format((float) ($summaryStats['total_receipt_amount'] ?? 0), 2) }}
|
||||||
|
</div>
|
||||||
|
<div class="muted muted-xs">口径:payment_summary.total_amount 存在或 payment_receipts 有记录</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>有退款订单 / 退款总额</h3>
|
||||||
|
<div class="num-md">
|
||||||
|
{{ $summaryStats['refund_orders'] ?? 0 }}
|
||||||
|
/ ¥{{ number_format((float) ($summaryStats['total_refunded_amount'] ?? 0), 2) }}
|
||||||
|
</div>
|
||||||
|
<div class="muted muted-xs">口径:refund_summary.total_amount 存在或 refund_receipts 有记录</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>对账差额(回执-已付)</h3>
|
||||||
|
@php $delta = (float) ($summaryStats['reconciliation_delta'] ?? 0); @endphp
|
||||||
|
<div class="num-md">¥{{ number_format($delta, 2) }}</div>
|
||||||
|
@if(abs($delta) >= 0.01)
|
||||||
|
<div class="muted muted-xs text-danger">提示:差额非 0,可能存在回执金额与订单已付金额不一致。</div>
|
||||||
|
@else
|
||||||
|
<div class="muted muted-xs">差额为 0(当前订阅维度)</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h3>失败原因Top3</h3>
|
<h3>失败原因Top3</h3>
|
||||||
@php $failedReasonStats = $failedReasonStats ?? []; @endphp
|
@php $failedReasonStats = $failedReasonStats ?? []; @endphp
|
||||||
|
|||||||
@@ -80,6 +80,21 @@ class AdminSiteSubscriptionShowTest extends TestCase
|
|||||||
'synced_at' => now()->subDay()->toDateTimeString(),
|
'synced_at' => now()->subDay()->toDateTimeString(),
|
||||||
'admin_id' => 1,
|
'admin_id' => 1,
|
||||||
],
|
],
|
||||||
|
// 订阅维度治理摘要:回执/退款汇总口径应可识别 summary
|
||||||
|
'payment_summary' => [
|
||||||
|
'count' => 1,
|
||||||
|
'total_amount' => 88,
|
||||||
|
'last_at' => now()->subDay()->toDateTimeString(),
|
||||||
|
'last_amount' => 88,
|
||||||
|
'last_channel' => 'bank_transfer',
|
||||||
|
],
|
||||||
|
'refund_summary' => [
|
||||||
|
'count' => 1,
|
||||||
|
'total_amount' => 10,
|
||||||
|
'last_at' => now()->subHours(12)->toDateTimeString(),
|
||||||
|
'last_amount' => 10,
|
||||||
|
'last_channel' => 'bank_transfer',
|
||||||
|
],
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -91,6 +106,9 @@ class AdminSiteSubscriptionShowTest extends TestCase
|
|||||||
->assertSee('可同步(已支付+已生效+未同步)')
|
->assertSee('可同步(已支付+已生效+未同步)')
|
||||||
->assertSee('未同步(无记录)')
|
->assertSee('未同步(无记录)')
|
||||||
->assertSee('失败原因Top3')
|
->assertSee('失败原因Top3')
|
||||||
|
->assertSee('有回执订单 / 回执总额')
|
||||||
|
->assertSee('有退款订单 / 退款总额')
|
||||||
|
->assertSee('对账差额(回执-已付)')
|
||||||
->assertSee('关联平台订单')
|
->assertSee('关联平台订单')
|
||||||
->assertSee('PO_SUB_SHOW_0001')
|
->assertSee('PO_SUB_SHOW_0001')
|
||||||
->assertSee('同步时间')
|
->assertSee('同步时间')
|
||||||
|
|||||||
Reference in New Issue
Block a user