Party.php 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
  8. // +----------------------------------------------------------------------
  9. // | Author: 萤火科技 <admin@yiovo.com>
  10. // +----------------------------------------------------------------------
  11. declare (strict_types=1);
  12. namespace app\api\service\passport;
  13. use app\api\model\UserOauth as UserOauthModel;
  14. use app\api\service\user\Oauth as OauthService;
  15. use app\api\service\user\Avatar as AvatarService;
  16. use app\common\enum\Client as ClientEnum;
  17. use app\common\service\BaseService;
  18. use cores\exception\BaseException;
  19. /**
  20. * 第三方用户注册登录服务
  21. * Class Party
  22. * @package app\api\service\passport
  23. */
  24. class Party extends BaseService
  25. {
  26. /**
  27. * 保存用户的第三方认证信息
  28. * @param int $userId 用户ID
  29. * @param array $partyData 第三方登录信息
  30. * @return bool
  31. * @throws BaseException
  32. * @throws \think\db\exception\DataNotFoundException
  33. * @throws \think\db\exception\DbException
  34. * @throws \think\db\exception\ModelNotFoundException
  35. */
  36. public function createUserOauth(int $userId, array $partyData = []): bool
  37. {
  38. try {
  39. // 获取oauthId和unionId
  40. $oauthInfo = $this->getOauthInfo($partyData);
  41. } catch (BaseException $e) {
  42. // isBack参数代表需重新获取code, 前端拿到该参数进行页面返回
  43. throwError($e->getMessage(), null, ['isBack' => true]);
  44. }
  45. // 是否存在第三方用户
  46. $oauthId = UserOauthModel::getOauthIdByUserId($userId, $partyData['oauth']);
  47. // 如果不存在oauth则写入
  48. if (empty($oauthId)) {
  49. return (new UserOauthModel)->add([
  50. 'user_id' => $userId,
  51. 'oauth_type' => $partyData['oauth'],
  52. 'oauth_id' => $oauthInfo['oauth_id'],
  53. 'unionid' => $oauthInfo['unionid'] ?? '', // unionid可以不存在
  54. 'store_id' => $this->storeId
  55. ]);
  56. }
  57. // 如果存在第三方用户, 需判断oauthId是否相同
  58. if ($oauthId != $oauthInfo['oauth_id']) {
  59. // isBack参数代表需重新获取code, 前端拿到该参数进行页面返回
  60. throwError('很抱歉,当前手机号已绑定其他微信号', null, ['isBack' => true]);
  61. }
  62. return true;
  63. }
  64. /**
  65. * 获取微信小程序登录态(session)
  66. * 这里支持静态变量缓存, 用于实现第二次调用该方法时直接返回已获得的session
  67. * @param string $code
  68. * @return array|false
  69. * @throws BaseException
  70. * @throws \think\db\exception\DataNotFoundException
  71. * @throws \think\db\exception\DbException
  72. * @throws \think\db\exception\ModelNotFoundException
  73. */
  74. public static function getMpWxSession(string $code)
  75. {
  76. static $session;
  77. if (empty($session)) {
  78. try {
  79. // 微信小程序通过code获取session
  80. $session = OauthService::wxCode2Session($code);
  81. } catch (BaseException $e) {
  82. // showError参数表示让前端显示错误
  83. throwError($e->getMessage());
  84. }
  85. }
  86. return $session;
  87. }
  88. /**
  89. * 第三方用户信息
  90. * @param array $partyData 第三方用户信息
  91. * @param bool $defaultNickName 是否需要生成默认用户昵称 (仅首次注册时)
  92. * @param bool $isGetAvatarUrl 是否下载头像
  93. * @return array
  94. * @throws BaseException
  95. * @throws \think\Exception
  96. */
  97. public static function partyUserInfo(array $partyData, bool $defaultNickName = false, bool $isGetAvatarUrl = true): array
  98. {
  99. $partyUserInfo = $partyData['userInfo'] ?? [];
  100. $data = [];
  101. if (!empty($partyUserInfo['nickName'])) {
  102. $data['nick_name'] = $partyUserInfo['nickName'];
  103. }
  104. // 生成默认的用户昵称
  105. if ($defaultNickName && empty($data['nick_name'])) {
  106. $data['nick_name'] = self::getDefaultNickName($partyData);
  107. }
  108. if ($isGetAvatarUrl) {
  109. // 记录avatarId
  110. if (!empty($partyUserInfo['avatarId']) && $partyUserInfo['avatarId'] > 0) {
  111. $data['avatar_id'] = (int)$partyUserInfo['avatarId'];
  112. }
  113. // 通过外链下载头像
  114. if (empty($data['avatar_id']) && !empty($partyUserInfo['avatarUrl'])) {
  115. $data['avatar_id'] = static::partyAvatar($partyUserInfo['avatarUrl']);
  116. }
  117. }
  118. return $data;
  119. }
  120. /**
  121. * 下载第三方头像并写入文件库
  122. * @param string $avatarUrl
  123. * @return int
  124. * @throws BaseException
  125. * @throws \think\Exception
  126. */
  127. private static function partyAvatar(string $avatarUrl): int
  128. {
  129. $Avatar = new AvatarService;
  130. $fileId = $Avatar->party($avatarUrl);
  131. return $fileId ? $fileId : 0;
  132. }
  133. /**
  134. * 获取第三方用户session信息 (openid、unionid、session_key等)
  135. * @param array $partyData
  136. * @return array|null
  137. * @throws BaseException
  138. * @throws \think\db\exception\DataNotFoundException
  139. * @throws \think\db\exception\DbException
  140. * @throws \think\db\exception\ModelNotFoundException
  141. */
  142. private function getOauthInfo(array $partyData): ?array
  143. {
  144. if ($partyData['oauth'] === 'MP-WEIXIN') {
  145. $wxSession = static::getMpWxSession($partyData['code']);
  146. return ['oauth_id' => $wxSession['openid'], 'unionid' => $wxSession['unionid'] ?? null];
  147. }
  148. return null;
  149. }
  150. /**
  151. * 根据第三方来源生成默认用户昵称
  152. * @param array $partyData
  153. * @return string
  154. */
  155. private static function getDefaultNickName(array $partyData): string
  156. {
  157. $default = [
  158. ClientEnum::MP_WEIXIN => '微信',
  159. ClientEnum::MP_ALIPAY => '支付宝',
  160. ClientEnum::WXOFFICIAL => '公众号',
  161. ClientEnum::H5 => 'H5',
  162. ClientEnum::APP => 'APP',
  163. ];
  164. return isset($default[$partyData['oauth']]) ? "{$default[$partyData['oauth']]}用户" : '商城用户';
  165. }
  166. }