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>
关键修正点说明
- 时间范围精准性
- 使用
Carbon::today()确保日期从当天0点开始,subDays(30)获取30天前的0点,搭配endOfDay()保证包含当日最后一秒的数据,避免遗漏或多统计。
- 使用
- 分组统计逻辑
- 用
DATE(created_at)和DATE(deleted_at)按自然日分组,规避时分秒或时区差异导致的分组错误。
- 用
- 初始基数计算
- 直接查询统计周期前(
$startDate之前)创建且未被软删除的用户数,确保起始活跃数完全符合你的需求(比如6月14日前有5个活跃用户,初始基数就是5,若6月14日新增1人,当日活跃数自动变为6)。
- 直接查询统计周期前(
- 空日期补0
- 用
array_map处理无数据的日期,保证每个日期都有对应数值,避免Chart.js图表出现断裂。
- 用
内容的提问来源于stack exchange,提问作者Ronak Panchal
相关产品推荐
相关产品推荐

