diff --git a/app/controller/FeishuController.php b/app/controller/FeishuController.php new file mode 100644 index 0000000..3465cb0 --- /dev/null +++ b/app/controller/FeishuController.php @@ -0,0 +1,186 @@ +all(); + + // 获取凭证 + $certificate = config('feishu.mbaf'); + + // 飞书服务 + $class = new \app\service\FeishuService($certificate['appId'], $certificate['appSecret'], $certificate['state']); + + if (isset($param['code']) && $param['code']) { + + // 获取token + $userAccessToken = $class->userAccessToken($param['code'], $certificate['rUrl']); + + // 获取用户信息 + $rst = $class->userInfo($userAccessToken['access_token']); + if ($rst['code'] != 0) { + throw new BusinessException('授权失败,请重试'); + } + + // var_dump($rst); + // array(3) { + // ["code"]=> + // int(0) + // ["data"]=> + // array(11) { + // ["avatar_big"]=> + // string(168) "https://s1-imfile.feishucdn.com/static-resource/v1/v3_00ie_7cc82f38-0a89-43d4-8b2c-d90fc9d9176g~?image_size=640x640&cut_type=&quality=&format=image&sticker_format=.webp" + // ["avatar_middle"]=> + // string(168) "https://s1-imfile.feishucdn.com/static-resource/v1/v3_00ie_7cc82f38-0a89-43d4-8b2c-d90fc9d9176g~?image_size=240x240&cut_type=&quality=&format=image&sticker_format=.webp" + // ["avatar_thumb"]=> + // string(166) "https://s1-imfile.feishucdn.com/static-resource/v1/v3_00ie_7cc82f38-0a89-43d4-8b2c-d90fc9d9176g~?image_size=72x72&cut_type=&quality=&format=image&sticker_format=.webp" + // ["avatar_url"]=> + // string(166) "https://s1-imfile.feishucdn.com/static-resource/v1/v3_00ie_7cc82f38-0a89-43d4-8b2c-d90fc9d9176g~?image_size=72x72&cut_type=&quality=&format=image&sticker_format=.webp" + // ["en_name"]=> + // string(9) "李佳博" + // ["mobile"]=> + // string(14) "+8615738985690" + // ["name"]=> + // string(9) "李佳博" + // ["open_id"]=> + // string(35) "ou_37bb7cfc4311c858ea32461700fa1a43" + // ["tenant_key"]=> + // string(16) "14e7bc0fe5985740" + // ["union_id"]=> + // string(35) "on_a3fdbca48cd9477a140c7f2167a63691" + // ["user_id"]=> + // string(8) "481g947d" + // } + // ["msg"]=> + // string(7) "success" + // } + + return json($rst); + } else { + // 授权地址 + $rst = $class->authorize($certificate); + // 拉起授权 + return redirect($rst); + } + } + + + public function send() + { + // 获取凭证 + $certificate = config('feishu.mbaf'); + + // 飞书服务 + $class = new \app\service\FeishuService($certificate['appId'], $certificate['appSecret'], $certificate['state']); + + // $receiveId = 'ou_37bb7cfc4311c858ea32461700fa1a43'; + $receiveId = 'ou_a3b97543acbf1b28d978356bb08f85dd'; + + // 富文本 类型 + // $content = "{ + // \"zh_cn\":{ + // \"title\":\"演示-消息提醒\", + // \"content\":[ + // [{ + // \"tag\":\"text\", + // \"text\":\"示例内容 - demo\", + // \"style\":[\"bold\"] + // }], + // [{ + // \"tag\":\"hr\" + // }], + // [{ + // \"tag\":\"a\", + // \"href\":\"http://219.157.255.213:25321/feishu/show\", + // \"text\":\"查看详情\" + // }] + // ] + // } + // }"; + + // 卡片 类型 + $content = "{ + \"config\":{ + \"wide_screen_mode\":true + }, + \"elements\":[{ + \"tag\":\"div\", + \"text\":{ + \"content\":\"**测试内容**,测试内容,测试内容。\\n 测试内容,测试内容,测试内容。\\n\\n 📬 示例1 示例1 示例1 \\n 😍 示例1 示例1 示例1 \\n 📝 示例1 示例1 示例1 \\n 🙌 示例1 示例1 示例1 \", + \"tag\":\"lark_md\" + }},{ + \"actions\":[{ + \"tag\":\"button\", + \"text\":{ + \"content\":\"立即查看\", + \"tag\":\"plain_text\" + }, + \"type\":\"primary\", + \"url\":\"http://219.157.255.213:25321/feishu/show\" + }], + \"tag\":\"action\" + }], + \"header\":{ + \"template\":\"turquoise\", + \"title\":{ + \"content\":\"📚标题\", + \"tag\":\"plain_text\" + } + } + }"; + $msgType = 'interactive'; + $receiveIdtype = 'open_id'; + + $result = $class->sendMessages($receiveId, $content, $msgType, $receiveIdtype); + return json($result); + } + + public function show() + { + return '
查看详情 - 示例页面
'; + } +} + +// 凭证 +function certificate($gs = ''){ + $data = []; + switch ($gs) { + case "xinyouhe": + $data = [ + 'appId' => "cli_a73e1e6406fa501c", + 'appSecret' => "9cMfxujjGG9JPmNfNcOwUdCoIXqaM1wY", + 'rUrl' => "https://server.xyh.hnzhwlkj.club", + 'scope' => "contact:user.base:readonly contact:contact.base:readonly contact:user.employee_id:readonly im:message:send_as_bot", + 'state' => "xinyouhe" + ]; + break; + + case "mbaf": + $data = [ + 'appId' => "cli_a75839fb4ff85028", + 'appSecret' => "VsqLvwsRzpmisBVbrTIuKdNCvRCO447d", + 'rUrl' => "https://server.xyh.hnzhwlkj.club", + 'scope' => "contact:user.base:readonly contact:contact.base:readonly contact:user.employee_id:readonly im:message:send_as_bot", + 'state' => "mbaf" + ]; + break; + + default: + throw new BusinessException('未授权应用'); + break; + } + return $data; +} \ No newline at end of file diff --git a/app/service/FeishuService.php b/app/service/FeishuService.php new file mode 100644 index 0000000..e38e6a8 --- /dev/null +++ b/app/service/FeishuService.php @@ -0,0 +1,210 @@ +baseUri = 'https://open.larksuite.com/open-apis/'; + } else { + $this->baseUri = 'https://open.feishu.cn/open-apis/'; + } + + $this->appId = $appId; + $this->appSecret = $appSecret; + $this->gs = $gs; + + $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, $url = 'authen/v1/authorize') + { + $arr = [ + 'app_id' => $certificate['appId'], + 'redirect_uri' => $certificate['rUrl'], + '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->gs.'-tenantAccessToken'); + if (!$tenantAccessToken) { + $this->options["json"] = [ + "app_id" => $this->appId, + "app_secret" => $this->appSecret + ]; + $rst = $this->requestResult('auth/v3/tenant_access_token/internal', 'POST'); + // var_dump($rst); + if ($rst) { + $tenantAccessToken = $rst['tenant_access_token']; + Cache::set($this->gs.'-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"); + } +} \ No newline at end of file diff --git a/config/cache.php b/config/cache.php new file mode 100644 index 0000000..74775dc --- /dev/null +++ b/config/cache.php @@ -0,0 +1,31 @@ + + * @copyright walkor + * @link http://www.workerman.net/ + * @license http://www.opensource.org/licenses/mit-license.php MIT License + */ + +return [ + 'default' => 'file', + 'stores' => [ + 'file' => [ + 'driver' => 'file', + 'path' => runtime_path('cache') + ], + 'redis' => [ + 'driver' => 'redis', + 'connection' => 'default' + ], + 'array' => [ + 'driver' => 'array' + ] + ] +]; \ No newline at end of file diff --git a/config/feishu.php b/config/feishu.php new file mode 100644 index 0000000..4b30c07 --- /dev/null +++ b/config/feishu.php @@ -0,0 +1,20 @@ + [ + 'appId' => "cli_a73e1e6406fa501c", + 'appSecret' => "9cMfxujjGG9JPmNfNcOwUdCoIXqaM1wY", + 'rUrl' => "http://219.157.255.213:25321/feishu/auth", + 'scope' => "contact:user.base:readonly contact:contact.base:readonly contact:user.employee_id:readonly im:message:send_as_bot", + 'state' => "xinyouhe", + 'baseUri' => "https://open.feishu.cn/open-apis/" + ], + 'mbaf' => [ + 'appId' => "cli_a75839fb4ff85028", + 'appSecret' => "VsqLvwsRzpmisBVbrTIuKdNCvRCO447d", + 'rUrl' => "http://219.157.255.213:25321/feishu/auth", + 'scope' => "contact:user.base:readonly contact:contact.base:readonly contact:user.employee_id:readonly im:message:send_as_bot", + 'state' => "mbaf", + 'baseUri' => "https://open.larksuite.com/open-apis/" + ] +]; \ No newline at end of file diff --git a/config/route.php b/config/route.php index 17514e0..7ad9935 100644 --- a/config/route.php +++ b/config/route.php @@ -27,7 +27,7 @@ Route::any('/login', [app\controller\UserController::class, 'login'])->middlewar Route::any('/upload', [app\controller\UploadController::class, 'uploadImage'])->middleware([app\middleware\CrossDomain::class]); - - - - +// 飞书 +Route::post('/feishu/auth', [app\controller\FeishuController::class, 'auth']); +Route::post('/feishu/show', [app\controller\FeishuController::class, 'show']); +Route::post('/feishu/send', [app\controller\FeishuController::class, 'send']); \ No newline at end of file diff --git a/start.php b/start.php index 2399638..41ad7ef 100644 --- a/start.php +++ b/start.php @@ -2,5 +2,4 @@