| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434 |
- <?php
- namespace App\Module\ThirdParty\Logics;
- use App\Module\ThirdParty\Models\ThirdPartyService;
- use App\Module\ThirdParty\Models\ThirdPartyCredential;
- use App\Module\ThirdParty\Enums\AUTH_TYPE;
- use Illuminate\Support\Collection;
- use Illuminate\Support\Facades\Crypt;
- /**
- * 第三方服务认证凭证业务逻辑类
- */
- class CredentialLogic
- {
- /**
- * 创建新的认证凭证
- *
- * @param array $data
- * @return ThirdPartyCredential
- * @throws \Exception
- */
- public static function createCredential(array $data): ThirdPartyCredential
- {
- // 验证服务是否存在
- $service = ThirdPartyService::find($data['service_id']);
- if (!$service) {
- throw new \Exception("服务不存在: {$data['service_id']}");
- }
- // 验证认证类型
- $authType = AUTH_TYPE::tryFrom($data['type']);
- if (!$authType) {
- throw new \Exception("不支持的认证类型: {$data['type']}");
- }
- // 验证认证类型与服务匹配
- if ($data['type'] !== $service->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,
- ];
- }
- }
|