Login.php 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2017~2021 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\User as UserModel;
  14. use app\api\service\user\Oauth as OauthService;
  15. use app\api\service\user\Avatar as AvatarService;
  16. use app\api\validate\passport\Login as ValidateLogin;
  17. use app\common\exception\BaseException;
  18. use app\common\service\BaseService;
  19. use edward\captcha\facade\CaptchaApi;
  20. use think\facade\Cache;
  21. /**
  22. * 服务类:用户登录
  23. * Class Login
  24. * @package app\api\service\passport
  25. */
  26. class Login extends BaseService
  27. {
  28. // 用户信息 (登录成功后才记录)
  29. private $userInfo;
  30. // 用于生成token的自定义盐
  31. const TOKEN_SALT = 'user_salt';
  32. /**
  33. * 执行用户登录
  34. * @param array $data
  35. * @return bool
  36. * @throws BaseException
  37. * @throws \think\db\exception\DataNotFoundException
  38. * @throws \think\db\exception\DbException
  39. * @throws \think\db\exception\ModelNotFoundException
  40. */
  41. public function login(array $data)
  42. {
  43. // 数据验证
  44. if (!$this->validate($data)) {
  45. return false;
  46. }
  47. // 自动登录注册
  48. $this->register($data);
  49. // 保存oauth信息
  50. $this->oauth($data);
  51. // 记录登录态
  52. return $this->session();
  53. }
  54. /**
  55. * 快捷登录:微信小程序用户
  56. * @param array $data
  57. * @return bool
  58. * @throws BaseException
  59. * @throws \think\db\exception\DataNotFoundException
  60. * @throws \think\db\exception\DbException
  61. * @throws \think\db\exception\ModelNotFoundException
  62. */
  63. public function mpWxLogin(array $data)
  64. {
  65. // 根据code换取openid
  66. $wxSession = OauthService::wxCode2Session($data['code']);
  67. // 判断openid是否存在
  68. $userId = OauthService::getUserIdByOauthId($wxSession['openid'], 'MP-WEIXIN');
  69. // 获取用户信息
  70. $userInfo = !empty($userId) ? UserModel::detail($userId) : null;
  71. if (empty($userId) || empty($userInfo)) {
  72. $this->error = '第三方用户不存在';
  73. return false;
  74. }
  75. // 更新用户登录信息
  76. $this->updateUser($userInfo, true, $data);
  77. // 记录登录态
  78. return $this->session();
  79. }
  80. /**
  81. * 保存oauth信息
  82. * @param array $data
  83. * @return bool
  84. * @throws BaseException
  85. * @throws \think\db\exception\DataNotFoundException
  86. * @throws \think\db\exception\DbException
  87. * @throws \think\db\exception\ModelNotFoundException
  88. */
  89. private function oauth(array $data)
  90. {
  91. if ($data['isParty']) {
  92. $Oauth = new OauthService;
  93. return $Oauth->party((int)$this->userInfo['user_id'], $data['partyData']);
  94. }
  95. return true;
  96. }
  97. /**
  98. * 当前登录的用户信息
  99. * @return array
  100. */
  101. public function getUserInfo()
  102. {
  103. return $this->userInfo;
  104. }
  105. /**
  106. * 自动登录注册
  107. * @param array $data
  108. * @return bool
  109. */
  110. private function register(array $data)
  111. {
  112. // 查询用户是否已存在
  113. $userInfo = UserModel::detail(['mobile' => $data['mobile']]);
  114. if ($userInfo) {
  115. // 用户存在: 更新登录信息
  116. return $this->updateUser($userInfo, $data['isParty'], $data['partyData']);
  117. } else {
  118. // 用户不存在: 新增用户
  119. return $this->createUser($data['mobile'], $data['isParty'], $data['partyData']);
  120. }
  121. }
  122. /**
  123. * 新增用户
  124. * @param string $mobile 手机号
  125. * @param bool $isParty 是否存在第三方用户信息
  126. * @param array $partyData 用户信息(第三方)
  127. * @return bool
  128. */
  129. private function createUser(string $mobile, bool $isParty, array $partyData = [])
  130. {
  131. // 用户信息
  132. $data = [
  133. 'mobile' => $mobile,
  134. 'nick_name' => hide_mobile($mobile),
  135. 'platform' => getPlatform(),
  136. 'last_login_time' => time(),
  137. 'store_id' => $this->storeId
  138. ];
  139. // 写入用户信息(第三方)
  140. if ($isParty === true && !empty($partyData)) {
  141. $partyUserInfo = $this->partyUserInfo($partyData, true);
  142. $data = array_merge($data, $partyUserInfo);
  143. }
  144. // 新增用户记录
  145. $model = new UserModel;
  146. $status = $model->save($data);
  147. // 记录用户信息
  148. $this->userInfo = $model;
  149. return $status;
  150. }
  151. /**
  152. * 第三方用户信息
  153. * @param array $partyData 第三方用户信息
  154. * @param bool $isGetAvatarUrl 是否下载头像
  155. * @return array
  156. */
  157. private function partyUserInfo(array $partyData, bool $isGetAvatarUrl = true)
  158. {
  159. $partyUserInfo = $partyData['userInfo'];
  160. $data = [
  161. 'nick_name' => $partyUserInfo['nickName'],
  162. 'gender' => $partyUserInfo['gender']
  163. ];
  164. // 下载用户头像
  165. if ($isGetAvatarUrl) {
  166. $data['avatar_id'] = $this->partyAvatar($partyUserInfo['avatarUrl']);
  167. }
  168. return $data;
  169. }
  170. /**
  171. * 下载第三方头像并写入文件库
  172. * @param string $avatarUrl
  173. * @return int
  174. */
  175. private function partyAvatar(string $avatarUrl)
  176. {
  177. $Avatar = new AvatarService;
  178. $fileId = $Avatar->party($avatarUrl);
  179. return $fileId ? $fileId : 0;
  180. }
  181. /**
  182. * 更新用户登录信息
  183. * @param UserModel $userInfo
  184. * @param bool $isParty 是否存在第三方用户信息
  185. * @param array $partyData 用户信息(第三方)
  186. * @return bool
  187. */
  188. private function updateUser(UserModel $userInfo, bool $isParty, array $partyData = [])
  189. {
  190. // 用户信息
  191. $data = [
  192. 'last_login_time' => time(),
  193. 'store_id' => $this->storeId
  194. ];
  195. // 写入用户信息(第三方)
  196. if ($isParty === true && !empty($partyData)) {
  197. $partyUserInfo = $this->partyUserInfo($partyData, !$userInfo['avatar_id']);
  198. $data = array_merge($data, $partyUserInfo);
  199. }
  200. // 更新用户记录
  201. $status = $userInfo->save($data) !== false;
  202. // 记录用户信息
  203. $this->userInfo = $userInfo;
  204. return $status;
  205. }
  206. /**
  207. * 记录登录态
  208. * @return bool
  209. * @throws BaseException
  210. */
  211. private function session()
  212. {
  213. empty($this->userInfo) && throwError('未找到用户信息');
  214. // 登录的token
  215. $token = $this->getToken((int)$this->userInfo['user_id']);
  216. // 记录缓存, 30天
  217. Cache::set($token, [
  218. 'user' => $this->userInfo,
  219. 'store_id' => $this->storeId,
  220. 'is_login' => true,
  221. ], 86400 * 30);
  222. return true;
  223. }
  224. /**
  225. * 数据验证
  226. * @param array $data
  227. * @return bool
  228. */
  229. private function validate(array $data)
  230. {
  231. // 数据验证
  232. $validate = new ValidateLogin;
  233. if (!$validate->check($data)) {
  234. $this->error = $validate->getError();
  235. return false;
  236. }
  237. // 验证短信验证码是否匹配
  238. if (!CaptchaApi::checkSms($data['smsCode'], $data['mobile'])) {
  239. $this->error = '短信验证码不正确';
  240. // return false;
  241. }
  242. return true;
  243. }
  244. /**
  245. * 获取登录的token
  246. * @param int $userId
  247. * @return string
  248. */
  249. public function getToken(int $userId)
  250. {
  251. static $token = '';
  252. if (empty($token)) {
  253. $token = $this->makeToken($userId);
  254. }
  255. return $token;
  256. }
  257. /**
  258. * 生成用户认证的token
  259. * @param int $userId
  260. * @return string
  261. */
  262. public function makeToken(int $userId)
  263. {
  264. $storeId = $this->storeId;
  265. // 生成一个不会重复的随机字符串
  266. $guid = get_guid_v4();
  267. // 当前时间戳 (精确到毫秒)
  268. $timeStamp = microtime(true);
  269. // 自定义一个盐
  270. $salt = self::TOKEN_SALT;
  271. return md5("{$storeId}_{$timeStamp}_{$userId}_{$guid}_{$salt}");
  272. }
  273. }