AdminRealtimeSummaries.php 2.7 KB

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