WechatAuthService.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | Niucloud-admin 企业快速开发的saas管理平台
  4. // +----------------------------------------------------------------------
  5. // | 官方网址:https://www.niucloud.com
  6. // +----------------------------------------------------------------------
  7. // | niucloud团队 版权所有 开源版本可自由商用
  8. // +----------------------------------------------------------------------
  9. // | Author: Niucloud Team
  10. // +----------------------------------------------------------------------
  11. namespace app\service\api\wechat;
  12. use app\dict\member\MemberLoginTypeDict;
  13. use app\dict\member\MemberRegisterTypeDict;
  14. use app\dict\scan\ScanDict;
  15. use app\service\api\login\LoginService;
  16. use app\service\api\login\RegisterService;
  17. use app\service\api\member\MemberConfigService;
  18. use app\service\api\member\MemberService;
  19. use app\service\core\scan\CoreScanService;
  20. use app\service\core\wechat\CoreWechatFansService;
  21. use app\service\core\wechat\CoreWechatServeService;
  22. use core\base\BaseApiService;
  23. use core\exception\ApiException;
  24. use core\exception\AuthException;
  25. use think\db\exception\DataNotFoundException;
  26. use think\db\exception\DbException;
  27. use think\db\exception\ModelNotFoundException;
  28. /**
  29. * 微信配置模型
  30. * Class WechatConfigService
  31. * @package app\service\core\wechat
  32. */
  33. class WechatAuthService extends BaseApiService
  34. {
  35. public $core_wechat_serve_service;
  36. public function __construct()
  37. {
  38. parent::__construct();
  39. $this->core_wechat_serve_service = new CoreWechatServeService();
  40. }
  41. /**
  42. * 网页授权
  43. * @param string $url
  44. * @param string $scopes
  45. * @return array
  46. */
  47. public function authorization(string $url = '', string $scopes = 'snsapi_base')
  48. {
  49. //todo 业务落地
  50. return ['url' => $this->core_wechat_serve_service->authorization(0, $url, $scopes)];
  51. }
  52. /**
  53. * 处理授权回调
  54. * @param string $code
  55. * @return array
  56. */
  57. public function userFromCode(string $code)
  58. {
  59. $userinfo = $this->core_wechat_serve_service->userFromCode(0, $code);
  60. if (empty($userinfo)) throw new ApiException('WECHAT_EMPOWER_NOT_EXIST');
  61. $token_response = $userinfo->getTokenResponse();
  62. if (empty($token_response)) throw new ApiException('WECHAT_EMPOWER_NOT_EXIST');
  63. $scope = $token_response['scope'];
  64. if ($scope == 'snsapi_base') {//静默授权
  65. $openid = $token_response['openid'] ?? '';
  66. } else {
  67. $openid = $userinfo->getId();//对应微信的 openid
  68. $nickname = $userinfo->getNickname();//对应微信的 nickname
  69. $avatar = $userinfo->getAvatar();//对应微信的 头像地址
  70. }
  71. $unionid = $userinfo->getRaw()[ 'unionid' ] ?? '';
  72. if (empty($openid)) throw new ApiException('WECHAT_EMPOWER_NOT_EXIST');
  73. $is_snapshotuser = $userinfo->getRaw()[ 'is_snapshotuser' ] ?? 0;
  74. if ($is_snapshotuser == 1) throw new ApiException('WECHAT_SNAPSHOUTUSER');
  75. //todo 这儿还可能会获取用户昵称 头像 性别 ....用以更新会员信息
  76. return [$avatar ?? '', $nickname ?? '', $openid, $unionid];
  77. //todo 业务落地
  78. }
  79. /**
  80. * 登录通过code
  81. * @param string $code
  82. * @return array|string[]|null
  83. * @throws DataNotFoundException
  84. * @throws DbException
  85. * @throws ModelNotFoundException
  86. */
  87. public function loginByCode(string $code)
  88. {
  89. [ $avatar, $nickname, $openid, $unionid ] = $this->userFromCode($code);
  90. return $this->login($openid, $nickname, $avatar, $unionid);
  91. }
  92. /**
  93. * 公众号登录
  94. * @param string $openid
  95. * @param string $nickname
  96. * @param string $avatar
  97. * @return array|null
  98. * @throws DataNotFoundException
  99. * @throws DbException
  100. * @throws ModelNotFoundException
  101. */
  102. public function login(string $openid, string $nickname = '', string $avatar = '', string $unionid = '')
  103. {
  104. $member_service = new MemberService();
  105. $member_info = $member_service->findMemberInfo([ 'wx_openid' => $openid ]);
  106. if ($member_info->isEmpty() && !empty($unionid)) {
  107. $member_info = $member_service->findMemberInfo([ 'wx_unionid' => $unionid ]);
  108. if (!$member_info->isEmpty()) {
  109. $member_info->wx_openid = $openid;
  110. }
  111. }
  112. if ($member_info->isEmpty()) {
  113. $config = ( new MemberConfigService() )->getLoginConfig();
  114. $is_auth_register = $config[ 'is_auth_register' ];
  115. $is_force_access_user_info = $config[ 'is_force_access_user_info' ];
  116. $is_bind_mobile = $config[ 'is_bind_mobile' ];
  117. // 开启自动注册会员
  118. if ($is_auth_register) {
  119. // 开启强制获取会员信息并且开启强制绑定手机号,必须获取手机号才能进行注册,由于公众号无法主动获取,所以不能注册
  120. if ($is_force_access_user_info && $is_bind_mobile) {
  121. return [ 'avatar' => $avatar, 'nickname' => $nickname, 'openid' => $openid, 'unionid' => $unionid ];
  122. } else if ($is_force_access_user_info) {
  123. // 开启强制获取会员信息时,必须获取到昵称和头像才能进行注册
  124. if (!empty($nickname) && !empty($avatar)) {
  125. return $this->register($openid, '', $nickname, $avatar, $unionid); // 获取到昵称和头像,然后进行注册
  126. } else {
  127. return [ 'avatar' => $avatar, 'nickname' => $nickname, 'openid' => $openid, 'unionid' => $unionid ];
  128. }
  129. } else if ($is_bind_mobile) {
  130. // 开启强制绑定手机号,必须获取手机号才能进行注册,由于公众号无法主动获取,所以不能注册
  131. return [ 'openid' => $openid, 'unionid' => $unionid ];
  132. } else if (!$is_force_access_user_info && !$is_bind_mobile) {
  133. // 关闭强制获取用户信息、并且关闭强制绑定手机号的情况下允许注册
  134. return $this->register($openid, '', $nickname, $avatar, $unionid);
  135. }
  136. } else {
  137. return [ 'openid' => $openid, 'unionid' => $unionid ]; // 将重要信息返回给前端保存
  138. }
  139. } else {
  140. // 可能会更新用户和粉丝表
  141. $login_service = new LoginService();
  142. // 若用户头像为空,那么从微信获取头像和昵称,然后进行更新
  143. if (empty($member_info->headimg)) {
  144. if (!empty($avatar)) $member_info->headimg = $avatar;
  145. if (!empty($nickname)) $member_info->nickname = $nickname;
  146. }
  147. return $login_service->login($member_info, MemberLoginTypeDict::WECHAT);
  148. }
  149. }
  150. /**
  151. * 同步数据
  152. * @param string $code
  153. * @return true
  154. */
  155. public function sync(string $code)
  156. {
  157. [$avatar, $nickname, $openid] = $this->userFromCode($code);
  158. //更新粉丝
  159. $core_wechat_fans_service = new CoreWechatFansService();
  160. //这儿或许可以异步
  161. $core_wechat_fans_service->edit(0, $openid, ['avatar' => $avatar, 'nickname' => $nickname]);
  162. $member_service = new MemberService();
  163. $member_info = $member_service->findMemberInfo(['wx_openid' => $openid]);
  164. if ($member_info->isEmpty()) throw new AuthException('MEMBER_NOT_EXIST');//账号不存在
  165. $member_service->editByFind($member_info, ['headimg' => $avatar, 'nickname' => $nickname]);
  166. return true;
  167. }
  168. /**
  169. * 注册
  170. * @param string $openid
  171. * @param string $mobile
  172. * @param string $nickname
  173. * @param string $avatar
  174. * @param string $wx_unionid
  175. * @return array
  176. * @throws DataNotFoundException
  177. * @throws DbException
  178. * @throws ModelNotFoundException
  179. */
  180. public function register(string $openid, string $mobile = '', string $nickname = '', string $avatar = '', string $wx_unionid = '')
  181. {
  182. $member_service = new MemberService();
  183. $member_info = $member_service->findMemberInfo(['wx_openid' => $openid]);
  184. if (!$member_info->isEmpty()) throw new AuthException('MEMBER_IS_EXIST');//账号已存在, 不能在注册
  185. if (!empty($wx_unionid)) {
  186. $member_info = $member_service->findMemberInfo([ 'wx_unionid' => $wx_unionid ]);
  187. if (!$member_info->isEmpty()) throw new AuthException('MEMBER_IS_EXIST');//账号已存在, 不能在注册
  188. }
  189. $register_service = new RegisterService();
  190. return $register_service->register($mobile,
  191. [
  192. 'wx_openid' => $openid,
  193. 'nickname' => $nickname,
  194. 'headimg' => $avatar,
  195. 'wx_unionid' => $wx_unionid
  196. ],
  197. MemberRegisterTypeDict::WECHAT
  198. );
  199. }
  200. /**
  201. * 获取jssdkconfig
  202. * @param string $url
  203. * @return array|string
  204. */
  205. public function jssdkConfig(string $url = '')
  206. {
  207. return $this->core_wechat_serve_service->jssdkConfig(0, $url);
  208. }
  209. /**
  210. * 扫码登录
  211. * @return array
  212. */
  213. public function scanLogin()
  214. {
  215. $data = [
  216. 'channel' => $this->channel,
  217. ];
  218. $key = (new CoreScanService())->scan(0, ScanDict::WECHAT_LOGIN, $data, 300);
  219. $url = $this->core_wechat_serve_service->scan($key, 300)['url'] ?? '';
  220. return [
  221. 'url' => $url,
  222. 'key' => $key
  223. ];
  224. }
  225. /**
  226. * 更新openid(用于账号密码或手机号注册时未正常获取到openid时再次获取)
  227. * @param string $code
  228. * @return true
  229. */
  230. public function updateOpenid(string $code)
  231. {
  232. [ $avatar, $nickname, $openid, $unionid ] = $this->userFromCode($code);
  233. $member_service = new MemberService();
  234. $member = $member_service->findMemberInfo([ 'wx_openid' => $openid ]);
  235. if (!$member->isEmpty()) throw new AuthException('MEMBER_OPENID_EXIST');//openid已存在
  236. $member_info = $member_service->findMemberInfo([ 'member_id' => $this->member_id ]);
  237. if ($member_info->isEmpty()) throw new AuthException('MEMBER_NOT_EXIST');//账号不存在
  238. $member_service->editByFind($member_info, [ 'wx_openid' => $openid ]);
  239. return true;
  240. }
  241. }