123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- <?php
- namespace App\Service;
- use App\Log;
- use App\Models\AuthorizeCorp;
- use App\RedisModel;
- class AccessTokenService
- {
- const ACCESS_TOKEN_REQUEST_LOCK = 'Playlet::AccessTokenRequestLockRds_';
- const ACCESS_TOKEN_SERVER_PROXY = 'Playlet::AccessTokenServerProxyKey_';
- /**
- * 获取代开发应用AccessToken
- * */
- public static function getAccessToken($corpid, $from='')
- {
- # 判断是否为待开发应用
- $accessTokenStr = RedisModel::get(TokenService::ACCESS_TOKEN_RDS. '_' .$corpid);
- // Log::logInfo('【'.$corpid.'】Redis取出的结果为'.$accessTokenStr, [], 'GetAccessTokenTrace');
- if(!empty($accessTokenStr)) {
- // Log::logInfo('【'.$corpid.'】发现了结果,开始解析:'.$accessTokenStr, [], 'GetAccessTokenTrace');
- $accessTokenData = json_decode($accessTokenStr, true);
- $accessToken = isset($accessTokenData['access_token']) ?
- $accessTokenData['access_token'] : '';
- $expire = $accessTokenData['expire'];
- $updateTime = isset($accessTokenData['update_time']) ? $accessTokenData['update_time'] : 0;
- if($expire <= time() || empty($accessToken) || $updateTime < time()) {
- self::refreshAccessToken($corpid, $accessToken, $from);
- }
- } else {
- $accessToken = '';
- self::refreshAccessToken($corpid, $accessToken, $from);
- }
- return $accessToken;
- }
- /**
- * 调用接口刷新AccessToken
- * @param string $corpid 企业ID
- * @param string $accessToken 当前缓存的access_token,更新成功后会返回新access_token
- * @param string $from 获取令牌的方法说明
- * @param int $retry 重试次数
- * */
- public static function refreshAccessToken($corpid, &$accessToken, $from='',$retry=0)
- {
- # 判断请求锁 新加同一个服务器限定锁,不影响其他服务器
- $nx = env('ACCESS_TOKEN_SERVER_PROXY_ID') ?? '';
- $lockJson = RedisModel::get(AccessTokenService::ACCESS_TOKEN_REQUEST_LOCK . $corpid. $nx);
- if(!empty($lockJson)) {
- $lockData = json_decode($lockJson, true);
- $lockStatus = $lockData['lock_status'];
- if($lockStatus) { // 请求锁开启
- $requestTime = $lockData['request_time'];
- if($requestTime > time()) { // 请求锁截止时间大于当前时间,则不执行刷新请求
- return false;
- }
- }
- }
- $secret = AuthorizeCorp::where('corpid', $corpid)->value('permanent_code');
- $redisKey = TokenService::ACCESS_TOKEN_RDS. '_' .$corpid;
- # 获取令牌API地址
- $apiRefreshTokenUrl = config('qyWechat.get_access_token');
- $apiRefreshTokenUrl .= $corpid .'&corpsecret='.$secret;
- $response = HttpService::httpGet($apiRefreshTokenUrl);
- $responseData = json_decode($response, true);
- if(empty($responseData) || (isset($responseData['errcode']) && $responseData['errcode'])) {
- Log::logError('获取accessToken失败', [
- 'corpid' => $corpid,
- 'response' => $responseData
- ], 'AccessToken');
- $errCode = isset($responseData['errcode']) ? $responseData['errcode'] : '';
- if($errCode == 45009) { // 提示Api请求超限,设置请求锁
- AccessTokenService::setRequestLock($corpid, time() + 6*60, 1);
- }
- if($errCode == 40001) { // 企微账号已解除授权
- AuthorizeCorp::where('corpid', $corpid)->update(['enable' => 0]);
- return false;
- }
- //新加5/7 失败后塞入队列,用其他服务器更新
- $freq_k = self::ACCESS_TOKEN_SERVER_PROXY;
- if( is_int($from) && $from>=80001 && $from<=80002){
- $rkn = $from + 1;
- if($rkn === 80003){
- $rkn = 80001;
- }
- } else {
- $rkn = '8000' . mt_rand(1, 2);
- }
- RedisModel::lPush($freq_k.$rkn, $corpid);
- return false;
- }
- $accessToken = $responseData['access_token'];
- $saveData = [
- 'access_token' => $accessToken,
- 'expire' => $responseData['expires_in'] + time(),
- 'update_time' => time() + 3600
- ];
- # 解除AccessToken请求锁
- AccessTokenService::setRequestLock($corpid, time(), 0);
- # 将accessToken存入Redis
- RedisModel::set($redisKey, json_encode($saveData));
- RedisModel::expire($redisKey, 7200);
- return 0;
- }
- /**
- * 设置AccessToken请求锁
- * @param $corpid string 企业id
- * @param $requestTime integer 下次允许请求刷新令牌的时间戳
- * @param $lockStatus integer 锁状态 0关闭 1打开
- * */
- public static function setRequestLock($corpid, $requestTime, $lockStatus)
- {
- $lockInfo = [
- 'request_time' => $requestTime,
- 'lock_status' => $lockStatus
- ];
- $nx = env('ACCESS_TOKEN_SERVER_PROXY_ID') ?? '';
- RedisModel::set(AccessTokenService::ACCESS_TOKEN_REQUEST_LOCK . $corpid . $nx, json_encode($lockInfo));
- }
- /**
- * 获取企业jsapiTicket
- * */
- public static function getJsapiTicket($corpid)
- {
- $jsapiTicketData = RedisModel::getAfterDecode(TokenService::JSAPI_TICKET_RDS . $corpid);
- if(!empty($jsapiTicketData)) {
- $jsapiTicket = $jsapiTicketData['ticket'] ?? '';
- } else {
- $responseData = self::refreshJsapiTicket($corpid);
- if(isset($responseData['errcode']) && $responseData['errcode']) {
- Log::logError('JsapiTicket获取失败', [
- 'response' => $responseData,
- 'corpid' => $corpid
- ], 'GetJsapiTicket');
- return false;
- }
- # 检出jsapiTicket并缓存到Redis
- $jsapiTicket = $responseData['ticket'] ?? false;
- $expireIn = $responseData['expires_in'] ?? 0;
- RedisModel::setAfterEncode(TokenService::JSAPI_TICKET_RDS . $corpid, $responseData);
- RedisModel::expire(TokenService::JSAPI_TICKET_RDS . $corpid, $expireIn);
- }
- return $jsapiTicket;
- }
- /**
- * 调用接口刷新JsapiTicket
- * @param string $corpid 企业ID
- * @param string $accessToken 当前缓存的access_token,更新成功后会返回新access_token
- * */
- public static function refreshJsapiTicket($corpid)
- {
- # 获取accessToken
- $accessToken = AuthorizeCorp::getAccessToken($corpid, '刷新JsapiTicket');
- if(empty($accessToken)) {
- return false;
- }
- $requestUri = config('qyWechat.get_jsapi_ticket');
- $requestUri .= $accessToken;
- $response = HttpService::httpGet($requestUri);
- $responseData = json_decode($response, true);
- return $responseData;
- }
- /**
- * 获取应用jsapiTicket
- * */
- public static function getAppJsapiTicket($corpid)
- {
- $jsapiTicketData = RedisModel::getAfterDecode(TokenService::APP_JSAPI_TICKET_RDS . $corpid);
- if(!empty($jsapiTicketData)) {
- $jsapiTicket = $jsapiTicketData['ticket'] ?? '';
- } else {
- $responseData = self::refreshAppJsapiTicket($corpid);
- if(isset($responseData['errcode']) && $responseData['errcode']) {
- Log::logError('AppJsapiTicket获取失败', [
- 'response' => $responseData,
- 'corpid' => $corpid
- ], 'GetAppJsapiTicket');
- return false;
- }
- # 检出jsapiTicket并缓存到Redis
- $jsapiTicket = $responseData['ticket'] ?? false;
- $expireIn = $responseData['expires_in'] ?? 0;
- RedisModel::setAfterEncode(TokenService::APP_JSAPI_TICKET_RDS . $corpid, $responseData);
- RedisModel::expire(TokenService::APP_JSAPI_TICKET_RDS . $corpid, $expireIn);
- }
- return $jsapiTicket;
- }
- /**
- * 调用接口刷新应用JsapiTicket
- * @param string $corpid 企业ID
- * @param string $accessToken 当前缓存的access_token,更新成功后会返回新access_token
- * */
- public static function refreshAppJsapiTicket($corpid)
- {
- # 获取accessToken
- $accessToken = AuthorizeCorp::getAccessToken($corpid, '刷新JsapiTicket');
- if(empty($accessToken)) {
- return false;
- }
- $requestUri = config('qyWechat.get_app_jsapi_ticket');
- $requestUri .= $accessToken;
- $response = HttpService::httpGet($requestUri);
- $responseData = json_decode($response, true);
- return $responseData;
- }
- }
|