'array', 'rate_limits' => 'array', 'ip_whitelist' => 'array', 'user_id' => 'integer', 'approved_by' => 'integer', 'approved_at' => 'datetime', 'expires_at' => 'datetime', 'last_used_at' => 'datetime', 'created_at' => 'datetime', 'updated_at' => 'datetime', ]; /** * 隐藏的属性 * * @var array */ protected $hidden = [ 'app_secret', ]; /** * 获取应用状态枚举 * * @return APP_STATUS|null */ public function getStatusEnumAttribute(): ?APP_STATUS { try { return APP_STATUS::from($this->status); } catch (\ValueError $e) { return null; } } /** * 获取认证类型枚举 * * @return AUTH_TYPE|null */ public function getAuthTypeEnumAttribute(): ?AUTH_TYPE { try { return AUTH_TYPE::from($this->auth_type); } catch (\ValueError $e) { return null; } } /** * 获取状态标签 * * @return string */ public function getStatusLabelAttribute(): string { $enum = $this->getStatusEnumAttribute(); return $enum ? $enum->getLabel() : $this->status; } /** * 获取状态颜色 * * @return string */ public function getStatusColorAttribute(): string { $enum = $this->getStatusEnumAttribute(); return $enum ? $enum->getColor() : 'secondary'; } /** * 获取认证类型标签 * * @return string */ public function getAuthTypeLabelAttribute(): string { $enum = $this->getAuthTypeEnumAttribute(); return $enum ? $enum->getLabel() : $this->auth_type; } /** * 判断应用是否可以调用API * * @return bool */ public function getCanCallApiAttribute(): bool { $enum = $this->getStatusEnumAttribute(); return $enum ? $enum->canCallApi() : false; } /** * 判断应用是否已过期 * * @return bool */ public function getIsExpiredAttribute(): bool { return $this->expires_at && $this->expires_at->isPast(); } /** * 判断应用是否需要审核 * * @return bool */ public function getNeedsApprovalAttribute(): bool { $enum = $this->getStatusEnumAttribute(); return $enum ? $enum->needsApproval() : false; } /** * 获取权限范围标签 * * @return array */ public function getScopeLabelsAttribute(): array { if (empty($this->scopes)) { return []; } $labels = []; foreach ($this->scopes as $scope) { try { $enum = \App\Module\OpenAPI\Enums\SCOPE_TYPE::from($scope); $labels[] = $enum->getLabel(); } catch (\ValueError $e) { $labels[] = $scope; } } return $labels; } /** * 获取掩码后的应用密钥 * * @return string */ public function getMaskedSecretAttribute(): string { if (empty($this->app_secret)) { return ''; } $length = strlen($this->app_secret); if ($length <= 8) { return str_repeat('*', $length); } return substr($this->app_secret, 0, 4) . str_repeat('*', $length - 8) . substr($this->app_secret, -4); } /** * 生成新的应用ID * * @return string */ public static function generateAppId(): string { $prefix = config('openapi.api_key.prefix', 'ak_'); $length = config('openapi.api_key.length', 32); do { $appId = $prefix . bin2hex(random_bytes($length / 2)); } while (self::where('app_id', $appId)->exists()); return $appId; } /** * 生成新的应用密钥 * * @return string */ public static function generateAppSecret(): string { $prefix = config('openapi.api_key.secret_prefix', 'sk_'); $length = config('openapi.api_key.secret_length', 64); return $prefix . bin2hex(random_bytes($length / 2)); } /** * 按状态查询 * * @param \Illuminate\Database\Eloquent\Builder $query * @param string|APP_STATUS $status * @return \Illuminate\Database\Eloquent\Builder */ public function scopeByStatus($query, $status) { if ($status instanceof APP_STATUS) { $status = $status->value; } return $query->where('status', $status); } /** * 按用户查询 * * @param \Illuminate\Database\Eloquent\Builder $query * @param int $userId * @return \Illuminate\Database\Eloquent\Builder */ public function scopeByUser($query, int $userId) { return $query->where('user_id', $userId); } /** * 查询活跃应用 * * @param \Illuminate\Database\Eloquent\Builder $query * @return \Illuminate\Database\Eloquent\Builder */ public function scopeActive($query) { return $query->where('status', APP_STATUS::ACTIVE->value) ->where(function ($q) { $q->whereNull('expires_at') ->orWhere('expires_at', '>', now()); }); } /** * 查询需要审核的应用 * * @param \Illuminate\Database\Eloquent\Builder $query * @return \Illuminate\Database\Eloquent\Builder */ public function scopePendingApproval($query) { return $query->where('status', APP_STATUS::PENDING->value); } /** * 查询即将过期的应用 * * @param \Illuminate\Database\Eloquent\Builder $query * @param int $days * @return \Illuminate\Database\Eloquent\Builder */ public function scopeExpiringSoon($query, int $days = 30) { return $query->whereNotNull('expires_at') ->whereBetween('expires_at', [now(), now()->addDays($days)]); } /** * 更新最后使用时间 * * @return void */ public function updateLastUsed(): void { $this->update(['last_used_at' => now()]); } /** * 检查是否有指定权限 * * @param string $scope * @return bool */ public function hasScope(string $scope): bool { if (empty($this->scopes)) { return false; } return in_array($scope, $this->scopes) || in_array('*', $this->scopes); } /** * 检查应用是否激活 * * @return bool */ public function isActive(): bool { return $this->status === APP_STATUS::ACTIVE->value; } /** * 检查应用是否被暂停 * * @return bool */ public function isSuspended(): bool { return $this->status === APP_STATUS::SUSPENDED->value; } /** * 检查应用是否过期 * * @return bool */ public function isExpired(): bool { return $this->expires_at && $this->expires_at->isPast(); } /** * 检查IP是否在白名单中 * * @param string $ip * @return bool */ public function isIpAllowed(string $ip): bool { if (empty($this->ip_whitelist)) { return true; // 没有设置白名单,允许所有IP } foreach ($this->ip_whitelist as $allowedIp) { if ($this->matchIp($ip, $allowedIp)) { return true; } } return false; } /** * 匹配IP地址 * * @param string $ip * @param string $pattern * @return bool */ protected function matchIp(string $ip, string $pattern): bool { // 精确匹配 if ($ip === $pattern) { return true; } // CIDR匹配 if (strpos($pattern, '/') !== false) { list($subnet, $mask) = explode('/', $pattern); return (ip2long($ip) & ~((1 << (32 - $mask)) - 1)) === ip2long($subnet); } // 通配符匹配 if (strpos($pattern, '*') !== false) { $pattern = str_replace('*', '.*', $pattern); return preg_match('/^' . $pattern . '$/', $ip); } return false; } }