baseUri = 'https://open.larksuite.com/open-apis/'; } else { $this->baseUri = 'https://open.feishu.cn/open-apis/'; } $this->appId = $appId; $this->appSecret = $appSecret; $this->abbr = $abbr; $this->client = new Client([ 'base_uri' => $this->baseUri ]); $this->options = [ 'headers' => [ 'Content-Type' => 'application/json; charset=utf-8' ] ]; } /** * 异常 */ public function error() { return $this->error; } /** * 请求 */ public function requestResult($url = "", $method = "POST") { try { $res = $this->client->request($method, $this->baseUri.$url, $this->options); if ($res->getBody()) { $result = json_decode($res->getBody(), true); // var_dump($result); if ($result["code"] === 0) { return $result; } else { $this->error = $result["msg"]; return false; } } else { $this->error = "result error"; return false; } } catch (RequestException $e) { if ($e->hasResponse()) { var_dump('2 :'.$e->getResponse()->getBody()); throw new BusinessException($e->getResponse()->getBody()); } else { var_dump('3 :'.$e->getMessage()); throw new BusinessException($e->getMessage()); } } catch (\Throwable $th) { var_dump('4 :'.$th->getFile()."|".$th->getLine()."|".$th->getMessage()); $this->error = $th->getFile()."|".$th->getLine()."|".$th->getMessage(); return false; } } /** * 授权 */ public function authorize($certificate,$mobile, $url = 'authen/v1/authorize') { $arr = [ 'app_id' => $certificate['app_id'], 'redirect_uri' => $mobile == 1 ? $certificate['m_url'] : $certificate['r_url'], 'scope' => $certificate['scope'], 'state' => $certificate['state'] ]; $queryData = http_build_query($arr); return $this->baseUri.$url.'?'.$queryData; } /** * 自建应用获取 tenant_access_token */ public function tenantAccessToken() { $tenantAccessToken = Cache::get($this->abbr.'-tenantAccessToken'); if (!$tenantAccessToken) { $this->options["json"] = [ "app_id" => $this->appId, "app_secret" => $this->appSecret ]; $rst = $this->requestResult('auth/v3/tenant_access_token/internal', 'POST'); if ($rst) { $tenantAccessToken = $rst['tenant_access_token']; Cache::set($this->abbr.'-tenantAccessToken', $tenantAccessToken, $rst['expire']); } else { throw new BusinessException('tenant_access_token 获取失败'); } } return $tenantAccessToken; } /** * 通过手机号或邮箱获取用户 ID */ public function getUserIdByPhoneOrEmail($phone = '', $userIdType = 'open_id') { $this->options["headers"] = [ "Authorization" => "Bearer " . $this->tenantAccessToken(), "Content-Type" => "application/json; charset=utf-8" ]; $this->options["json"] = [ "emails" => [], "mobiles" => [$phone], "include_resigned" => false ]; $rst = $this->requestResult('contact/v3/users/batch_get_id?user_id_type='.$userIdType, 'POST'); return $rst; } /** * 发送消息 */ public function sendMessages($receiveId = '', $content = '', $msgType = 'text', $receiveIdtype = 'open_id') { $this->options["headers"] = [ "Authorization" => "Bearer " . $this->tenantAccessToken(), "Content-Type" => "application/json; charset=utf-8" ]; $this->options["json"] = [ "receive_id" => $receiveId, "msg_type" => $msgType, "content" => $content ]; $rst = $this->requestResult('im/v1/messages?receive_id_type='.$receiveIdtype, 'POST'); return $rst; } /** * 编辑消息 */ public function updateSendMessages($messageId = '', $content = '', $msgType = 'text') { $this->options["headers"] = [ "Authorization" => "Bearer " . $this->tenantAccessToken(), "Content-Type" => "application/json; charset=utf-8" ]; $this->options["json"] = [ "msg_type" => $msgType, "content" => $content ]; $rst = $this->requestResult('im/v1/messages/'.$messageId, 'PUT'); return $rst; } /** * 用户 user_access_token */ public function userAccessToken($code = "", $redirect_uri = "") { $this->options["headers"] = [ "Content-Type" => "application/json; charset=utf-8" ]; $this->options["json"] = [ "grant_type" => "authorization_code", "client_id" => $this->appId, "client_secret" => $this->appSecret, "code" => $code, "redirect_uri" => $redirect_uri ]; return $this->requestResult('authen/v2/oauth/token'); } /** * 用户信息 */ public function userInfo($userAccessToken = "") { $this->options["headers"] = [ "Authorization" => "Bearer " . $userAccessToken, "Content-Type" => "application/json; charset=utf-8" ]; return $this->requestResult('authen/v1/user_info', "GET"); } // public function user_access_token($code = "") // { // $url = ""; // $this->options["headers"] = [ // "Authorization" => "Bearer ".$this->tenantAccessToken(), // "Content-Type" => "application/json; charset=utf-8" // ]; // $this->options["json"] = [ // "grant_type" => "authorization_code", // "code" => $code // ]; // $this->options["form_params"] = [ // "app_id" => $this->app_id, // "app_secret" => $this->app_secret // ]; // return $this->requestResult($url); // } /** * 获取 JSAPI 配置(签名) * @param string $url 当前页面完整 URL(不包含 # 后面的内容) * @return array */ public function getJsApiConfig($url) { // 1. 获取 tenant_access_token //$accessToken = $this->getTenantAccessToken(); $accessToken = $this->tenantAccessToken(); // 2. 获取 jsapi_ticket $ticket = $this->getJsapiTicket($accessToken); // 3. 生成签名参数 $timestamp = (int) round(microtime(true) * 1000); $nonceStr = $this->generateNonceStr(); // 4. 生成签名 $signature = $this->generateSignature($ticket, $nonceStr, $timestamp, $url); return [ 'appId' => $this->appId, 'timestamp' => $timestamp, 'nonceStr' => $nonceStr, 'signature' => $signature, 'url' => $url ]; } /** * 获取 jsapi_ticket * @param string $accessToken * @return string */ private function getJsapiTicket($accessToken) { // 检查缓存 $cachedTicket = Cache::get($this->abbr.'-feishu:jsapi_ticket'); // $cachedTicket = $this->getCachedData('feishu:jsapi_ticket'); if ($cachedTicket) { return $cachedTicket; } // 使用 requestResult 方法发起请求,而不是 httpGet $this->options["headers"] = [ "Authorization" => "Bearer " . $accessToken, "Content-Type" => "application/json; charset=utf-8" ]; $result = $this->requestResult('jssdk/ticket/get', 'GET'); if (!$result) { throw new BusinessException('获取 jsapi_ticket 失败: ' . $this->error()); } $ticket = $result['data']['ticket']; $expire = $result['data']['expire_in'] - 300; // 提前5分钟过期 // 缓存 ticket Cache::set($this->abbr.'-feishu:jsapi_ticket', $ticket, $expire); // $this->setCachedData('feishu:jsapi_ticket', $ticket, $expire); return $ticket; } /** * 生成签名 * @param string $ticket * @param string $nonceStr * @param int $timestamp * @param string $url * @return string */ private function generateSignature($ticket, $nonceStr, $timestamp, $url) { // 拼接待签名字符串(注意顺序) $signStr = "jsapi_ticket={$ticket}&noncestr={$nonceStr}×tamp={$timestamp}&url={$url}"; return sha1($signStr); } /** * 生成随机字符串 * @param int $length * @return string */ private function generateNonceStr($length = 16) { $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; $str = ''; for ($i = 0; $i < $length; $i++) { $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); } return $str; } }