time()) { return self::$tokenCache[$cacheKey]['token']; } // 获取应用配置 $app = CompanyFeishuApp::find($appId); if (!$app || $app->status !== 1) { throw new Exception('飞书应用配置不存在或已禁用'); } // 请求新的 token try { $response = HttpClient::httpPost(self::APP_ACCESS_TOKEN_URL, [ 'app_id' => $app->app_id, 'app_secret' => $app->app_secret, ]); if ($response['code'] !== 0) { $errorMsg = $response['msg'] ?? $response['message'] ?? '未知错误'; throw new Exception('获取 app_access_token 失败: ' . $errorMsg); } $token = $response['tenant_access_token']; $expire = $response['expire'] ?? 7200; // 缓存 token(提前5分钟过期) self::$tokenCache[$cacheKey] = [ 'token' => $token, 'expire' => time() + $expire - 300, ]; error_log("✅ 获取 app_access_token 成功: " . substr($token, 0, 30) . "..."); return $token; } catch (Exception $e) { error_log("❌ 获取 app_access_token 失败: " . $e->getMessage()); throw $e; } } /** * 使用 app_access_token 获取 user_access_token * * @param string $code 飞书授权码 * @param int $appId 飞书应用ID * @return string user_access_token * @throws Exception */ public static function getUserAccessToken(string $code, int $appId): string { $appAccessToken = self::getAppAccessToken($appId); $app = CompanyFeishuApp::find($appId); if (!$app || $app->status !== 1) { throw new Exception('飞书应用配置不存在或已禁用'); } error_log("🔍 获取 user_access_token, auth_code: " . substr($code, 0, 10) . "..."); error_log("🔍 使用 app_id: " . $app->app_id); error_log("👑 app_access_token: " . substr($appAccessToken, 0, 30) . "..."); try { $response = HttpClient::httpPost( self::USER_ACCESS_TOKEN_URL, [ 'grant_type' => 'authorization_code', 'client_id' => $app->app_id, 'client_secret' => $app->app_secret, 'code' => $code, ], [ 'Authorization: Bearer ' . $appAccessToken, ] ); error_log("📝 获取 user_access_token 响应: " . json_encode($response)); if ($response['code'] !== 0) { $errorMsg = $response['msg'] ?? $response['message'] ?? '未知错误'; throw new Exception('获取 user_access_token 失败: ' . $errorMsg); } if (!isset($response['data']['access_token'])) { throw new Exception('响应中缺少 access_token: ' . json_encode($response)); } $userAccessToken = $response['data']['access_token']; error_log("✅ 获取 user_access_token 成功: " . substr($userAccessToken, 0, 20) . "..."); return $userAccessToken; } catch (Exception $e) { error_log("❌ 获取 user_access_token 失败: " . $e->getMessage()); throw $e; } } /** * 使用 user_access_token 获取用户信息 * * @param string $userAccessToken user_access_token * @return array 用户信息 * @throws Exception */ public static function getUserInfo(string $userAccessToken): array { error_log("🔍 获取用户信息, user_access_token: " . substr($userAccessToken, 0, 20) . "..."); try { $response = HttpClient::httpGet( self::USER_INFO_URL, [], [ 'Authorization: Bearer ' . $userAccessToken, ] ); error_log("📝 获取用户信息响应: " . json_encode($response)); if ($response['code'] !== 0) { $errorMsg = $response['msg'] ?? $response['message'] ?? '未知错误'; throw new Exception('获取用户信息失败: ' . $errorMsg); } $userInfo = $response['data'] ?? []; error_log("✅ 获取用户信息成功: " . ($userInfo['name'] ?? '未知')); return $userInfo; } catch (Exception $e) { error_log("❌ 获取用户信息失败: " . $e->getMessage()); throw $e; } } /** * 清除指定应用的 token 缓存 * * @param int $appId 飞书应用ID */ public static function clearTokenCache(int $appId): void { $cacheKey = "feishu:app_access_token:{$appId}"; unset(self::$tokenCache[$cacheKey]); error_log("🗑️ 已清除应用 {$appId} 的 token 缓存"); } /** * 清除所有 token 缓存 */ public static function clearAllTokenCache(): void { self::$tokenCache = []; error_log("🗑️ 已清除所有 token 缓存"); } /** * 获取完整的用户信息(一步完成整个授权流程) * * @param string $code 飞书授权码 * @param int $appId 飞书应用ID * @return array 用户信息 * @throws Exception */ public static function getCompleteUserInfo(string $code, int $appId): array { $userAccessToken = self::getUserAccessToken($code, $appId); return self::getUserInfo($userAccessToken); } }