LoginLogic.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | likeadmin快速开发前后端分离管理后台(PHP版)
  4. // +----------------------------------------------------------------------
  5. // | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
  6. // | 开源版本可自由商用,可去除界面版权logo
  7. // | gitee下载:https://gitee.com/likeshop_gitee/likeadmin
  8. // | github下载:https://github.com/likeshop-github/likeadmin
  9. // | 访问官网:https://www.likeadmin.cn
  10. // | likeadmin团队 版权所有 拥有最终解释权
  11. // +----------------------------------------------------------------------
  12. // | author: likeadminTeam
  13. // +----------------------------------------------------------------------
  14. namespace app\api\logic;
  15. use app\common\cache\WebScanLoginCache;
  16. use app\common\logic\BaseLogic;
  17. use app\api\service\{UserTokenService, WechatUserService};
  18. use app\common\enum\{LoginEnum, user\UserTerminalEnum, YesNoEnum};
  19. use app\common\service\{
  20. ConfigService,
  21. FileService,
  22. wechat\WeChatConfigService,
  23. wechat\WeChatMnpService,
  24. wechat\WeChatOaService,
  25. wechat\WeChatRequestService
  26. };
  27. use app\common\model\user\{User, UserAuth};
  28. use think\facade\{Db, Config};
  29. /**
  30. * 登录逻辑
  31. * Class LoginLogic
  32. * @package app\api\logic
  33. */
  34. class LoginLogic extends BaseLogic
  35. {
  36. /**
  37. * @param array $params
  38. * @return User|\think\Model
  39. * @throws \think\db\exception\DataNotFoundException
  40. * @throws \think\db\exception\DbException
  41. * @throws \think\db\exception\ModelNotFoundException
  42. */
  43. public static function register(array $params)
  44. {
  45. $userSn = User::createUserSn();
  46. $params['password'] = !empty($params['password'])?$params['password']:rand(100000,999999);
  47. $passwordSalt = Config::get('project.unique_identification');
  48. $password = create_password($params['password'], $passwordSalt);
  49. $avatar = ConfigService::get('default_image', 'user_avatar');
  50. $user = User::create([
  51. 'sn' => $userSn,
  52. 'avatar' => $avatar,
  53. 'nickname' => '用户' . $userSn,
  54. 'account' => $params['account'],
  55. 'password' => $password,
  56. 'channel' => $params['channel'],
  57. ]);
  58. return $user;
  59. }
  60. /**
  61. * @notes 账号/手机号登录,手机号验证码
  62. * @param $params
  63. * @return array|false
  64. * @author 段誉
  65. * @date 2022/9/6 19:26
  66. */
  67. public static function login($params)
  68. {
  69. try {
  70. // 账号/手机号 密码登录
  71. $where = ['account|mobile' => $params['account']];
  72. if ($params['scene'] == LoginEnum::MOBILE_CAPTCHA) {
  73. //手机验证码登录
  74. $where = ['mobile' => $params['account']];
  75. }
  76. $user = User::where($where)->findOrEmpty();
  77. if ($user->isEmpty()) {
  78. //直接注册用户
  79. $params['channel'] = $params['terminal'];
  80. $user = self::register($params);
  81. }
  82. //更新登录信息
  83. $user->login_time = time();
  84. $user->login_ip = request()->ip();
  85. $user->save();
  86. //设置token
  87. $userInfo = UserTokenService::setToken($user->id, $params['terminal']);
  88. //返回登录信息
  89. $avatar = $user->avatar ?: Config::get('project.default_image.user_avatar');
  90. $avatar = FileService::getFileUrl($avatar);
  91. return [
  92. 'nickname' => $userInfo['nickname'],
  93. 'sn' => $userInfo['sn'],
  94. 'mobile' => $userInfo['mobile'],
  95. 'avatar' => $avatar,
  96. 'token' => $userInfo['token'],
  97. ];
  98. } catch (\Exception $e) {
  99. self::setError($e->getMessage());
  100. return false;
  101. }
  102. }
  103. /**
  104. * @notes 退出登录
  105. * @param $userInfo
  106. * @return bool
  107. * @throws \think\db\exception\DataNotFoundException
  108. * @throws \think\db\exception\DbException
  109. * @throws \think\db\exception\ModelNotFoundException
  110. * @author 段誉
  111. * @date 2022/9/16 17:56
  112. */
  113. public static function logout($userInfo)
  114. {
  115. //token不存在,不注销
  116. if (!isset($userInfo['token'])) {
  117. return false;
  118. }
  119. //设置token过期
  120. return UserTokenService::expireToken($userInfo['token']);
  121. }
  122. /**
  123. * @notes 获取微信请求code的链接
  124. * @param string $url
  125. * @return string
  126. * @author 段誉
  127. * @date 2022/9/20 19:47
  128. */
  129. public static function codeUrl(string $url)
  130. {
  131. return (new WeChatOaService())->getCodeUrl($url);
  132. }
  133. /**
  134. * @notes 公众号登录
  135. * @param array $params
  136. * @return array|false
  137. * @throws \GuzzleHttp\Exception\GuzzleException
  138. * @author 段誉
  139. * @date 2022/9/20 19:47
  140. */
  141. public static function oaLogin(array $params)
  142. {
  143. Db::startTrans();
  144. try {
  145. //通过code获取微信 openid
  146. $response = (new WeChatOaService())->getOaResByCode($params['code']);
  147. $userServer = new WechatUserService($response, UserTerminalEnum::WECHAT_OA);
  148. $userInfo = $userServer->getResopnseByUserInfo()->authUserLogin()->getUserInfo();
  149. // 更新登录信息
  150. self::updateLoginInfo($userInfo['id']);
  151. Db::commit();
  152. return $userInfo;
  153. } catch (\Exception $e) {
  154. Db::rollback();
  155. self::$error = $e->getMessage();
  156. return false;
  157. }
  158. }
  159. /**
  160. * @notes 小程序-静默登录
  161. * @param array $params
  162. * @return array|false
  163. * @author 段誉
  164. * @date 2022/9/20 19:47
  165. */
  166. public static function silentLogin(array $params)
  167. {
  168. try {
  169. //通过code获取微信 openid
  170. $response = (new WeChatMnpService())->getMnpResByCode($params['code']);
  171. $userServer = new WechatUserService($response, UserTerminalEnum::WECHAT_MMP);
  172. $userInfo = $userServer->getResopnseByUserInfo('silent')->getUserInfo();
  173. if (!empty($userInfo)) {
  174. // 更新登录信息
  175. self::updateLoginInfo($userInfo['id']);
  176. }
  177. return $userInfo;
  178. } catch (\Exception $e) {
  179. self::$error = $e->getMessage();
  180. return false;
  181. }
  182. }
  183. /**
  184. * @notes 小程序-授权登录
  185. * @param array $params
  186. * @return array|false
  187. * @author 段誉
  188. * @date 2022/9/20 19:47
  189. */
  190. public static function mnpLogin(array $params)
  191. {
  192. Db::startTrans();
  193. try {
  194. //通过code获取微信 openid
  195. $response = (new WeChatMnpService())->getMnpResByCode($params['code']);
  196. $userServer = new WechatUserService($response, UserTerminalEnum::WECHAT_MMP);
  197. $userInfo = $userServer->getResopnseByUserInfo()->authUserLogin()->getUserInfo();
  198. // 更新登录信息
  199. self::updateLoginInfo($userInfo['id']);
  200. Db::commit();
  201. return $userInfo;
  202. } catch (\Exception $e) {
  203. Db::rollback();
  204. self::$error = $e->getMessage();
  205. return false;
  206. }
  207. }
  208. /**
  209. * @notes 更新登录信息
  210. * @param $userId
  211. * @throws \Exception
  212. * @author 段誉
  213. * @date 2022/9/20 19:46
  214. */
  215. public static function updateLoginInfo($userId)
  216. {
  217. $user = User::findOrEmpty($userId);
  218. if ($user->isEmpty()) {
  219. throw new \Exception('用户不存在');
  220. }
  221. $time = time();
  222. $user->login_time = $time;
  223. $user->login_ip = request()->ip();
  224. $user->update_time = $time;
  225. $user->save();
  226. }
  227. /**
  228. * @notes 小程序端绑定微信
  229. * @param array $params
  230. * @return bool
  231. * @author 段誉
  232. * @date 2022/9/20 19:46
  233. */
  234. public static function mnpAuthLogin(array $params)
  235. {
  236. try {
  237. //通过code获取微信openid
  238. $response = (new WeChatMnpService())->getMnpResByCode($params['code']);
  239. $response['user_id'] = $params['user_id'];
  240. $response['terminal'] = UserTerminalEnum::WECHAT_MMP;
  241. return self::createAuth($response);
  242. } catch (\Exception $e) {
  243. self::$error = $e->getMessage();
  244. return false;
  245. }
  246. }
  247. /**
  248. * @notes 公众号端绑定微信
  249. * @param array $params
  250. * @return bool
  251. * @throws \GuzzleHttp\Exception\GuzzleException
  252. * @author 段誉
  253. * @date 2022/9/16 10:43
  254. */
  255. public static function oaAuthLogin(array $params)
  256. {
  257. try {
  258. //通过code获取微信openid
  259. $response = (new WeChatOaService())->getOaResByCode($params['code']);
  260. $response['user_id'] = $params['user_id'];
  261. $response['terminal'] = UserTerminalEnum::WECHAT_OA;
  262. return self::createAuth($response);
  263. } catch (\Exception $e) {
  264. self::$error = $e->getMessage();
  265. return false;
  266. }
  267. }
  268. /**
  269. * @notes 生成授权记录
  270. * @param $response
  271. * @return bool
  272. * @throws \Exception
  273. * @author 段誉
  274. * @date 2022/9/16 10:43
  275. */
  276. public static function createAuth($response)
  277. {
  278. //先检查openid是否有记录
  279. $isAuth = UserAuth::where('openid', '=', $response['openid'])->findOrEmpty();
  280. if (!$isAuth->isEmpty()) {
  281. if($isAuth->user_id != $response['user_id']) {
  282. throw new \Exception('该微信已被绑定');
  283. }
  284. if($isAuth->user_id == 0) {
  285. //更新操作
  286. $isAuth->user_id = $response['user_id'];
  287. $isAuth->save();
  288. return true;
  289. }
  290. if($isAuth->user_id == $response['user_id']) {
  291. return true;
  292. }
  293. }
  294. if (isset($response['unionid']) && !empty($response['unionid'])) {
  295. //在用unionid找记录,防止生成两个账号,同个unionid的问题
  296. $userAuth = UserAuth::where(['unionid' => $response['unionid']])
  297. ->findOrEmpty();
  298. if (!$userAuth->isEmpty() && $userAuth->user_id != $response['user_id']) {
  299. throw new \Exception('该微信已被绑定');
  300. }
  301. }
  302. //如果没有授权,直接生成一条微信授权记录
  303. UserAuth::create([
  304. 'user_id' => $response['user_id'],
  305. 'openid' => $response['openid'],
  306. 'unionid' => $response['unionid'] ?? '',
  307. 'terminal' => $response['terminal'],
  308. ]);
  309. return true;
  310. }
  311. /**
  312. * @notes 获取扫码登录地址
  313. * @return array|false
  314. * @author 段誉
  315. * @date 2022/10/20 18:23
  316. */
  317. public static function getScanCode($redirectUri)
  318. {
  319. try {
  320. $config = WeChatConfigService::getOpConfig();
  321. $appId = $config['app_id'];
  322. $redirectUri = UrlEncode($redirectUri);
  323. // 设置有效时间标记状态, 超时扫码不可登录
  324. $state = MD5(time().rand(10000, 99999));
  325. (new WebScanLoginCache())->setScanLoginState($state);
  326. // 扫码地址
  327. $url = WeChatRequestService::getScanCodeUrl($appId, $redirectUri, $state);
  328. return ['url' => $url];
  329. } catch (\Exception $e) {
  330. self::$error = $e->getMessage();
  331. return false;
  332. }
  333. }
  334. /**
  335. * @notes 网站扫码登录
  336. * @param $params
  337. * @return array|false
  338. * @author 段誉
  339. * @date 2022/10/21 10:28
  340. */
  341. public static function scanLogin($params)
  342. {
  343. Db::startTrans();
  344. try {
  345. // 通过code 获取 access_token,openid,unionid等信息
  346. $userAuth = WeChatRequestService::getUserAuthByCode($params['code']);
  347. if (empty($userAuth['openid']) || empty($userAuth['access_token'])) {
  348. throw new \Exception('获取用户授权信息失败');
  349. }
  350. // 获取微信用户信息
  351. $response = WeChatRequestService::getUserInfoByAuth($userAuth['access_token'], $userAuth['openid']);
  352. // 生成用户或更新用户信息
  353. $userServer = new WechatUserService($response, UserTerminalEnum::PC);
  354. $userInfo = $userServer->getResopnseByUserInfo()->authUserLogin()->getUserInfo();
  355. // 更新登录信息
  356. self::updateLoginInfo($userInfo['id']);
  357. Db::commit();
  358. return $userInfo;
  359. } catch (\Exception $e) {
  360. Db::rollback();
  361. self::$error = $e->getMessage();
  362. return false;
  363. }
  364. }
  365. /**
  366. * @notes 更新用户信息
  367. * @param $params
  368. * @param $userId
  369. * @return User
  370. * @author 段誉
  371. * @date 2023/2/22 11:19
  372. */
  373. public static function updateUser($params, $userId)
  374. {
  375. return User::where(['id' => $userId])->update([
  376. 'nickname' => $params['nickname'],
  377. 'avatar' => FileService::setFileUrl($params['avatar']),
  378. 'is_new_user' => YesNoEnum::NO
  379. ]);
  380. }
  381. public static function firmLogin($params)
  382. {
  383. try {
  384. // 账号/手机号 密码登录
  385. $where = ['account|mobile' => $params['account']];
  386. $user = User::where($where)->findOrEmpty();
  387. if ($user->isEmpty()) {
  388. throw new \Exception('用户不存在');
  389. }
  390. if($user->user_type != YesNoEnum::YES){
  391. throw new \Exception('普通用户不能登录后台');
  392. }
  393. //更新登录信息
  394. $user->login_time = time();
  395. $user->login_ip = request()->ip();
  396. $user->save();
  397. //设置token
  398. $userInfo = UserTokenService::setToken($user->id, $params['terminal']);
  399. //返回登录信息
  400. $avatar = $user->avatar ?: Config::get('project.default_image.user_avatar');
  401. $avatar = FileService::getFileUrl($avatar);
  402. return [
  403. 'nickname' => $userInfo['nickname'],
  404. 'sn' => $userInfo['sn'],
  405. 'mobile' => $userInfo['mobile'],
  406. 'avatar' => $avatar,
  407. 'token' => $userInfo['token'],
  408. ];
  409. } catch (\Exception $e) {
  410. self::setError($e->getMessage());
  411. return false;
  412. }
  413. }
  414. }