Login.php 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  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. * 保存oauth信息
  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. private function oauth(array $data)
  64. {
  65. if ($data['isParty']) {
  66. $Oauth = new OauthService;
  67. return $Oauth->party((int)$this->userInfo['user_id'], $data['partyData']);
  68. }
  69. return true;
  70. }
  71. /**
  72. * 当前登录的用户信息
  73. * @return array
  74. */
  75. public function getUserInfo()
  76. {
  77. return $this->userInfo;
  78. }
  79. /**
  80. * 自动登录注册
  81. * @param array $data
  82. * @return bool
  83. */
  84. private function register(array $data)
  85. {
  86. // 查询用户是否已存在
  87. $userInfo = UserModel::detail(['mobile' => $data['mobile']]);
  88. if ($userInfo) {
  89. // 用户存在: 更新登录信息
  90. return $this->updateUser($userInfo, $data['isParty'], $data['partyData']);
  91. } else {
  92. // 用户不存在: 新增用户
  93. return $this->createUser($data['mobile'], $data['isParty'], $data['partyData']);
  94. }
  95. }
  96. /**
  97. * 新增用户
  98. * @param string $mobile 手机号
  99. * @param bool $isParty 是否存在第三方用户信息
  100. * @param array $partyData 用户信息(第三方)
  101. * @return bool
  102. */
  103. private function createUser(string $mobile, bool $isParty, array $partyData = [])
  104. {
  105. // 用户信息
  106. $data = [
  107. 'mobile' => $mobile,
  108. 'nick_name' => hide_mobile($mobile),
  109. 'platform' => getPlatform(),
  110. 'last_login_time' => time(),
  111. 'store_id' => $this->storeId
  112. ];
  113. // 写入用户信息(第三方)
  114. if ($isParty === true && !empty($partyData)) {
  115. $partyUserInfo = $this->partyUserInfo($partyData, true);
  116. $data = array_merge($data, $partyUserInfo);
  117. }
  118. // 新增用户记录
  119. $model = new UserModel;
  120. $status = $model->save($data);
  121. // 记录用户信息
  122. $this->userInfo = $model;
  123. return $status;
  124. }
  125. /**
  126. * 第三方用户信息
  127. * @param array $partyData 第三方用户信息
  128. * @param bool $isGetAvatarUrl 是否下载头像
  129. * @return array
  130. */
  131. private function partyUserInfo(array $partyData, bool $isGetAvatarUrl = true)
  132. {
  133. $partyUserInfo = $partyData['userInfo'];
  134. $data = [
  135. 'nick_name' => $partyUserInfo['nickName'],
  136. 'gender' => $partyUserInfo['gender']
  137. ];
  138. // 下载用户头像
  139. if ($isGetAvatarUrl) {
  140. $data['avatar_id'] = $this->partyAvatar($partyUserInfo['avatarUrl']);
  141. }
  142. return $data;
  143. }
  144. /**
  145. * 下载第三方头像并写入文件库
  146. * @param string $avatarUrl
  147. * @return int
  148. */
  149. private function partyAvatar(string $avatarUrl)
  150. {
  151. $Avatar = new AvatarService;
  152. $fileId = $Avatar->party($avatarUrl);
  153. return $fileId ? $fileId : 0;
  154. }
  155. /**
  156. * 更新用户登录信息
  157. * @param UserModel $userInfo
  158. * @param bool $isParty 是否存在第三方用户信息
  159. * @param array $partyData 用户信息(第三方)
  160. * @return bool
  161. */
  162. private function updateUser(UserModel $userInfo, bool $isParty, array $partyData = [])
  163. {
  164. // 用户信息
  165. $data = [
  166. 'last_login_time' => time(),
  167. 'store_id' => $this->storeId
  168. ];
  169. // 写入用户信息(第三方)
  170. if ($isParty === true && !empty($partyData)) {
  171. $partyUserInfo = $this->partyUserInfo($partyData, !$userInfo['avatar_id']);
  172. $data = array_merge($data, $partyUserInfo);
  173. }
  174. // 更新用户记录
  175. $status = $userInfo->save($data) !== false;
  176. // 记录用户信息
  177. $this->userInfo = $userInfo;
  178. return $status;
  179. }
  180. /**
  181. * 记录登录态
  182. * @return bool
  183. * @throws BaseException
  184. */
  185. private function session()
  186. {
  187. empty($this->userInfo) && throwError('未找到用户信息');
  188. // 登录的token
  189. $token = $this->getToken((int)$this->userInfo['user_id']);
  190. // 记录缓存, 30天
  191. Cache::set($token, [
  192. 'user' => $this->userInfo,
  193. 'store_id' => $this->storeId,
  194. 'is_login' => true,
  195. ], 86400 * 30);
  196. return true;
  197. }
  198. /**
  199. * 数据验证
  200. * @param array $data
  201. * @return bool
  202. */
  203. private function validate(array $data)
  204. {
  205. // 数据验证
  206. $validate = new ValidateLogin;
  207. if (!$validate->check($data)) {
  208. $this->error = $validate->getError();
  209. return false;
  210. }
  211. // 验证短信验证码是否匹配
  212. if (!CaptchaApi::checkSms($data['smsCode'], $data['mobile'])) {
  213. $this->error = '短信验证码不正确';
  214. return false;
  215. }
  216. return true;
  217. }
  218. /**
  219. * 获取登录的token
  220. * @param int $userId
  221. * @return string
  222. */
  223. public function getToken(int $userId)
  224. {
  225. static $token = '';
  226. if (empty($token)) {
  227. $token = $this->makeToken($userId);
  228. }
  229. return $token;
  230. }
  231. /**
  232. * 生成用户认证的token
  233. * @param int $userId
  234. * @return string
  235. */
  236. public function makeToken(int $userId)
  237. {
  238. $storeId = $this->storeId;
  239. // 生成一个不会重复的随机字符串
  240. $guid = get_guid_v4();
  241. // 当前时间戳 (精确到毫秒)
  242. $timeStamp = microtime(true);
  243. // 自定义一个盐
  244. $salt = self::TOKEN_SALT;
  245. return md5("{$storeId}_{$timeStamp}_{$userId}_{$guid}_{$salt}");
  246. }
  247. }