userDao = $userDao; $this->channelDao = $channelDao; $this->sendOrderDao = $sendOrderDao; } /** * 实例化小程序 * * @param array $param * @return $this */ public function getInstance(array $param = []): OpenService { // 获取配置 $config = $this->getConfig($param); $this->app = Application::miniProgram($config); return $this; } /** * 实例化小程序 * * @param array $param * @return $this */ public function getOpenInstance(array $param = []): OpenService { // 获取配置 $config = $this->getConfig($param); $this->openApp = Application::openPlatform($config); return $this; } /** * @param $param * @return array */ private function getConfig($param): array { $config = [ 'app_id' => env('DOUYIN_APP_ID'), 'secret' => env('DOUYIN_APP_SECRET'), 'cache' => [ 'type' => 'Redis', 'host' => env('REDIS_HOST'), 'port' => env('REDIS_PORT'), 'password' => env('REDIS_PASSWORD'), 'select' => env('REDIS_DB'), 'prefix' => '', 'serialize' => [] ], ]; // 基本参数 $appId = getProp($param, 'app_id'); $secret = getProp($param, 'secret'); if ($appId) $config['app_id'] = $appId; if ($secret) $config['secret'] = $secret; // 沙箱模式 if (getProp($param, 'sandbox')) { $config['http'] = [ 'base_uri' => 'https://open-sandbox.douyin.com/api/', ]; } return $config; } /** * @param bool $refresh * @return array * @throws Exceptions\HttpException * @throws Exceptions\InvalidArgumentException * @throws Exceptions\InvalidConfigException * @throws Exceptions\RuntimeException * @throws GuzzleException * @throws \Psr\SimpleCache\InvalidArgumentException */ public function getAccessToken(bool $refresh = false): array { $result = $this->app->access_token->getToken($refresh); dLog('openApi')->info('getAccessToken', $result); return $result; } /** * 登录 * * @param int $sendOrderId * @param string $code * @param string $anonymousCode * @return array * @throws ApiException * @throws GuzzleException */ public function code2Session(int $sendOrderId, string $code, string $anonymousCode = '') { dLog('openApi')->info('code2Session', compact('sendOrderId', 'code', 'anonymousCode')); try { $result = $this->app->auth->session($code, $anonymousCode); } catch (\Exception $e) { dLog('openApi')->info('code2Session-exception', [$e->getMessage()]); Utils::throwError(ErrorConst::SYS_EXCEPTION); } dLog('openApi')->info('code2Session-result', compact('result')); // 解析返回值 $resultData = getProp($result, 'data', []); $unionId = getProp($resultData, 'unionid'); $openId = getProp($resultData, 'openid'); $newSessionKey = getProp($resultData, 'session_key'); // 判断返回信息 if (empty($openId) || empty($unionId)) { Utils::throwError(ErrorConst::USER_AUTH_FAIL); } // 获取派单信息(派单对应的站点id即为用户注册的站点id,无派单,则设置默认站点id) $sendOrder = $this->sendOrderDao->getSendOrderById($sendOrderId); $sendOrderChannelId = (int)getProp($sendOrder, 'distribution_channel_id', SysConst::DEFAULT_CHANNEL_ID); // 获取用户数据 $user = $this->userDao->getUserByOpenIdAndUnionId($openId, $unionId); $uid = (int)getProp($user, 'id'); $oldSessionKey = getProp($user, 'session_key'); $userSendOrderId = (int)getProp($user, 'send_order_id', $sendOrderId); $channelId = (int)getProp($user, 'distribution_channel_id', $sendOrderChannelId); dLog('openApi')->info('code2Session-user', compact('uid', 'oldSessionKey')); // 计算token $isNewUser = false; [$oldToken, $newToken] = [md5($oldSessionKey), md5($newSessionKey)]; if (empty($user)) { // 新建用户 $user = $this->userDao->createUser([ 'openid' => $openId, 'unionid' => $unionId, 'session_key' => $newSessionKey, 'token' => $newToken, 'distribution_channel_id' => $channelId, 'bind_time' => date('Y-m-d H:i:s'), 'sex' => 2, 'register_ip' => _getIp() ]); // 新uid $uid = getProp($user, 'id'); if (!$uid) { Utils::throwError(ErrorConst::USER_INI_FAIL); } // 更新用户名 $user->nickname = "用户" . $uid; $user->invite_code = (int)$uid + 100000; $user->save(); // 新用户标识 $isNewUser = true; } else { // 更新用户数据 $this->userDao->updateUserById($uid, [ 'session_key' => $newSessionKey, 'token' => $newToken, 'invite_code' => $uid + 100000, 'updated_at' => date('Y-m-d H:i:s') ]); } // 更新redis token数据 UserCache::setTokenData($newToken, [ 'uid' => $uid, 'distribution_channel_id' => $channelId, 'send_order_id' => $userSendOrderId, 'from_uid' => (int)getProp($user, 'from_uid'), ]); UserCache::delTokenData($oldToken); // 获取注册用户站点信息 $channel = $this->channelDao->getChannelById($channelId); // 全局绑定(后续绑定逻辑需要用到这些字段) $site = app('siteData'); $site->uid = (int)$uid; $site->token = $newToken; $site->channel_type = getProp($channel, 'channel_type', 'PERIOD'); return ['uid' => $uid, 'token' => $newToken, 'distribution_channel_id' => $channelId, 'is_new_user' => $isNewUser]; } /** * @param string $rawData * @param string $sessionKey * @param string $signature * @return bool */ public function checkUserDataValid(string $rawData, string $sessionKey, string $signature): bool { if ($signature != sha1($rawData . $sessionKey)) { return false; } return true; } /** * 解密用户数据 * * @param string $encryptedData * @param string $sessionKey * @param string $iv * @return array */ public function decryptUserData(string $encryptedData, string $sessionKey, string $iv): array { if (empty($encryptedData) || empty($sessionKey) || empty($iv)) { return []; } try { $result = $this->app->encryptor->decryptData($sessionKey, $iv, $encryptedData); } catch (\Exception $e) { dLog('openApi')->info('decryptUserData-exception', [$e->getMessage()]); return []; } dLog('openApi')->info('decryptUserData', $result); return $result; } /** * 获取小程序端内分享链接 * * @param string $path * @param string $query * @return array * @throws GuzzleException */ public function generateShareLink(string $path = '', string $query = ''): array { try { $result = $this->app->share->generateLink($path, $query); } catch (\Exception $e) { dLog('openApi')->info('generateShareLink-exception', [$e->getMessage()]); return []; } dLog('openApi')->info('generateShareLink', $result); return $result; } /** * 查询已经生成的 link 的信息 * * @param string $url_link * @return array */ public function getShareQueryInfo(string $url_link = '') { try { $result = $this->app->share->getQueryInfo($url_link); } catch (\Exception $e) { dLog('openApi')->info('getShareQueryInfo-exception', [$e->getMessage()]); return []; } dLog('openApi')->info('getShareQueryInfo', $result); return $result; } public function createQRCode(string $path = '') { try { $result = $this->app->share->createQRCode($path); } catch (\Exception $e) { dLog('openApi')->info('createQRCode-exception', [$e->getMessage()]); return []; } dLog('openApi')->info('createQRCode', $result); return $result; } /** * @param string $openId * @param int $orderStatus * @param array $orderDetail * @return array * @throws GuzzleException */ public function pushOrder(string $openId, int $orderStatus, array $orderDetail): array { try { $result = $this->app->order->push($openId, $orderStatus, $orderDetail); } catch (\Exception $e) { dLog('openApi')->info('pushOrder-exception', [$e->getMessage()]); return []; } dLog('openApi')->info('pushOrder', $result); return $result; } /** * 查询抖音开放能力列表 * * @return array */ public function permissionList(): array { try { $result = $this->app->capacity->queryPermissionList(); } catch (\Exception $e) { dLog('openApi')->info('permissionList-exception', [$e->getMessage()]); return []; } dLog('openApi')->info('permissionList', $result); return $result; } /** * @param bool $refresh * @return array * @throws Exceptions\HttpException * @throws Exceptions\InvalidArgumentException * @throws Exceptions\InvalidConfigException * @throws Exceptions\RuntimeException * @throws GuzzleException * @throws \Psr\SimpleCache\InvalidArgumentException */ public function getClientToken(bool $refresh = false): array { $result = $this->openApp->access_token->getToken($refresh); dLog('openApi')->info('getClientToken', $result); return $result; } /** * @param string $serviceOpenId 客服号openId * @param array $data * @return array * @throws GuzzleException */ public function senImMsg(string $serviceOpenId, array $data): array { dLog('openApi')->info('senImMsg-params', compact('serviceOpenId', 'data')); try { $result = $this->openApp->im->sendMsg($serviceOpenId, $data); } catch (\Exception $e) { dLog('openApi')->info('senImMsg-exception', [$e->getMessage()]); return []; } dLog('openApi')->info('senImMsg', $result); return $result; } /** * @return array */ public function eventList() { try { $result = $this->openApp->event->eventList(); } catch (\Exception $e) { dLog('openApi')->info('eventList-exception', [$e->getMessage()]); return []; } dLog('openApi')->info('eventList', $result); return $result; } }