TencentIm.php 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. <?php
  2. namespace App\Traits;
  3. use App\Exceptions\TencentImAccountException;
  4. use App\Exceptions\TencentImException;
  5. use App\Services\RedisService;
  6. use GuzzleHttp\Client;
  7. use Illuminate\Support\Arr;
  8. use Tencent\TLSSigAPIv2;
  9. trait TencentIm
  10. {
  11. protected $restApiName;
  12. protected $identifier;
  13. protected $TENCENT_IM_USER_SIG_EXPIRE = 86400 * 180; //签名过期时间,单位秒,默认 180 天(同步SDK)
  14. protected $TENCENT_IM_BASE_API_HOST = 'https://console.tim.qq.com/'; //基础请求接口域名
  15. protected $REST_APT_MAX_ITEM = 100;
  16. /**
  17. * 验证是否配置完善
  18. * @return bool
  19. * @throws TencentImException
  20. */
  21. static public function verifyConfig()
  22. {
  23. if (!config('im.sdk_app_id') || !config('im.identifier') || !config('im.secret_key')) {
  24. throw new TencentImException('未配置完善,请检查!');
  25. }
  26. return true;
  27. }
  28. /**
  29. * 获取配置
  30. * @return array
  31. * @throws TencentImException
  32. */
  33. static public function getTencentImConfig()
  34. {
  35. self::verifyConfig();
  36. return [
  37. 'sdk_app_id' => config('im.sdk_app_id'),
  38. 'secret_key' => config('im.secret_key'),
  39. 'identifier' => config('im.identifier')
  40. ];
  41. }
  42. /**
  43. * 获取签名
  44. * @param string $identifier
  45. * @return string
  46. * @throws TencentImException
  47. */
  48. public function getUserSig(string $identifier): string
  49. {
  50. if (RedisService::redis()->exists('tencentIm:userSign:' . $identifier)) {
  51. return RedisService::redis()->get('tencentIm:userSign:' . $identifier);
  52. }
  53. $userSig = self::generateUserSig($identifier);
  54. RedisService::redis()->SETEX('tencentIm:userSign:' . $identifier, $this->TENCENT_IM_USER_SIG_EXPIRE - 10, $userSig);
  55. return $userSig;
  56. }
  57. /**
  58. * 生成签名
  59. * @param string $identifier
  60. * @param float|int $expire
  61. * @return string
  62. * @throws TencentImException
  63. */
  64. public function generateUserSig(string $identifier, $expire = null)
  65. {
  66. $imConfig = self::getTencentImConfig();
  67. $tlsApi = new TLSSigAPIv2(Arr::get($imConfig, 'sdk_app_id'), Arr::get($imConfig, 'secret_key'));
  68. $userSig = $tlsApi->genSig($identifier, $expire ?? $this->TENCENT_IM_USER_SIG_EXPIRE);
  69. return $userSig;
  70. }
  71. /**
  72. * 获取IM接口请求公共参数
  73. * @return string
  74. * @throws TencentImException
  75. */
  76. public function generateTencentImRestApiPublicParams()
  77. {
  78. self::verifyConfig();
  79. $params = [
  80. 'sdkappid' => config('im.sdk_app_id'),
  81. 'identifier' => config('im.identifier'),
  82. 'usersig' => $this->getUserSig(config('im.identifier')),
  83. 'random' => rand(0, 4294967295),
  84. 'contenttype' => 'json'
  85. ];
  86. return http_build_query($params);
  87. }
  88. /**
  89. * 获取腾讯IM RESTAPI 基础 HOST
  90. * @return string
  91. * @throws TencentImException
  92. */
  93. public function getTencentImRestApiBaseHost()
  94. {
  95. if (is_null($this->restApiName)) {
  96. throw new TencentImException('未设置请求接口');
  97. }
  98. return $this->TENCENT_IM_BASE_API_HOST . $this->restApiName . '?' . $this->generateTencentImRestApiPublicParams();
  99. }
  100. /**
  101. * 检测单次api请求是否超过上限
  102. * @param array $accounts
  103. * @return bool
  104. * @throws TencentImAccountException
  105. */
  106. public function verifyRestApiMaxItem(array $accounts)
  107. {
  108. if (count($accounts) > $this->REST_APT_MAX_ITEM) {
  109. throw new TencentImAccountException('单次最多处理100个用户名');
  110. }
  111. return true;
  112. }
  113. /**
  114. * 请求接口
  115. * @param string $requestUrl
  116. * @param array $apiData
  117. * @param string $method
  118. * @param string $paramsType
  119. * @return \Psr\Http\Message\ResponseInterface
  120. * @throws \GuzzleHttp\Exception\GuzzleException
  121. */
  122. public function requestApi(string $requestUrl, array $apiData, string $paramsType = 'json', string $method = 'POST')
  123. {
  124. $client = new Client();
  125. if ($paramsType === 'json') {
  126. $apiData = json_encode($apiData);
  127. }
  128. $response = $client->request($method, $requestUrl, [
  129. 'headers' => ['Accept' => 'multipart/json'],
  130. 'body' => $apiData,
  131. ]);
  132. $result = json_decode($response->getBody(), true);
  133. return $result;
  134. }
  135. /**
  136. * 验证接口返回
  137. * @param $apiResult
  138. * @return bool
  139. * @throws TencentImException
  140. */
  141. static public function verifyApiResult($apiResult)
  142. {
  143. if (!is_array($apiResult)) {
  144. throw new TencentImException('IM 请求失败');
  145. }
  146. if (count(array_diff(['ActionStatus', 'ErrorCode', 'ErrorInfo'], array_keys($apiResult))) > 0) {
  147. throw new TencentImException('IM 接口返回异常');
  148. }
  149. if (($apiResult['ActionStatus'] != 'OK') || ($apiResult['ErrorCode'] != 0)) {
  150. throw new TencentImException('操作失败: ' . $apiResult['ErrorInfo']);
  151. }
  152. return true;
  153. }
  154. }