Login.php 9.7 KB

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