企微短剧业务系统

AccessTokenService.php 8.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. <?php
  2. namespace App\Service;
  3. use App\Log;
  4. use App\Models\AuthorizeCorp;
  5. use App\RedisModel;
  6. class AccessTokenService
  7. {
  8. const ACCESS_TOKEN_REQUEST_LOCK = 'Playlet::AccessTokenRequestLockRds_';
  9. const ACCESS_TOKEN_SERVER_PROXY = 'Playlet::AccessTokenServerProxyKey_';
  10. /**
  11. * 获取代开发应用AccessToken
  12. * */
  13. public static function getAccessToken($corpid, $from='')
  14. {
  15. # 判断是否为待开发应用
  16. $accessTokenStr = RedisModel::get(TokenService::ACCESS_TOKEN_RDS. '_' .$corpid);
  17. // Log::logInfo('【'.$corpid.'】Redis取出的结果为'.$accessTokenStr, [], 'GetAccessTokenTrace');
  18. if(!empty($accessTokenStr)) {
  19. // Log::logInfo('【'.$corpid.'】发现了结果,开始解析:'.$accessTokenStr, [], 'GetAccessTokenTrace');
  20. $accessTokenData = json_decode($accessTokenStr, true);
  21. $accessToken = isset($accessTokenData['access_token']) ?
  22. $accessTokenData['access_token'] : '';
  23. $expire = $accessTokenData['expire'];
  24. $updateTime = isset($accessTokenData['update_time']) ? $accessTokenData['update_time'] : 0;
  25. if($expire <= time() || empty($accessToken) || $updateTime < time()) {
  26. self::refreshAccessToken($corpid, $accessToken, $from);
  27. }
  28. } else {
  29. $accessToken = '';
  30. self::refreshAccessToken($corpid, $accessToken, $from);
  31. }
  32. return $accessToken;
  33. }
  34. /**
  35. * 调用接口刷新AccessToken
  36. * @param string $corpid 企业ID
  37. * @param string $accessToken 当前缓存的access_token,更新成功后会返回新access_token
  38. * @param string $from 获取令牌的方法说明
  39. * @param int $retry 重试次数
  40. * */
  41. public static function refreshAccessToken($corpid, &$accessToken, $from='',$retry=0)
  42. {
  43. # 判断请求锁 新加同一个服务器限定锁,不影响其他服务器
  44. $nx = env('ACCESS_TOKEN_SERVER_PROXY_ID') ?? '';
  45. $lockJson = RedisModel::get(AccessTokenService::ACCESS_TOKEN_REQUEST_LOCK . $corpid. $nx);
  46. if(!empty($lockJson)) {
  47. $lockData = json_decode($lockJson, true);
  48. $lockStatus = $lockData['lock_status'];
  49. if($lockStatus) { // 请求锁开启
  50. $requestTime = $lockData['request_time'];
  51. if($requestTime > time()) { // 请求锁截止时间大于当前时间,则不执行刷新请求
  52. return false;
  53. }
  54. }
  55. }
  56. $secret = AuthorizeCorp::where('corpid', $corpid)->value('permanent_code');
  57. $redisKey = TokenService::ACCESS_TOKEN_RDS. '_' .$corpid;
  58. # 获取令牌API地址
  59. $apiRefreshTokenUrl = config('qyWechat.get_access_token');
  60. $apiRefreshTokenUrl .= $corpid .'&corpsecret='.$secret;
  61. $response = HttpService::httpGet($apiRefreshTokenUrl);
  62. $responseData = json_decode($response, true);
  63. if(empty($responseData) || (isset($responseData['errcode']) && $responseData['errcode'])) {
  64. Log::logError('获取accessToken失败', [
  65. 'corpid' => $corpid,
  66. 'response' => $responseData
  67. ], 'AccessToken');
  68. $errCode = isset($responseData['errcode']) ? $responseData['errcode'] : '';
  69. if($errCode == 45009) { // 提示Api请求超限,设置请求锁
  70. AccessTokenService::setRequestLock($corpid, time() + 6*60, 1);
  71. }
  72. if($errCode == 40001) { // 企微账号已解除授权
  73. AuthorizeCorp::where('corpid', $corpid)->update(['enable' => 0]);
  74. return false;
  75. }
  76. //新加5/7 失败后塞入队列,用其他服务器更新
  77. $freq_k = self::ACCESS_TOKEN_SERVER_PROXY;
  78. if( is_int($from) && $from>=80001 && $from<=80002){
  79. $rkn = $from + 1;
  80. if($rkn === 80003){
  81. $rkn = 80001;
  82. }
  83. } else {
  84. $rkn = '8000' . mt_rand(1, 2);
  85. }
  86. RedisModel::lPush($freq_k.$rkn, $corpid);
  87. return false;
  88. }
  89. $accessToken = $responseData['access_token'];
  90. $saveData = [
  91. 'access_token' => $accessToken,
  92. 'expire' => $responseData['expires_in'] + time(),
  93. 'update_time' => time() + 3600
  94. ];
  95. # 解除AccessToken请求锁
  96. AccessTokenService::setRequestLock($corpid, time(), 0);
  97. # 将accessToken存入Redis
  98. RedisModel::set($redisKey, json_encode($saveData));
  99. RedisModel::expire($redisKey, 7200);
  100. return 0;
  101. }
  102. /**
  103. * 设置AccessToken请求锁
  104. * @param $corpid string 企业id
  105. * @param $requestTime integer 下次允许请求刷新令牌的时间戳
  106. * @param $lockStatus integer 锁状态 0关闭 1打开
  107. * */
  108. public static function setRequestLock($corpid, $requestTime, $lockStatus)
  109. {
  110. $lockInfo = [
  111. 'request_time' => $requestTime,
  112. 'lock_status' => $lockStatus
  113. ];
  114. $nx = env('ACCESS_TOKEN_SERVER_PROXY_ID') ?? '';
  115. RedisModel::set(AccessTokenService::ACCESS_TOKEN_REQUEST_LOCK . $corpid . $nx, json_encode($lockInfo));
  116. }
  117. /**
  118. * 获取企业jsapiTicket
  119. * */
  120. public static function getJsapiTicket($corpid)
  121. {
  122. $jsapiTicketData = RedisModel::getAfterDecode(TokenService::JSAPI_TICKET_RDS . $corpid);
  123. if(!empty($jsapiTicketData)) {
  124. $jsapiTicket = $jsapiTicketData['ticket'] ?? '';
  125. } else {
  126. $responseData = self::refreshJsapiTicket($corpid);
  127. if(isset($responseData['errcode']) && $responseData['errcode']) {
  128. Log::logError('JsapiTicket获取失败', [
  129. 'response' => $responseData,
  130. 'corpid' => $corpid
  131. ], 'GetJsapiTicket');
  132. return false;
  133. }
  134. # 检出jsapiTicket并缓存到Redis
  135. $jsapiTicket = $responseData['ticket'] ?? false;
  136. $expireIn = $responseData['expires_in'] ?? 0;
  137. RedisModel::setAfterEncode(TokenService::JSAPI_TICKET_RDS . $corpid, $responseData);
  138. RedisModel::expire(TokenService::JSAPI_TICKET_RDS . $corpid, $expireIn);
  139. }
  140. return $jsapiTicket;
  141. }
  142. /**
  143. * 调用接口刷新JsapiTicket
  144. * @param string $corpid 企业ID
  145. * @param string $accessToken 当前缓存的access_token,更新成功后会返回新access_token
  146. * */
  147. public static function refreshJsapiTicket($corpid)
  148. {
  149. # 获取accessToken
  150. $accessToken = AuthorizeCorp::getAccessToken($corpid, '刷新JsapiTicket');
  151. if(empty($accessToken)) {
  152. return false;
  153. }
  154. $requestUri = config('qyWechat.get_jsapi_ticket');
  155. $requestUri .= $accessToken;
  156. $response = HttpService::httpGet($requestUri);
  157. $responseData = json_decode($response, true);
  158. return $responseData;
  159. }
  160. /**
  161. * 获取应用jsapiTicket
  162. * */
  163. public static function getAppJsapiTicket($corpid)
  164. {
  165. $jsapiTicketData = RedisModel::getAfterDecode(TokenService::APP_JSAPI_TICKET_RDS . $corpid);
  166. if(!empty($jsapiTicketData)) {
  167. $jsapiTicket = $jsapiTicketData['ticket'] ?? '';
  168. } else {
  169. $responseData = self::refreshAppJsapiTicket($corpid);
  170. if(isset($responseData['errcode']) && $responseData['errcode']) {
  171. Log::logError('AppJsapiTicket获取失败', [
  172. 'response' => $responseData,
  173. 'corpid' => $corpid
  174. ], 'GetAppJsapiTicket');
  175. return false;
  176. }
  177. # 检出jsapiTicket并缓存到Redis
  178. $jsapiTicket = $responseData['ticket'] ?? false;
  179. $expireIn = $responseData['expires_in'] ?? 0;
  180. RedisModel::setAfterEncode(TokenService::APP_JSAPI_TICKET_RDS . $corpid, $responseData);
  181. RedisModel::expire(TokenService::APP_JSAPI_TICKET_RDS . $corpid, $expireIn);
  182. }
  183. return $jsapiTicket;
  184. }
  185. /**
  186. * 调用接口刷新应用JsapiTicket
  187. * @param string $corpid 企业ID
  188. * @param string $accessToken 当前缓存的access_token,更新成功后会返回新access_token
  189. * */
  190. public static function refreshAppJsapiTicket($corpid)
  191. {
  192. # 获取accessToken
  193. $accessToken = AuthorizeCorp::getAccessToken($corpid, '刷新JsapiTicket');
  194. if(empty($accessToken)) {
  195. return false;
  196. }
  197. $requestUri = config('qyWechat.get_app_jsapi_ticket');
  198. $requestUri .= $accessToken;
  199. $response = HttpService::httpGet($requestUri);
  200. $responseData = json_decode($response, true);
  201. return $responseData;
  202. }
  203. }