RegisterStatService.php 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  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. // 测试代理名下会员不统计
  48. $rows = \App\Support\StatsScope::excludeTestUsers(User::query(), 'users')
  49. ->selectRaw("{$bucketExpr} as time_key, `from` as user_from, COUNT(*) as cnt")
  50. ->whereBetween('created_at', [$rangeStart, $rangeEnd])
  51. ->groupByRaw("{$bucketExpr}, `from`")
  52. ->get();
  53. $memberMap = array_fill_keys($times, 0);
  54. $virtualMap = array_fill_keys($times, 0);
  55. foreach ($rows as $row) {
  56. $key = (string)$row->time_key;
  57. if (!array_key_exists($key, $memberMap)) {
  58. continue;
  59. }
  60. if ((string)$row->user_from === '2') {
  61. $virtualMap[$key] += (int)$row->cnt;
  62. } else {
  63. $memberMap[$key] += (int)$row->cnt;
  64. }
  65. }
  66. return array_merge($meta, [
  67. 'times' => $times,
  68. 'member' => array_values($memberMap),
  69. 'virtual' => array_values($virtualMap),
  70. 'member_total' => array_sum($memberMap),
  71. 'virtual_total' => array_sum($virtualMap),
  72. ]);
  73. }
  74. /**
  75. * 域名成功登录人数统计(同域名按用户去重,SQL 分页)
  76. *
  77. * @return array{total:int,data:list<array{login_url:string,login_count:int}>}
  78. */
  79. public static function domain(?string $startDate, ?string $endDate, int $page, int $limit): array
  80. {
  81. $domainExpr = "CASE WHEN login_domain = '' OR login_domain IS NULL THEN '未知' ELSE login_domain END";
  82. $base = DB::table('user_login')
  83. ->where('status', UserLogin::STATUS_SUCCESS)
  84. ->whereNotNull('user_id')
  85. ->where('user_id', '<>', '');
  86. // user_login.user_id 是会员登录 ID(users.user_id);测试代理名下会员不统计
  87. \App\Support\StatsScope::excludeTestMembers($base, 'user_id', 'user_id');
  88. if ($startDate) {
  89. $base->where('created_at', '>=', $startDate . ' 00:00:00');
  90. }
  91. if ($endDate) {
  92. $base->where('created_at', '<=', $endDate . ' 23:59:59');
  93. }
  94. $grouped = (clone $base)
  95. ->selectRaw("{$domainExpr} as login_url, COUNT(DISTINCT user_id) as login_count")
  96. ->groupByRaw($domainExpr);
  97. $total = (int)DB::query()->fromSub(
  98. (clone $grouped),
  99. 'domain_groups'
  100. )->count();
  101. $offset = max(0, ($page - 1) * $limit);
  102. $rows = (clone $grouped)
  103. ->orderByDesc('login_count')
  104. ->orderBy('login_url')
  105. ->offset($offset)
  106. ->limit($limit)
  107. ->get();
  108. $data = $rows->map(fn ($row) => [
  109. 'login_url' => (string)$row->login_url,
  110. 'login_count' => (int)$row->login_count,
  111. ])->values()->all();
  112. return ['total' => $total, 'data' => $data];
  113. }
  114. }