'array', 'timeout' => 'integer', 'retry_count' => 'integer', 'current_retry_count' => 'integer', 'total_deliveries' => 'integer', 'successful_deliveries' => 'integer', 'failed_deliveries' => 'integer', 'last_success_at' => 'datetime', 'last_failure_at' => 'datetime', ]; /** * 默认值 * * @var array */ protected $attributes = [ 'status' => 'ACTIVE', 'timeout' => 30, 'retry_count' => 3, 'current_retry_count' => 0, 'total_deliveries' => 0, 'successful_deliveries' => 0, 'failed_deliveries' => 0, ]; /** * 关联应用模型 * * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ public function app() { return $this->belongsTo(OpenApiApp::class, 'app_id', 'app_id'); } /** * 获取状态标签 * * @return string */ public function getStatusLabelAttribute(): string { return match ($this->status) { 'ACTIVE' => '激活', 'INACTIVE' => '停用', 'FAILED' => '失败', default => $this->status, }; } /** * 获取状态颜色 * * @return string */ public function getStatusColorAttribute(): string { return match ($this->status) { 'ACTIVE' => 'success', 'INACTIVE' => 'warning', 'FAILED' => 'danger', default => 'secondary', }; } /** * 获取成功率 * * @return float */ public function getSuccessRateAttribute(): float { if ($this->total_deliveries === 0) { return 0; } return round(($this->successful_deliveries / $this->total_deliveries) * 100, 2); } /** * 获取失败率 * * @return float */ public function getFailureRateAttribute(): float { if ($this->total_deliveries === 0) { return 0; } return round(($this->failed_deliveries / $this->total_deliveries) * 100, 2); } /** * 获取事件列表的字符串表示 * * @return string */ public function getEventsStringAttribute(): string { if (empty($this->events)) { return '无'; } if (in_array('*', $this->events)) { return '全部事件'; } return implode(', ', $this->events); } /** * 检查是否监听指定事件 * * @param string $event * @return bool */ public function listensToEvent(string $event): bool { if (empty($this->events)) { return false; } return in_array('*', $this->events) || in_array($event, $this->events); } /** * 重置重试计数 * * @return void */ public function resetRetryCount(): void { $this->update(['current_retry_count' => 0]); } /** * 检查是否可以重试 * * @return bool */ public function canRetry(): bool { return $this->current_retry_count < $this->retry_count; } /** * 按应用ID查询 * * @param \Illuminate\Database\Eloquent\Builder $query * @param string $appId * @return \Illuminate\Database\Eloquent\Builder */ public function scopeByApp($query, string $appId) { return $query->where('app_id', $appId); } /** * 查询激活状态的Webhook * * @param \Illuminate\Database\Eloquent\Builder $query * @return \Illuminate\Database\Eloquent\Builder */ public function scopeActive($query) { return $query->where('status', 'ACTIVE'); } /** * 按事件类型查询 * * @param \Illuminate\Database\Eloquent\Builder $query * @param string $event * @return \Illuminate\Database\Eloquent\Builder */ public function scopeForEvent($query, string $event) { return $query->where(function ($q) use ($event) { $q->whereJsonContains('events', $event) ->orWhereJsonContains('events', '*'); }); } /** * 查询最近失败的Webhook * * @param \Illuminate\Database\Eloquent\Builder $query * @param int $hours * @return \Illuminate\Database\Eloquent\Builder */ public function scopeRecentlyFailed($query, int $hours = 24) { return $query->where('last_failure_at', '>=', now()->subHours($hours)); } /** * 查询需要重试的Webhook * * @param \Illuminate\Database\Eloquent\Builder $query * @return \Illuminate\Database\Eloquent\Builder */ public function scopeNeedsRetry($query) { return $query->whereColumn('current_retry_count', '<', 'retry_count') ->where('status', 'ACTIVE'); } /** * 生成新的密钥 * * @return string */ public function regenerateSecret(): string { $secret = bin2hex(random_bytes(32)); $this->update(['secret' => $secret]); return $secret; } /** * 获取掩码后的密钥(用于显示) * * @return string */ public function getMaskedSecretAttribute(): string { if (empty($this->secret)) { return ''; } return substr($this->secret, 0, 8) . str_repeat('*', 48) . substr($this->secret, -8); } }