OpenService.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. <?php
  2. namespace App\Services\OpenApi;
  3. use App\Cache\UserCache;
  4. use App\Consts\ErrorConst;
  5. use App\Consts\SysConst;
  6. use App\Dao\Channel\ChannelDao;
  7. use App\Dao\SendOrder\SendOrderDao;
  8. use App\Dao\User\UserDao;
  9. use App\Libs\TikTok\Application;
  10. use App\Libs\TikTok\MiniProgram\Application as MiniProgramApplication;
  11. use App\Libs\TikTok\OpenPlatform\Application as OpenPlatformApplication;
  12. use App\Libs\TikTok\Kernel\Exceptions;
  13. use App\Libs\Utils;
  14. use GuzzleHttp\Exception\GuzzleException;
  15. use App\Exceptions\ApiException;
  16. class OpenService
  17. {
  18. // private MiniProgramApplication $app;
  19. // private OpenPlatformApplication $openApp;
  20. private $app;
  21. private $openApp;
  22. private $userDao;
  23. private $channelDao;
  24. private $sendOrderDao;
  25. public function __construct(
  26. UserDao $userDao,
  27. ChannelDao $channelDao,
  28. SendOrderDao $sendOrderDao
  29. )
  30. {
  31. $this->userDao = $userDao;
  32. $this->channelDao = $channelDao;
  33. $this->sendOrderDao = $sendOrderDao;
  34. }
  35. /**
  36. * 实例化小程序
  37. *
  38. * @param array $param
  39. * @return $this
  40. */
  41. public function getInstance(array $param = []): OpenService
  42. {
  43. // 获取配置
  44. $config = $this->getConfig($param);
  45. $this->app = Application::miniProgram($config);
  46. return $this;
  47. }
  48. /**
  49. * 实例化小程序
  50. *
  51. * @param array $param
  52. * @return $this
  53. */
  54. public function getOpenInstance(array $param = []): OpenService
  55. {
  56. // 获取配置
  57. $config = $this->getConfig($param);
  58. $this->openApp = Application::openPlatform($config);
  59. return $this;
  60. }
  61. /**
  62. * @param $param
  63. * @return array
  64. */
  65. private function getConfig($param): array
  66. {
  67. $config = [
  68. 'app_id' => env('DOUYIN_APP_ID'),
  69. 'secret' => env('DOUYIN_APP_SECRET'),
  70. 'cache' => [
  71. 'type' => 'Redis',
  72. 'host' => env('REDIS_HOST'),
  73. 'port' => env('REDIS_PORT'),
  74. 'password' => env('REDIS_PASSWORD'),
  75. 'select' => env('REDIS_DB'),
  76. 'prefix' => '',
  77. 'serialize' => []
  78. ],
  79. ];
  80. // 基本参数
  81. $appId = getProp($param, 'app_id');
  82. $secret = getProp($param, 'secret');
  83. if ($appId) $config['app_id'] = $appId;
  84. if ($secret) $config['secret'] = $secret;
  85. // 沙箱模式
  86. if (getProp($param, 'sandbox')) {
  87. $config['http'] = [
  88. 'base_uri' => 'https://open-sandbox.douyin.com/api/',
  89. ];
  90. }
  91. return $config;
  92. }
  93. /**
  94. * @param bool $refresh
  95. * @return array
  96. * @throws Exceptions\HttpException
  97. * @throws Exceptions\InvalidArgumentException
  98. * @throws Exceptions\InvalidConfigException
  99. * @throws Exceptions\RuntimeException
  100. * @throws GuzzleException
  101. * @throws \Psr\SimpleCache\InvalidArgumentException
  102. */
  103. public function getAccessToken(bool $refresh = false): array
  104. {
  105. $result = $this->app->access_token->getToken($refresh);
  106. dLog('openApi')->info('getAccessToken', $result);
  107. return $result;
  108. }
  109. /**
  110. * 登录
  111. *
  112. * @param int $sendOrderId
  113. * @param string $code
  114. * @param string $anonymousCode
  115. * @return array
  116. * @throws ApiException
  117. * @throws GuzzleException
  118. */
  119. public function code2Session(int $sendOrderId, string $code, string $anonymousCode = '')
  120. {
  121. dLog('openApi')->info('code2Session', compact('sendOrderId', 'code', 'anonymousCode'));
  122. try {
  123. $result = $this->app->auth->session($code, $anonymousCode);
  124. } catch (\Exception $e) {
  125. dLog('openApi')->info('code2Session-exception', [$e->getMessage()]);
  126. Utils::throwError(ErrorConst::SYS_EXCEPTION);
  127. }
  128. dLog('openApi')->info('code2Session-result', compact('result'));
  129. // 解析返回值
  130. $resultData = getProp($result, 'data', []);
  131. $unionId = getProp($resultData, 'unionid');
  132. $openId = getProp($resultData, 'openid');
  133. $newSessionKey = getProp($resultData, 'session_key');
  134. // 判断返回信息
  135. if (empty($openId) || empty($unionId)) {
  136. Utils::throwError(ErrorConst::USER_AUTH_FAIL);
  137. }
  138. // 获取派单信息(派单对应的站点id即为用户注册的站点id,无派单,则设置默认站点id)
  139. $sendOrder = $this->sendOrderDao->getSendOrderById($sendOrderId);
  140. $sendOrderChannelId = (int)getProp($sendOrder, 'distribution_channel_id', SysConst::DEFAULT_CHANNEL_ID);
  141. // 获取用户数据
  142. $user = $this->userDao->getUserByOpenIdAndUnionId($openId, $unionId);
  143. $uid = (int)getProp($user, 'id');
  144. $oldSessionKey = getProp($user, 'session_key');
  145. $userSendOrderId = (int)getProp($user, 'send_order_id', $sendOrderId);
  146. $channelId = (int)getProp($user, 'distribution_channel_id', $sendOrderChannelId);
  147. dLog('openApi')->info('code2Session-user', compact('uid', 'oldSessionKey'));
  148. // 计算token
  149. $isNewUser = false;
  150. [$oldToken, $newToken] = [md5($oldSessionKey), md5($newSessionKey)];
  151. if (empty($user)) {
  152. // 新建用户
  153. $user = $this->userDao->createUser([
  154. 'openid' => $openId,
  155. 'unionid' => $unionId,
  156. 'session_key' => $newSessionKey,
  157. 'token' => $newToken,
  158. 'distribution_channel_id' => $channelId,
  159. 'bind_time' => date('Y-m-d H:i:s'),
  160. 'sex' => 2,
  161. 'register_ip' => _getIp()
  162. ]);
  163. // 新uid
  164. $uid = getProp($user, 'id');
  165. if (!$uid) {
  166. Utils::throwError(ErrorConst::USER_INI_FAIL);
  167. }
  168. // 更新用户名
  169. $user->nickname = "用户" . $uid;
  170. $user->invite_code = (int)$uid + 100000;
  171. $user->save();
  172. // 新用户标识
  173. $isNewUser = true;
  174. } else {
  175. // 更新用户数据
  176. $this->userDao->updateUserById($uid, [
  177. 'session_key' => $newSessionKey,
  178. 'token' => $newToken,
  179. 'invite_code' => $uid + 100000,
  180. 'updated_at' => date('Y-m-d H:i:s')
  181. ]);
  182. }
  183. // 更新redis token数据
  184. UserCache::setTokenData($newToken, [
  185. 'uid' => $uid,
  186. 'distribution_channel_id' => $channelId,
  187. 'send_order_id' => $userSendOrderId,
  188. 'from_uid' => (int)getProp($user, 'from_uid'),
  189. ]);
  190. UserCache::delTokenData($oldToken);
  191. // 获取注册用户站点信息
  192. $channel = $this->channelDao->getChannelById($channelId);
  193. // 全局绑定(后续绑定逻辑需要用到这些字段)
  194. $site = app('siteData');
  195. $site->uid = (int)$uid;
  196. $site->token = $newToken;
  197. $site->channel_type = getProp($channel, 'channel_type', 'PERIOD');
  198. return ['uid' => $uid, 'token' => $newToken, 'distribution_channel_id' => $channelId, 'is_new_user' => $isNewUser];
  199. }
  200. /**
  201. * @param string $rawData
  202. * @param string $sessionKey
  203. * @param string $signature
  204. * @return bool
  205. */
  206. public function checkUserDataValid(string $rawData, string $sessionKey, string $signature): bool
  207. {
  208. if ($signature != sha1($rawData . $sessionKey)) {
  209. return false;
  210. }
  211. return true;
  212. }
  213. /**
  214. * 解密用户数据
  215. *
  216. * @param string $encryptedData
  217. * @param string $sessionKey
  218. * @param string $iv
  219. * @return array
  220. */
  221. public function decryptUserData(string $encryptedData, string $sessionKey, string $iv): array
  222. {
  223. if (empty($encryptedData) || empty($sessionKey) || empty($iv)) {
  224. return [];
  225. }
  226. try {
  227. $result = $this->app->encryptor->decryptData($sessionKey, $iv, $encryptedData);
  228. } catch (\Exception $e) {
  229. dLog('openApi')->info('decryptUserData-exception', [$e->getMessage()]);
  230. return [];
  231. }
  232. dLog('openApi')->info('decryptUserData', $result);
  233. return $result;
  234. }
  235. /**
  236. * 获取小程序端内分享链接
  237. *
  238. * @param string $path
  239. * @param string $query
  240. * @return array
  241. * @throws GuzzleException
  242. */
  243. public function generateShareLink(string $path = '', string $query = ''): array
  244. {
  245. try {
  246. $result = $this->app->share->generateLink($path, $query);
  247. } catch (\Exception $e) {
  248. dLog('openApi')->info('generateShareLink-exception', [$e->getMessage()]);
  249. return [];
  250. }
  251. dLog('openApi')->info('generateShareLink', $result);
  252. return $result;
  253. }
  254. /**
  255. * 查询已经生成的 link 的信息
  256. *
  257. * @param string $url_link
  258. * @return array
  259. */
  260. public function getShareQueryInfo(string $url_link = '')
  261. {
  262. try {
  263. $result = $this->app->share->getQueryInfo($url_link);
  264. } catch (\Exception $e) {
  265. dLog('openApi')->info('getShareQueryInfo-exception', [$e->getMessage()]);
  266. return [];
  267. }
  268. dLog('openApi')->info('getShareQueryInfo', $result);
  269. return $result;
  270. }
  271. public function createQRCode(string $path = '')
  272. {
  273. try {
  274. $result = $this->app->share->createQRCode($path);
  275. } catch (\Exception $e) {
  276. dLog('openApi')->info('createQRCode-exception', [$e->getMessage()]);
  277. return [];
  278. }
  279. dLog('openApi')->info('createQRCode', $result);
  280. return $result;
  281. }
  282. /**
  283. * @param string $openId
  284. * @param int $orderStatus
  285. * @param array $orderDetail
  286. * @return array
  287. * @throws GuzzleException
  288. */
  289. public function pushOrder(string $openId, int $orderStatus, array $orderDetail): array
  290. {
  291. try {
  292. $result = $this->app->order->push($openId, $orderStatus, $orderDetail);
  293. } catch (\Exception $e) {
  294. dLog('openApi')->info('pushOrder-exception', [$e->getMessage()]);
  295. return [];
  296. }
  297. dLog('openApi')->info('pushOrder', $result);
  298. return $result;
  299. }
  300. /**
  301. * 查询抖音开放能力列表
  302. *
  303. * @return array
  304. */
  305. public function permissionList(): array
  306. {
  307. try {
  308. $result = $this->app->capacity->queryPermissionList();
  309. } catch (\Exception $e) {
  310. dLog('openApi')->info('permissionList-exception', [$e->getMessage()]);
  311. return [];
  312. }
  313. dLog('openApi')->info('permissionList', $result);
  314. return $result;
  315. }
  316. /**
  317. * @param bool $refresh
  318. * @return array
  319. * @throws Exceptions\HttpException
  320. * @throws Exceptions\InvalidArgumentException
  321. * @throws Exceptions\InvalidConfigException
  322. * @throws Exceptions\RuntimeException
  323. * @throws GuzzleException
  324. * @throws \Psr\SimpleCache\InvalidArgumentException
  325. */
  326. public function getClientToken(bool $refresh = false): array
  327. {
  328. $result = $this->openApp->access_token->getToken($refresh);
  329. dLog('openApi')->info('getClientToken', $result);
  330. return $result;
  331. }
  332. /**
  333. * @param string $serviceOpenId 客服号openId
  334. * @param array $data
  335. * @return array
  336. * @throws GuzzleException
  337. */
  338. public function senImMsg(string $serviceOpenId, array $data): array
  339. {
  340. dLog('openApi')->info('senImMsg-params', compact('serviceOpenId', 'data'));
  341. try {
  342. $result = $this->openApp->im->sendMsg($serviceOpenId, $data);
  343. } catch (\Exception $e) {
  344. dLog('openApi')->info('senImMsg-exception', [$e->getMessage()]);
  345. return [];
  346. }
  347. dLog('openApi')->info('senImMsg', $result);
  348. return $result;
  349. }
  350. /**
  351. * @return array
  352. */
  353. public function eventList()
  354. {
  355. try {
  356. $result = $this->openApp->event->eventList();
  357. } catch (\Exception $e) {
  358. dLog('openApi')->info('eventList-exception', [$e->getMessage()]);
  359. return [];
  360. }
  361. dLog('openApi')->info('eventList', $result);
  362. return $result;
  363. }
  364. }