You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel获取近30天用户数据适配Chart.js折线图问题排查

Laravel 用户数据统计修正方案

核心问题分析

你的需求是统计近30天每日新增用户、销户用户(软删除),并生成包含起始活跃用户基数的趋势折线图。现有数据不准确通常源于时间范围界定模糊、分组统计逻辑错误、初始基数计算偏差这几个点。


修正后的完整代码实现

1. 控制器逻辑(获取并处理统计数据)

use Carbon\Carbon;
use App\Models\User;

public function userTrendStats()
{
    // 定义统计时间范围:当前日期往前推30天,包含当日
    $endDate = Carbon::today();
    $startDate = Carbon::today()->subDays(30);

    // 生成连续的日期数组(用于Chart.js的labels)
    $dateRange = [];
    $currentDate = $startDate->copy();
    while ($currentDate->lte($endDate)) {
        $dateRange[] = $currentDate->format('Y-m-d');
        $currentDate->addDay();
    }

    // 统计每日新增用户:按自然日分组
    $dailyNew = User::whereBetween('created_at', [$startDate, $endDate->copy()->endOfDay()])
        ->selectRaw('DATE(created_at) as date, COUNT(*) as total')
        ->groupBy('date')
        ->pluck('total', 'date')
        ->toArray();

    // 统计每日销户用户:仅统计软删除的用户,按删除日期分组
    $dailyDeleted = User::onlyTrashed()
        ->whereBetween('deleted_at', [$startDate, $endDate->copy()->endOfDay()])
        ->selectRaw('DATE(deleted_at) as date, COUNT(*) as total')
        ->groupBy('date')
        ->pluck('total', 'date')
        ->toArray();

    // 计算统计周期前的初始活跃用户数(即$startDate之前创建且未被删除的用户)
    $initialActive = User::where('created_at', '<', $startDate)
        ->whereNull('deleted_at')
        ->count();

    // 计算每日活跃用户趋势:初始基数 + 当日新增 - 当日销户
    $activeTrend = [];
    $currentActive = $initialActive;
    foreach ($dateRange as $date) {
        $add = $dailyNew[$date] ?? 0;
        $subtract = $dailyDeleted[$date] ?? 0;

        $currentActive += $add - $subtract;
        $activeTrend[] = $currentActive;
    }

    // 整理Chart.js所需的数据集
    $chartData = [
        'labels' => $dateRange,
        'datasets' => [
            [
                'label' => '新增用户',
                'data' => array_map(fn($d) => $dailyNew[$d] ?? 0, $dateRange),
                'borderColor' => '#27ae60',
                'backgroundColor' => 'rgba(39, 174, 96, 0.1)',
                'fill' => false
            ],
            [
                'label' => '销户用户',
                'data' => array_map(fn($d) => $dailyDeleted[$d] ?? 0, $dateRange),
                'borderColor' => '#e74c3c',
                'backgroundColor' => 'rgba(231, 76, 60, 0.1)',
                'fill' => false
            ],
            [
                'label' => '活跃用户',
                'data' => $activeTrend,
                'borderColor' => '#3498db',
                'backgroundColor' => 'rgba(52, 152, 219, 0.1)',
                'fill' => true
            ]
        ]
    ];

    return view('user-stats', compact('chartData'));
}

2. Blade视图(渲染Chart.js折线图)

<div style="height: 400px; width: 100%;">
    <canvas id="userTrendChart"></canvas>
</div>

<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
    const ctx = document.getElementById('userTrendChart').getContext('2d');
    const chartData = @json($chartData);

    new Chart(ctx, {
        type: 'line',
        data: chartData,
        options: {
            responsive: true,
            scales: {
                y: {
                    beginAtZero: false,
                    title: { display: true, text: '用户数量' }
                },
                x: {
                    title: { display: true, text: '日期' }
                }
            }
        }
    });
</script>

关键修正点说明

  1. 时间范围精准性
    • 使用Carbon::today()确保日期从当天0点开始,subDays(30)获取30天前的0点,搭配endOfDay()保证包含当日最后一秒的数据,避免遗漏或多统计。
  2. 分组统计逻辑
    • 用DATE(created_at)和DATE(deleted_at)按自然日分组,规避时分秒或时区差异导致的分组错误。
  3. 初始基数计算
    • 直接查询统计周期前($startDate之前)创建且未被软删除的用户数,确保起始活跃数完全符合你的需求(比如6月14日前有5个活跃用户,初始基数就是5,若6月14日新增1人,当日活跃数自动变为6)。
  4. 空日期补0
    • 用array_map处理无数据的日期,保证每个日期都有对应数值,避免Chart.js图表出现断裂。

内容的提问来源于stack exchange,提问作者Ronak Panchal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 14:52:36