RegisterStatService.php 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. <?php
  2. namespace App\Services;
  3. use App\Models\User;
  4. use Illuminate\Support\Facades\DB;
  5. /**
  6. * 注册走势 / 注册域名统计
  7. */
  8. class RegisterStatService
  9. {
  10. /**
  11. * @param string $type today|all
  12. * @return array{type:string,times:array,member:array,virtual:array,member_total:int,virtual_total:int,...}
  13. */
  14. public static function trend(string $type, ?string $startDate, ?string $endDate): array
  15. {
  16. $today = date('Y-m-d');
  17. $startDate = $startDate ?: $today;
  18. $endDate = $endDate ?: $today;
  19. if ($type === 'today') {
  20. $day = $startDate;
  21. $bucketExpr = "DATE_FORMAT(created_at, '%H:00')";
  22. $times = [];
  23. for ($h = 0; $h < 24; $h++) {
  24. $times[] = sprintf('%02d:00', $h);
  25. }
  26. $rangeStart = $day . ' 00:00:00';
  27. $rangeEnd = $day . ' 23:59:59';
  28. $meta = ['type' => 'today', 'date' => $day];
  29. } else {
  30. $bucketExpr = "DATE_FORMAT(created_at, '%Y-%m-%d')";
  31. $times = [];
  32. $cursor = strtotime($startDate);
  33. $endTs = strtotime($endDate);
  34. while ($cursor <= $endTs) {
  35. $times[] = date('Y-m-d', $cursor);
  36. $cursor = strtotime('+1 day', $cursor);
  37. }
  38. $rangeStart = $startDate . ' 00:00:00';
  39. $rangeEnd = $endDate . ' 23:59:59';
  40. $meta = [
  41. 'type' => 'all',
  42. 'start_time' => $startDate,
  43. 'end_time' => $endDate,
  44. ];
  45. }
  46. $rows = User::query()
  47. ->selectRaw("{$bucketExpr} as time_key, `from` as user_from, COUNT(*) as cnt")
  48. ->whereBetween('created_at', [$rangeStart, $rangeEnd])
  49. ->groupByRaw("{$bucketExpr}, `from`")
  50. ->get();
  51. $memberMap = array_fill_keys($times, 0);
  52. $virtualMap = array_fill_keys($times, 0);
  53. foreach ($rows as $row) {
  54. $key = (string)$row->time_key;
  55. if (!array_key_exists($key, $memberMap)) {
  56. continue;
  57. }
  58. if ((string)$row->user_from === '2') {
  59. $virtualMap[$key] += (int)$row->cnt;
  60. } else {
  61. $memberMap[$key] += (int)$row->cnt;
  62. }
  63. }
  64. return array_merge($meta, [
  65. 'times' => $times,
  66. 'member' => array_values($memberMap),
  67. 'virtual' => array_values($virtualMap),
  68. 'member_total' => array_sum($memberMap),
  69. 'virtual_total' => array_sum($virtualMap),
  70. ]);
  71. }
  72. /**
  73. * 域名统计(SQL 分页)
  74. *
  75. * @return array{total:int,data:list<array{register_url:string,register_count:int}>}
  76. */
  77. public static function domain(?string $startDate, ?string $endDate, int $page, int $limit): array
  78. {
  79. $domainExpr = "IF(register_domain = '' OR register_domain IS NULL, '未知', register_domain)";
  80. $base = User::query();
  81. if ($startDate) {
  82. $base->where('created_at', '>=', $startDate . ' 00:00:00');
  83. }
  84. if ($endDate) {
  85. $base->where('created_at', '<=', $endDate . ' 23:59:59');
  86. }
  87. $grouped = (clone $base)
  88. ->selectRaw("{$domainExpr} as register_url, COUNT(*) as register_count")
  89. ->groupByRaw($domainExpr);
  90. $total = (int)DB::query()->fromSub(
  91. (clone $grouped),
  92. 'domain_groups'
  93. )->count();
  94. $offset = max(0, ($page - 1) * $limit);
  95. $rows = (clone $grouped)
  96. ->orderByDesc(DB::raw('register_count'))
  97. ->offset($offset)
  98. ->limit($limit)
  99. ->get();
  100. $data = $rows->map(fn ($row) => [
  101. 'register_url' => (string)$row->register_url,
  102. 'register_count' => (int)$row->register_count,
  103. ])->values()->all();
  104. return ['total' => $total, 'data' => $data];
  105. }
  106. }