AdminRealtimeSummaries.php 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. <?php
  2. namespace App\Services\AdminRealtime;
  3. use App\Constants\HttpStatus;
  4. use App\Http\Controllers\admin\Home;
  5. use App\Http\Controllers\admin\Online;
  6. use App\Http\Controllers\admin\PaymentOrder;
  7. use Illuminate\Http\JsonResponse;
  8. use Illuminate\Support\Facades\Cache;
  9. use Illuminate\Support\Facades\DB;
  10. use Illuminate\Support\Facades\Log;
  11. use RuntimeException;
  12. /** 全量统计按变化登记事件,覆盖跨仓库写入、在线平台变化及统计时间窗口到期。 */
  13. final class AdminRealtimeSummaries
  14. {
  15. public const DEFINITIONS = [
  16. 'online_platform_count' => ['admin.stats.platform_count', Online::class, 'platformCount'],
  17. 'payment_unprocessed' => ['admin.stats.unprocessed', PaymentOrder::class, 'unProcessed'],
  18. 'third_game_balance' => ['admin.stats.third_game_balance', Home::class, 'thirdGameBalance'],
  19. ];
  20. public static function read(string $resource): array
  21. {
  22. [, $controller, $method] = self::DEFINITIONS[$resource];
  23. // 复用 HTTP 端点本身,统计字段、筛选条件和时间窗口只有一个实现。
  24. $response = app()->call([app()->make($controller), $method]);
  25. $body = $response instanceof JsonResponse ? $response->getData(true) : [];
  26. if (($body['code'] ?? null) !== HttpStatus::OK || !is_array($body['data'] ?? null)) {
  27. throw new RuntimeException($controller . '@' . $method . ' failed: ' . ($body['msg'] ?? 'invalid response'));
  28. }
  29. return $body['data'];
  30. }
  31. public static function scan(): void
  32. {
  33. if (!config('admin_realtime.enabled')) return;
  34. $interval = max(1, (int) config('admin_realtime.summary_scan_seconds', 5));
  35. foreach (array_keys(self::DEFINITIONS) as $resource) {
  36. try {
  37. if (!Cache::add('admin_realtime:summary_scan:' . $resource, 1, $interval)) continue;
  38. $snapshot = self::read($resource);
  39. $key = 'admin_realtime:summary_snapshot:' . $resource;
  40. $previous = Cache::get($key);
  41. if ($resource === 'third_game_balance' && $snapshot['balance'] === null && $previous === null) continue;
  42. $signature = hash('sha256', json_encode($snapshot, JSON_THROW_ON_ERROR));
  43. if ($previous === $signature) continue;
  44. // 必须先确认事件登记成功再记住快照;失败或崩溃最多重复,不会吞掉变化。
  45. if (!DB::table('admin_realtime_events')->insert([
  46. 'resource' => $resource, 'resource_id' => '1', 'action' => 'updated',
  47. 'source' => 'bot-28', 'created_at' => now('Asia/Shanghai')->format('Y-m-d H:i:s'),
  48. ])) throw new RuntimeException('Summary event registration failed');
  49. Cache::forever($key, $signature);
  50. } catch (\Throwable $e) {
  51. Log::channel('realtime')->error('admin_realtime_summary_scan_failed', [
  52. 'resource' => $resource, 'error' => $e->getMessage(),
  53. ]);
  54. }
  55. }
  56. }
  57. }