OpenApiWebhook.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. <?php
  2. namespace App\Module\OpenAPI\Models;
  3. use UCore\ModelCore;
  4. /**
  5. * OpenAPI Webhook配置模型
  6. *
  7. * field start
  8. * field end
  9. */
  10. class OpenApiWebhook extends ModelCore
  11. {
  12. /**
  13. * 数据类型转换
  14. *
  15. * @var array
  16. */
  17. protected $casts = [
  18. 'events' => 'array',
  19. 'timeout' => 'integer',
  20. 'retry_count' => 'integer',
  21. 'current_retry_count' => 'integer',
  22. 'total_deliveries' => 'integer',
  23. 'successful_deliveries' => 'integer',
  24. 'failed_deliveries' => 'integer',
  25. 'last_success_at' => 'datetime',
  26. 'last_failure_at' => 'datetime',
  27. ];
  28. /**
  29. * 默认值
  30. *
  31. * @var array
  32. */
  33. protected $attributes = [
  34. 'status' => 'ACTIVE',
  35. 'timeout' => 30,
  36. 'retry_count' => 3,
  37. 'current_retry_count' => 0,
  38. 'total_deliveries' => 0,
  39. 'successful_deliveries' => 0,
  40. 'failed_deliveries' => 0,
  41. ];
  42. /**
  43. * 关联应用模型
  44. *
  45. * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
  46. */
  47. public function app()
  48. {
  49. return $this->belongsTo(OpenApiApp::class, 'app_id', 'app_id');
  50. }
  51. /**
  52. * 获取状态标签
  53. *
  54. * @return string
  55. */
  56. public function getStatusLabelAttribute(): string
  57. {
  58. return match ($this->status) {
  59. 'ACTIVE' => '激活',
  60. 'INACTIVE' => '停用',
  61. 'FAILED' => '失败',
  62. default => $this->status,
  63. };
  64. }
  65. /**
  66. * 获取状态颜色
  67. *
  68. * @return string
  69. */
  70. public function getStatusColorAttribute(): string
  71. {
  72. return match ($this->status) {
  73. 'ACTIVE' => 'success',
  74. 'INACTIVE' => 'warning',
  75. 'FAILED' => 'danger',
  76. default => 'secondary',
  77. };
  78. }
  79. /**
  80. * 获取成功率
  81. *
  82. * @return float
  83. */
  84. public function getSuccessRateAttribute(): float
  85. {
  86. if ($this->total_deliveries === 0) {
  87. return 0;
  88. }
  89. return round(($this->successful_deliveries / $this->total_deliveries) * 100, 2);
  90. }
  91. /**
  92. * 获取失败率
  93. *
  94. * @return float
  95. */
  96. public function getFailureRateAttribute(): float
  97. {
  98. if ($this->total_deliveries === 0) {
  99. return 0;
  100. }
  101. return round(($this->failed_deliveries / $this->total_deliveries) * 100, 2);
  102. }
  103. /**
  104. * 获取事件列表的字符串表示
  105. *
  106. * @return string
  107. */
  108. public function getEventsStringAttribute(): string
  109. {
  110. if (empty($this->events)) {
  111. return '无';
  112. }
  113. if (in_array('*', $this->events)) {
  114. return '全部事件';
  115. }
  116. return implode(', ', $this->events);
  117. }
  118. /**
  119. * 检查是否监听指定事件
  120. *
  121. * @param string $event
  122. * @return bool
  123. */
  124. public function listensToEvent(string $event): bool
  125. {
  126. if (empty($this->events)) {
  127. return false;
  128. }
  129. return in_array('*', $this->events) || in_array($event, $this->events);
  130. }
  131. /**
  132. * 重置重试计数
  133. *
  134. * @return void
  135. */
  136. public function resetRetryCount(): void
  137. {
  138. $this->update(['current_retry_count' => 0]);
  139. }
  140. /**
  141. * 检查是否可以重试
  142. *
  143. * @return bool
  144. */
  145. public function canRetry(): bool
  146. {
  147. return $this->current_retry_count < $this->retry_count;
  148. }
  149. /**
  150. * 按应用ID查询
  151. *
  152. * @param \Illuminate\Database\Eloquent\Builder $query
  153. * @param string $appId
  154. * @return \Illuminate\Database\Eloquent\Builder
  155. */
  156. public function scopeByApp($query, string $appId)
  157. {
  158. return $query->where('app_id', $appId);
  159. }
  160. /**
  161. * 查询激活状态的Webhook
  162. *
  163. * @param \Illuminate\Database\Eloquent\Builder $query
  164. * @return \Illuminate\Database\Eloquent\Builder
  165. */
  166. public function scopeActive($query)
  167. {
  168. return $query->where('status', 'ACTIVE');
  169. }
  170. /**
  171. * 按事件类型查询
  172. *
  173. * @param \Illuminate\Database\Eloquent\Builder $query
  174. * @param string $event
  175. * @return \Illuminate\Database\Eloquent\Builder
  176. */
  177. public function scopeForEvent($query, string $event)
  178. {
  179. return $query->where(function ($q) use ($event) {
  180. $q->whereJsonContains('events', $event)
  181. ->orWhereJsonContains('events', '*');
  182. });
  183. }
  184. /**
  185. * 查询最近失败的Webhook
  186. *
  187. * @param \Illuminate\Database\Eloquent\Builder $query
  188. * @param int $hours
  189. * @return \Illuminate\Database\Eloquent\Builder
  190. */
  191. public function scopeRecentlyFailed($query, int $hours = 24)
  192. {
  193. return $query->where('last_failure_at', '>=', now()->subHours($hours));
  194. }
  195. /**
  196. * 查询需要重试的Webhook
  197. *
  198. * @param \Illuminate\Database\Eloquent\Builder $query
  199. * @return \Illuminate\Database\Eloquent\Builder
  200. */
  201. public function scopeNeedsRetry($query)
  202. {
  203. return $query->whereColumn('current_retry_count', '<', 'retry_count')
  204. ->where('status', 'ACTIVE');
  205. }
  206. /**
  207. * 生成新的密钥
  208. *
  209. * @return string
  210. */
  211. public function regenerateSecret(): string
  212. {
  213. $secret = bin2hex(random_bytes(32));
  214. $this->update(['secret' => $secret]);
  215. return $secret;
  216. }
  217. /**
  218. * 获取掩码后的密钥(用于显示)
  219. *
  220. * @return string
  221. */
  222. public function getMaskedSecretAttribute(): string
  223. {
  224. if (empty($this->secret)) {
  225. return '';
  226. }
  227. return substr($this->secret, 0, 8) . str_repeat('*', 48) . substr($this->secret, -8);
  228. }
  229. }