Login.php 7.7 KB

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