feat(admin-dashboard): add rank coverage meta for merchant revenue top5
This commit is contained in:
@@ -227,10 +227,12 @@ class DashboardController extends Controller
|
|||||||
$planIdToName = Plan::query()->pluck('name', 'id')->all();
|
$planIdToName = Plan::query()->pluck('name', 'id')->all();
|
||||||
|
|
||||||
// 排行卡(最小可用):近 7 天站点收入排行 Top5(按已付金额)
|
// 排行卡(最小可用):近 7 天站点收入排行 Top5(按已付金额)
|
||||||
|
// 说明:只统计 payment_status=paid,避免“未支付订单=0金额”混入排行造成噪音。
|
||||||
$merchantRevenueRank7d = PlatformOrder::query()
|
$merchantRevenueRank7d = PlatformOrder::query()
|
||||||
->selectRaw('merchant_id, COUNT(*) as cnt')
|
->selectRaw('merchant_id, COUNT(*) as cnt')
|
||||||
->selectRaw("SUM(CASE WHEN payment_status = 'paid' THEN paid_amount ELSE 0 END) as paid_sum")
|
->selectRaw('SUM(paid_amount) as paid_sum')
|
||||||
->whereBetween('created_at', [$trendStart, $trendEnd])
|
->whereBetween('created_at', [$trendStart, $trendEnd])
|
||||||
|
->where('payment_status', 'paid')
|
||||||
->groupBy('merchant_id')
|
->groupBy('merchant_id')
|
||||||
->orderByDesc('paid_sum')
|
->orderByDesc('paid_sum')
|
||||||
->limit(5)
|
->limit(5)
|
||||||
@@ -245,6 +247,17 @@ class DashboardController extends Controller
|
|||||||
->values()
|
->values()
|
||||||
->all();
|
->all();
|
||||||
|
|
||||||
|
// 用于计算“Top5覆盖率/其它”的全量分母(近 7 天全站点已付总额/总订单数)
|
||||||
|
$merchantRevenueTotalPaid7d = (float) PlatformOrder::query()
|
||||||
|
->whereBetween('created_at', [$trendStart, $trendEnd])
|
||||||
|
->where('payment_status', 'paid')
|
||||||
|
->sum('paid_amount');
|
||||||
|
|
||||||
|
$merchantRevenueTotalOrders7d = (int) PlatformOrder::query()
|
||||||
|
->whereBetween('created_at', [$trendStart, $trendEnd])
|
||||||
|
->where('payment_status', 'paid')
|
||||||
|
->count();
|
||||||
|
|
||||||
$merchantIdToName = Merchant::query()->pluck('name', 'id')->all();
|
$merchantIdToName = Merchant::query()->pluck('name', 'id')->all();
|
||||||
|
|
||||||
return view('admin.dashboard', [
|
return view('admin.dashboard', [
|
||||||
@@ -256,6 +269,8 @@ class DashboardController extends Controller
|
|||||||
'planOrderShareTotal' => (int) $planOrderShareTotal,
|
'planOrderShareTotal' => (int) $planOrderShareTotal,
|
||||||
'planIdToName' => $planIdToName,
|
'planIdToName' => $planIdToName,
|
||||||
'merchantRevenueRank7d' => $merchantRevenueRank7d,
|
'merchantRevenueRank7d' => $merchantRevenueRank7d,
|
||||||
|
'merchantRevenueTotalPaid7d' => (float) $merchantRevenueTotalPaid7d,
|
||||||
|
'merchantRevenueTotalOrders7d' => (int) $merchantRevenueTotalOrders7d,
|
||||||
'merchantIdToName' => $merchantIdToName,
|
'merchantIdToName' => $merchantIdToName,
|
||||||
'platformAdmin' => $admin,
|
'platformAdmin' => $admin,
|
||||||
'cacheMeta' => [
|
'cacheMeta' => [
|
||||||
|
|||||||
@@ -185,12 +185,25 @@
|
|||||||
$rankPaidMax = max($rankPaidMax, (float) ($r['paid_sum'] ?? 0));
|
$rankPaidMax = max($rankPaidMax, (float) ($r['paid_sum'] ?? 0));
|
||||||
$rankOrdersTotal += (int) ($r['count'] ?? 0);
|
$rankOrdersTotal += (int) ($r['count'] ?? 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$rankTotalPaidAll = (float) ($merchantRevenueTotalPaid7d ?? 0);
|
||||||
|
if ($rankTotalPaidAll <= 0) {
|
||||||
|
$rankTotalPaidAll = (float) $rankTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rankCoveragePct = $rankTotalPaidAll > 0 ? round(((float) $rankTotal / $rankTotalPaidAll) * 100, 1) : 0;
|
||||||
|
$rankOtherPaid = max(0.0, $rankTotalPaidAll - (float) $rankTotal);
|
||||||
|
$rankOtherPct = max(0.0, round(100 - $rankCoveragePct, 1));
|
||||||
@endphp
|
@endphp
|
||||||
<div class="adm-mini-meta" data-role="merchant-revenue-rank-7d-meta">
|
<div class="adm-mini-meta" data-role="merchant-revenue-rank-7d-meta">
|
||||||
<span class="adm-mini-meta-item">Top5合计已付:<strong>¥{{ number_format($rankTotal, 2) }}</strong></span>
|
<span class="adm-mini-meta-item">Top5合计已付:<strong>¥{{ number_format($rankTotal, 2) }}</strong></span>
|
||||||
<span class="adm-mini-meta-sep">|</span>
|
<span class="adm-mini-meta-sep">|</span>
|
||||||
<span class="adm-mini-meta-item">Top5订单数:<strong>{{ (int) $rankOrdersTotal }}</strong></span>
|
<span class="adm-mini-meta-item">Top5订单数:<strong>{{ (int) $rankOrdersTotal }}</strong></span>
|
||||||
<span class="adm-mini-meta-sep">|</span>
|
<span class="adm-mini-meta-sep">|</span>
|
||||||
|
<span class="adm-mini-meta-item">覆盖率:<strong>{{ $rankCoveragePct }}%</strong></span>
|
||||||
|
<span class="adm-mini-meta-sep">|</span>
|
||||||
|
<span class="adm-mini-meta-item">其它:<strong>{{ $rankOtherPct }}%</strong>(¥{{ number_format($rankOtherPaid, 2) }})</span>
|
||||||
|
<span class="adm-mini-meta-sep">|</span>
|
||||||
<span class="adm-mini-meta-item">Top1金额:<strong>¥{{ number_format($rankPaidMax, 2) }}</strong></span>
|
<span class="adm-mini-meta-item">Top1金额:<strong>¥{{ number_format($rankPaidMax, 2) }}</strong></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class AdminDashboardMerchantRevenueRank7dMetaShouldIncludeCoverageTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
protected function loginAsPlatformAdmin(): void
|
||||||
|
{
|
||||||
|
$this->seed();
|
||||||
|
|
||||||
|
$this->post('/admin/login', [
|
||||||
|
'email' => 'platform.admin@demo.local',
|
||||||
|
'password' => 'Platform@123456',
|
||||||
|
])->assertRedirect('/admin');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_dashboard_merchant_revenue_rank_7d_meta_should_include_coverage_and_other(): void
|
||||||
|
{
|
||||||
|
$this->loginAsPlatformAdmin();
|
||||||
|
|
||||||
|
$res = $this->get('/admin');
|
||||||
|
$res->assertOk();
|
||||||
|
|
||||||
|
$res->assertSee('data-role="merchant-revenue-rank-7d-meta"', false);
|
||||||
|
$res->assertSee('覆盖率:', false);
|
||||||
|
$res->assertSee('其它:', false);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user