auth_type) { throw new \Exception("认证类型 {$data['type']} 与服务要求的 {$service->auth_type} 不匹配"); } // 验证凭证配置完整性 $validation = static::validateCredentialData($authType, $data['credentials'] ?? []); if (!$validation['valid']) { throw new \Exception("凭证配置不完整,缺少字段: " . implode(', ', $validation['missing_fields'])); } // 检查同环境下是否已有激活的凭证 $environment = $data['environment'] ?? 'production'; $existingActive = ThirdPartyCredential::where('service_id', $data['service_id']) ->where('environment', $environment) ->where('is_active', true) ->exists(); if ($existingActive && ($data['is_active'] ?? true)) { throw new \Exception("环境 {$environment} 下已有激活的凭证,请先停用现有凭证"); } // 设置默认值 $data = array_merge([ 'environment' => 'production', 'is_active' => true, 'usage_count' => 0, ], $data); // 创建凭证 $credential = new ThirdPartyCredential(); $credential->fill($data); // 加密敏感信息 if (isset($data['credentials'])) { $credential->setEncryptedCredentials($data['credentials']); } $credential->save(); return $credential; } /** * 更新认证凭证 * * @param ThirdPartyCredential $credential * @param array $data * @return bool * @throws \Exception */ public static function updateCredential(ThirdPartyCredential $credential, array $data): bool { // 如果更新认证类型,验证类型有效性 if (isset($data['type'])) { $authType = AUTH_TYPE::tryFrom($data['type']); if (!$authType) { throw new \Exception("不支持的认证类型: {$data['type']}"); } // 验证认证类型与服务匹配 if ($data['type'] !== $credential->service->auth_type) { throw new \Exception("认证类型 {$data['type']} 与服务要求的 {$credential->service->auth_type} 不匹配"); } } // 如果更新凭证信息,验证完整性 if (isset($data['credentials'])) { $authType = AUTH_TYPE::from($data['type'] ?? $credential->type); $validation = static::validateCredentialData($authType, $data['credentials']); if (!$validation['valid']) { throw new \Exception("凭证配置不完整,缺少字段: " . implode(', ', $validation['missing_fields'])); } } // 如果要激活凭证,检查同环境下是否已有其他激活的凭证 if (isset($data['is_active']) && $data['is_active']) { $environment = $data['environment'] ?? $credential->environment; $existingActive = ThirdPartyCredential::where('service_id', $credential->service_id) ->where('environment', $environment) ->where('is_active', true) ->where('id', '!=', $credential->id) ->exists(); if ($existingActive) { throw new \Exception("环境 {$environment} 下已有其他激活的凭证,请先停用其他凭证"); } } // 更新基本信息 $updateData = array_except($data, ['credentials']); if (!empty($updateData)) { $credential->update($updateData); } // 更新凭证信息 if (isset($data['credentials'])) { $credential->setEncryptedCredentials($data['credentials']); $credential->save(); } return true; } /** * 删除认证凭证 * * @param ThirdPartyCredential $credential * @return bool * @throws \Exception */ public static function deleteCredential(ThirdPartyCredential $credential): bool { // 检查凭证是否正在使用 if ($credential->is_active) { throw new \Exception("激活状态的凭证无法删除,请先停用凭证"); } // 检查最近是否有使用记录 if ($credential->last_used_at && $credential->last_used_at->gt(now()->subHours(1))) { throw new \Exception("凭证在1小时内有使用记录,无法删除"); } return $credential->delete(); } /** * 激活/停用凭证 * * @param ThirdPartyCredential $credential * @param bool $active * @return bool * @throws \Exception */ public static function toggleCredentialStatus(ThirdPartyCredential $credential, bool $active): bool { if ($active) { // 激活凭证前检查 if ($credential->isExpired()) { throw new \Exception("凭证已过期,无法激活"); } // 检查同环境下是否已有其他激活的凭证 $existingActive = ThirdPartyCredential::where('service_id', $credential->service_id) ->where('environment', $credential->environment) ->where('is_active', true) ->where('id', '!=', $credential->id) ->exists(); if ($existingActive) { throw new \Exception("环境 {$credential->environment} 下已有其他激活的凭证,请先停用其他凭证"); } } return $credential->update(['is_active' => $active]); } /** * 获取凭证列表 * * @param array $filters * @param array $options * @return Collection */ public static function getCredentialList(array $filters = [], array $options = []): Collection { $query = ThirdPartyCredential::with('service'); // 应用过滤条件 if (isset($filters['service_id'])) { $query->where('service_id', $filters['service_id']); } if (isset($filters['type'])) { $query->where('type', $filters['type']); } if (isset($filters['environment'])) { $query->where('environment', $filters['environment']); } if (isset($filters['is_active'])) { $query->where('is_active', $filters['is_active']); } if (isset($filters['expired'])) { if ($filters['expired']) { $query->where('expires_at', '<=', now()); } else { $query->where(function ($q) { $q->whereNull('expires_at') ->orWhere('expires_at', '>', now()); }); } } if (isset($filters['search'])) { $search = $filters['search']; $query->where(function ($q) use ($search) { $q->where('name', 'like', "%{$search}%") ->orWhereHas('service', function ($sq) use ($search) { $sq->where('name', 'like', "%{$search}%") ->orWhere('code', 'like', "%{$search}%"); }); }); } // 应用排序 $sortBy = $options['sort_by'] ?? 'created_at'; $sortOrder = $options['sort_order'] ?? 'desc'; $query->orderBy($sortBy, $sortOrder); return $query->get(); } /** * 获取即将过期的凭证 * * @param int $days 提前天数 * @return Collection */ public static function getExpiringCredentials(int $days = 7): Collection { return ThirdPartyCredential::with('service') ->where('is_active', true) ->whereNotNull('expires_at') ->whereBetween('expires_at', [now(), now()->addDays($days)]) ->orderBy('expires_at') ->get(); } /** * 获取未使用的凭证 * * @param int $days 天数 * @return Collection */ public static function getUnusedCredentials(int $days = 30): Collection { return ThirdPartyCredential::with('service') ->where('is_active', true) ->where(function ($query) use ($days) { $query->whereNull('last_used_at') ->orWhere('last_used_at', '<', now()->subDays($days)); }) ->orderBy('created_at') ->get(); } /** * 验证凭证数据完整性 * * @param AUTH_TYPE $authType * @param array $credentials * @return array */ public static function validateCredentialData(AUTH_TYPE $authType, array $credentials): array { $requiredFields = $authType->getRequiredFields(); $missingFields = []; foreach ($requiredFields as $field) { if (empty($credentials[$field])) { $missingFields[] = $field; } } return [ 'valid' => empty($missingFields), 'missing_fields' => $missingFields, 'required_fields' => $requiredFields, ]; } /** * 测试凭证连接 * * @param ThirdPartyCredential $credential * @return array */ public static function testCredential(ThirdPartyCredential $credential): array { $result = [ 'success' => false, 'message' => '', 'details' => [], ]; try { // 检查凭证状态 if (!$credential->isUsable()) { $result['message'] = '凭证不可用'; return $result; } // 验证凭证配置 $validation = $credential->validateCredentials(); if (!$validation['valid']) { $result['message'] = '凭证配置不完整: ' . implode(', ', $validation['missing_fields']); return $result; } // 生成认证头 $headers = $credential->generateAuthHeaders(); if (empty($headers)) { $result['message'] = '无法生成认证头'; return $result; } $result['success'] = true; $result['message'] = '凭证测试成功'; $result['details'] = [ 'auth_type' => $credential->getTypeLabel(), 'environment' => $credential->environment, 'expires_at' => $credential->expires_at?->toDateTimeString(), 'usage_count' => $credential->usage_count, ]; } catch (\Exception $e) { $result['message'] = '凭证测试失败: ' . $e->getMessage(); } return $result; } /** * 轮换凭证 * * @param ThirdPartyCredential $credential * @param array $newCredentials * @return ThirdPartyCredential * @throws \Exception */ public static function rotateCredential(ThirdPartyCredential $credential, array $newCredentials): ThirdPartyCredential { // 验证新凭证数据 $authType = AUTH_TYPE::from($credential->type); $validation = static::validateCredentialData($authType, $newCredentials); if (!$validation['valid']) { throw new \Exception("新凭证配置不完整,缺少字段: " . implode(', ', $validation['missing_fields'])); } // 创建新凭证 $newCredential = static::createCredential([ 'service_id' => $credential->service_id, 'name' => $credential->name . '_rotated_' . now()->format('YmdHis'), 'type' => $credential->type, 'credentials' => $newCredentials, 'environment' => $credential->environment, 'is_active' => false, // 先创建为非激活状态 'expires_at' => $credential->expires_at, ]); // 停用旧凭证 $credential->update(['is_active' => false]); // 激活新凭证 $newCredential->update(['is_active' => true]); return $newCredential; } /** * 获取凭证统计信息 * * @return array */ public static function getCredentialStats(): array { $total = ThirdPartyCredential::count(); $active = ThirdPartyCredential::where('is_active', true)->count(); $expired = ThirdPartyCredential::where('expires_at', '<=', now())->count(); $expiringSoon = ThirdPartyCredential::where('is_active', true) ->whereNotNull('expires_at') ->whereBetween('expires_at', [now(), now()->addDays(7)]) ->count(); $envStats = ThirdPartyCredential::selectRaw('environment, COUNT(*) as count') ->groupBy('environment') ->pluck('count', 'environment') ->toArray(); $typeStats = ThirdPartyCredential::selectRaw('type, COUNT(*) as count') ->groupBy('type') ->pluck('count', 'type') ->toArray(); return [ 'total' => $total, 'active' => $active, 'expired' => $expired, 'expiring_soon' => $expiringSoon, 'by_environment' => $envStats, 'by_type' => $typeStats, 'health_rate' => $total > 0 ? round((($active - $expired) / $total) * 100, 2) : 0, ]; } }