RegisterStatService.php 4.1 KB

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