Login.php 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  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. if (!empty($partyUserInfo['nickName'])){
  191. $data['nick_name'] = $partyUserInfo['nickName'];
  192. }
  193. $data['gender'] = $partyUserInfo['gender']??0;
  194. // $data = [
  195. // 'nick_name' => $partyUserInfo['nickName'] ?? "用户",
  196. // 'gender' => $partyUserInfo['gender']??0
  197. // ];
  198. // 下载用户头像
  199. if ($isGetAvatarUrl && !empty($partyUserInfo['avatarUrl'])) {
  200. $data['avatar_id'] = $this->partyAvatar($partyUserInfo['avatarUrl']);
  201. }
  202. return $data;
  203. }
  204. /**
  205. * 下载第三方头像并写入文件库
  206. * @param string $avatarUrl
  207. * @return int
  208. */
  209. private function partyAvatar(string $avatarUrl)
  210. {
  211. $Avatar = new AvatarService;
  212. $fileId = $Avatar->party($avatarUrl);
  213. return $fileId ? $fileId : 0;
  214. }
  215. /**
  216. * 更新用户登录信息
  217. * @param UserModel $userInfo
  218. * @return bool
  219. */
  220. private function updateUser(UserModel $userInfo)
  221. {
  222. // 用户信息
  223. $data = [
  224. 'last_login_time' => time(),
  225. 'store_id' => $this->storeId
  226. ];
  227. // 写入用户信息(第三方)
  228. // if ($isParty === true && !empty($partyData)) {
  229. // $partyUserInfo = $this->partyUserInfo($partyData, !$userInfo['avatar_id']);
  230. // $data = array_merge($data, $partyUserInfo);
  231. // }
  232. // 更新用户记录
  233. $status = $userInfo->save($data) !== false;
  234. // 记录用户信息
  235. $this->userInfo = $userInfo;
  236. return $status;
  237. }
  238. /**
  239. * 记录登录态
  240. * @return bool
  241. * @throws BaseException
  242. */
  243. private function session()
  244. {
  245. empty($this->userInfo) && throwError('未找到用户信息');
  246. // 登录的token
  247. $token = $this->getToken((int)$this->userInfo['user_id']);
  248. // 记录缓存, 30天
  249. Cache::set($token, [
  250. 'user' => $this->userInfo,
  251. 'store_id' => $this->storeId,
  252. 'is_login' => true,
  253. ], 86400 * 30);
  254. return true;
  255. }
  256. /**
  257. * 数据验证
  258. * @param array $data
  259. * @return bool
  260. */
  261. private function validate(array $data)
  262. {
  263. // 数据验证
  264. $validate = new ValidateLogin;
  265. if (!$validate->check($data)) {
  266. $this->error = $validate->getError();
  267. return false;
  268. }
  269. // 验证短信验证码是否匹配
  270. // if (!CaptchaApi::checkSms($data['smsCode'], $data['mobile'])) {
  271. // $this->error = '短信验证码不正确';
  272. // return false;
  273. // }
  274. return true;
  275. }
  276. /**
  277. * 获取登录的token
  278. * @param int $userId
  279. * @return string
  280. */
  281. public function getToken(int $userId)
  282. {
  283. static $token = '';
  284. if (empty($token)) {
  285. $token = $this->makeToken($userId);
  286. }
  287. return $token;
  288. }
  289. /**
  290. * 生成用户认证的token
  291. * @param int $userId
  292. * @return string
  293. */
  294. public function makeToken(int $userId)
  295. {
  296. $storeId = $this->storeId;
  297. // 生成一个不会重复的随机字符串
  298. $guid = get_guid_v4();
  299. // 当前时间戳 (精确到毫秒)
  300. $timeStamp = microtime(true);
  301. // 自定义一个盐
  302. $salt = self::TOKEN_SALT;
  303. return md5("{$storeId}_{$timeStamp}_{$userId}_{$guid}_{$salt}");
  304. }
  305. }