QuotaAlertEvent.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. <?php
  2. namespace App\Module\ThirdParty\Events;
  3. use App\Module\ThirdParty\Models\ThirdPartyService;
  4. use App\Module\ThirdParty\Models\ThirdPartyQuota;
  5. use App\Module\ThirdParty\Enums\QUOTA_TYPE;
  6. use Illuminate\Foundation\Events\Dispatchable;
  7. use Illuminate\Queue\SerializesModels;
  8. /**
  9. * 配额告警事件
  10. *
  11. * 当第三方服务配额达到告警阈值时触发此事件
  12. */
  13. class QuotaAlertEvent
  14. {
  15. use Dispatchable, SerializesModels;
  16. /**
  17. * 第三方服务实例
  18. *
  19. * @var ThirdPartyService
  20. */
  21. public ThirdPartyService $service;
  22. /**
  23. * 配额实例
  24. *
  25. * @var ThirdPartyQuota
  26. */
  27. public ThirdPartyQuota $quota;
  28. /**
  29. * 告警类型
  30. *
  31. * @var string
  32. */
  33. public string $alertType;
  34. /**
  35. * 当前使用量
  36. *
  37. * @var int
  38. */
  39. public int $currentUsage;
  40. /**
  41. * 配额限制
  42. *
  43. * @var int
  44. */
  45. public int $quotaLimit;
  46. /**
  47. * 使用百分比
  48. *
  49. * @var float
  50. */
  51. public float $usagePercentage;
  52. /**
  53. * 告警阈值
  54. *
  55. * @var float
  56. */
  57. public float $alertThreshold;
  58. /**
  59. * 告警时间
  60. *
  61. * @var \Carbon\Carbon
  62. */
  63. public \Carbon\Carbon $alertTime;
  64. /**
  65. * 额外数据
  66. *
  67. * @var array
  68. */
  69. public array $metadata;
  70. // 告警类型常量
  71. public const ALERT_TYPE_WARNING = 'warning'; // 警告:达到警告阈值
  72. public const ALERT_TYPE_CRITICAL = 'critical'; // 严重:达到严重阈值
  73. public const ALERT_TYPE_EXHAUSTED = 'exhausted'; // 耗尽:配额用完
  74. /**
  75. * 创建事件实例
  76. *
  77. * @param ThirdPartyService $service
  78. * @param ThirdPartyQuota $quota
  79. * @param string $alertType
  80. * @param int $currentUsage
  81. * @param int $quotaLimit
  82. * @param float $usagePercentage
  83. * @param float $alertThreshold
  84. * @param array $metadata
  85. */
  86. public function __construct(
  87. ThirdPartyService $service,
  88. ThirdPartyQuota $quota,
  89. string $alertType,
  90. int $currentUsage,
  91. int $quotaLimit,
  92. float $usagePercentage,
  93. float $alertThreshold,
  94. array $metadata = []
  95. ) {
  96. $this->service = $service;
  97. $this->quota = $quota;
  98. $this->alertType = $alertType;
  99. $this->currentUsage = $currentUsage;
  100. $this->quotaLimit = $quotaLimit;
  101. $this->usagePercentage = $usagePercentage;
  102. $this->alertThreshold = $alertThreshold;
  103. $this->alertTime = now();
  104. $this->metadata = $metadata;
  105. }
  106. /**
  107. * 获取事件数据数组
  108. *
  109. * @return array
  110. */
  111. public function toArray(): array
  112. {
  113. return [
  114. 'service_id' => $this->service->id,
  115. 'service_code' => $this->service->code,
  116. 'service_name' => $this->service->name,
  117. 'quota_id' => $this->quota->id,
  118. 'quota_type' => $this->quota->quota_type,
  119. 'quota_type_label' => QUOTA_TYPE::from($this->quota->quota_type)->getLabel(),
  120. 'alert_type' => $this->alertType,
  121. 'current_usage' => $this->currentUsage,
  122. 'quota_limit' => $this->quotaLimit,
  123. 'usage_percentage' => $this->usagePercentage,
  124. 'alert_threshold' => $this->alertThreshold,
  125. 'alert_time' => $this->alertTime->toISOString(),
  126. 'metadata' => $this->metadata,
  127. ];
  128. }
  129. /**
  130. * 获取告警级别
  131. *
  132. * @return string
  133. */
  134. public function getAlertLevel(): string
  135. {
  136. return match ($this->alertType) {
  137. self::ALERT_TYPE_WARNING => 'WARNING',
  138. self::ALERT_TYPE_CRITICAL => 'CRITICAL',
  139. self::ALERT_TYPE_EXHAUSTED => 'CRITICAL',
  140. default => 'INFO',
  141. };
  142. }
  143. /**
  144. * 获取告警消息
  145. *
  146. * @return string
  147. */
  148. public function getAlertMessage(): string
  149. {
  150. $serviceName = $this->service->name;
  151. $quotaTypeLabel = QUOTA_TYPE::from($this->quota->quota_type)->getLabel();
  152. $percentage = number_format($this->usagePercentage, 1);
  153. return match ($this->alertType) {
  154. self::ALERT_TYPE_WARNING => "第三方服务 [{$serviceName}] 的 {$quotaTypeLabel} 配额使用率已达到 {$percentage}%,请注意监控",
  155. self::ALERT_TYPE_CRITICAL => "第三方服务 [{$serviceName}] 的 {$quotaTypeLabel} 配额使用率已达到 {$percentage}%,即将耗尽",
  156. self::ALERT_TYPE_EXHAUSTED => "第三方服务 [{$serviceName}] 的 {$quotaTypeLabel} 配额已耗尽,服务调用将被限制",
  157. default => "第三方服务 [{$serviceName}] 配额状态更新",
  158. };
  159. }
  160. /**
  161. * 获取剩余配额
  162. *
  163. * @return int
  164. */
  165. public function getRemainingQuota(): int
  166. {
  167. return max(0, $this->quotaLimit - $this->currentUsage);
  168. }
  169. /**
  170. * 判断是否为严重告警
  171. *
  172. * @return bool
  173. */
  174. public function isCritical(): bool
  175. {
  176. return in_array($this->alertType, [self::ALERT_TYPE_CRITICAL, self::ALERT_TYPE_EXHAUSTED]);
  177. }
  178. /**
  179. * 获取预计耗尽时间
  180. *
  181. * @return \Carbon\Carbon|null
  182. */
  183. public function getEstimatedExhaustionTime(): ?\Carbon\Carbon
  184. {
  185. if ($this->alertType === self::ALERT_TYPE_EXHAUSTED) {
  186. return null; // 已经耗尽
  187. }
  188. // 基于当前使用率估算耗尽时间
  189. $quotaType = QUOTA_TYPE::from($this->quota->quota_type);
  190. $windowStart = \Carbon\Carbon::instance($quotaType->getCurrentWindowStart());
  191. $windowEnd = \Carbon\Carbon::instance($quotaType->getCurrentWindowEnd());
  192. $elapsedTime = now()->diffInMinutes($windowStart);
  193. if ($elapsedTime <= 0 || $this->currentUsage <= 0) {
  194. return null;
  195. }
  196. $usageRate = $this->currentUsage / $elapsedTime; // 每分钟使用量
  197. $remainingQuota = $this->getRemainingQuota();
  198. if ($usageRate <= 0) {
  199. return null;
  200. }
  201. $minutesToExhaustion = $remainingQuota / $usageRate;
  202. return now()->addMinutes($minutesToExhaustion);
  203. }
  204. /**
  205. * 获取建议操作
  206. *
  207. * @return array
  208. */
  209. public function getSuggestedActions(): array
  210. {
  211. $actions = [];
  212. switch ($this->alertType) {
  213. case self::ALERT_TYPE_WARNING:
  214. $actions[] = '监控配额使用情况';
  215. $actions[] = '检查是否有异常调用';
  216. $actions[] = '考虑优化调用频率';
  217. break;
  218. case self::ALERT_TYPE_CRITICAL:
  219. $actions[] = '立即检查调用情况';
  220. $actions[] = '暂停非必要的API调用';
  221. $actions[] = '联系服务提供商增加配额';
  222. $actions[] = '启用备用服务(如有)';
  223. break;
  224. case self::ALERT_TYPE_EXHAUSTED:
  225. $actions[] = '停止所有API调用';
  226. $actions[] = '等待配额重置';
  227. $actions[] = '切换到备用服务';
  228. $actions[] = '联系服务提供商紧急增加配额';
  229. break;
  230. }
  231. return $actions;
  232. }
  233. }