LoginServices.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2016~2023 https://www.crmeb.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  8. // +----------------------------------------------------------------------
  9. // | Author: CRMEB Team <admin@crmeb.com>
  10. // +----------------------------------------------------------------------
  11. declare (strict_types=1);
  12. namespace app\services\user;
  13. use app\dao\user\UserDao;
  14. use app\services\BaseServices;
  15. use app\services\yihaotong\SmsRecordServices;
  16. use app\services\message\notice\SmsService;
  17. use app\services\wechat\WechatUserServices;
  18. use crmeb\exceptions\ApiException;
  19. use crmeb\services\CacheService;
  20. use crmeb\services\HttpService;
  21. use Firebase\JWT\JWT;
  22. use think\facade\Config;
  23. /**
  24. *
  25. * Class LoginServices
  26. * @package app\services\user
  27. */
  28. class LoginServices extends BaseServices
  29. {
  30. /**
  31. * LoginServices constructor.
  32. * @param UserDao $dao
  33. */
  34. public function __construct(UserDao $dao)
  35. {
  36. $this->dao = $dao;
  37. }
  38. /**
  39. * H5账号登陆
  40. * @param $account
  41. * @param $password
  42. * @param $spread
  43. * @return array
  44. * @throws \think\db\exception\DataNotFoundException
  45. * @throws \think\db\exception\DbException
  46. * @throws \think\db\exception\ModelNotFoundException
  47. */
  48. public function login($account, $password, $spread)
  49. {
  50. $user = $this->dao->getOne(['account|phone' => $account, 'is_del' => 0]);
  51. if ($user) {
  52. if ($user->pwd !== md5((string)$password))
  53. throw new ApiException(410025);
  54. if ($user->pwd === md5('123456'))
  55. throw new ApiException(410026);
  56. } else {
  57. throw new ApiException(410025);
  58. }
  59. if (!$user['status'])
  60. throw new ApiException(410027);
  61. //更新用户信息
  62. $this->updateUserInfo(['code' => $spread], $user);
  63. $token = $this->createToken((int)$user['uid'], 'api');
  64. if ($token) {
  65. return ['token' => $token['token'], 'expires_time' => $token['params']['exp']];
  66. } else
  67. throw new ApiException(410019);
  68. }
  69. /**
  70. * 更新用户信息
  71. * @param $user
  72. * @param $userInfo
  73. * @param false $is_new
  74. * @return bool
  75. * @throws \think\db\exception\DataNotFoundException
  76. * @throws \think\db\exception\DbException
  77. * @throws \think\db\exception\ModelNotFoundException
  78. */
  79. public function updateUserInfo($user, $userInfo, $is_new = false)
  80. {
  81. $data = [];
  82. $data['phone'] = !isset($user['phone']) || !$user['phone'] ? $userInfo->phone : $user['phone'];
  83. $data['last_time'] = time();
  84. $data['last_ip'] = app()->request->ip();
  85. $spreadUid = $user['code'] ?? 0;
  86. //如果扫了员工邀请码,上级,代理商,区域代理都会改动。
  87. if (isset($user['is_staff']) && !$userInfo['is_agent'] && !$userInfo['is_division']) {
  88. $spreadInfo = $this->dao->get($spreadUid);
  89. if ($userInfo['uid'] != $spreadUid) {
  90. $data['spread_uid'] = $spreadUid;
  91. $data['spread_time'] = $userInfo->last_time;
  92. }
  93. $data['agent_id'] = $spreadInfo->agent_id;
  94. $data['division_id'] = $spreadInfo->division_id;
  95. $data['staff_id'] = $userInfo['uid'];
  96. $data['is_staff'] = $user['is_staff'] ?? 0;
  97. $data['division_type'] = 3;
  98. $data['division_status'] = 1;
  99. $data['division_change_time'] = time();
  100. $data['division_end_time'] = $spreadInfo->division_end_time;
  101. //如果店员切换代理商,则店员在之前代理商下推广的用户,他们的直接上级从当前店员变为之前代理商
  102. if ($userInfo->agent_id != 0 && $userInfo->agent_id != $spreadInfo->agent_id) {
  103. $this->dao->update(['staff_id' => $userInfo['uid'], 'spread_uid' => $userInfo['uid']], ['spread_uid' => $spreadInfo['agent_id'], 'staff_id' => 0]);
  104. $this->dao->update(['staff_id' => $userInfo['uid'], 'not_spread_uid' => $userInfo['uid']], ['staff_id' => 0]);
  105. }
  106. //绑定用户后置事件
  107. event('UserRegisterListener', [$spreadUid, $userInfo['user_type'], $userInfo['nickname'], $userInfo['uid'], $is_new]);
  108. //推送消息
  109. event('NoticeListener', [['spreadUid' => $spreadUid, 'user_type' => $userInfo['user_type'], 'nickname' => $userInfo['nickname']], 'bind_spread_uid']);
  110. //自定义事件-绑定关系
  111. event('CustomEventListener', ['user_spread', [
  112. 'uid' => $userInfo['uid'],
  113. 'nickname' => $userInfo['nickname'],
  114. 'spread_uid' => $spreadUid,
  115. 'spread_time' => date('Y-m-d H:i:s'),
  116. 'user_type' => $user->user_type,
  117. ]]);
  118. } else {
  119. if ($is_new) {
  120. if ($spreadUid) {
  121. $spreadInfo = $this->dao->get($spreadUid);
  122. $spreadUid = (int)$spreadUid;
  123. $data['spread_uid'] = $spreadUid;
  124. $data['spread_time'] = time();
  125. $data['agent_id'] = $spreadInfo->agent_id;
  126. $data['division_id'] = $spreadInfo->division_id;
  127. $data['staff_id'] = $spreadInfo->staff_id;
  128. //绑定用户后置事件
  129. event('UserRegisterListener', [$spreadUid, $userInfo['user_type'], $userInfo['nickname'], $userInfo['uid'], 1]);
  130. //推送消息
  131. event('NoticeListener', [['spreadUid' => $spreadUid, 'user_type' => $userInfo['user_type'], 'nickname' => $userInfo['nickname']], 'bind_spread_uid']);
  132. //自定义事件-绑定关系
  133. event('CustomEventListener', ['user_spread', [
  134. 'uid' => $userInfo['uid'],
  135. 'nickname' => $userInfo['nickname'],
  136. 'spread_uid' => $spreadUid,
  137. 'spread_time' => date('Y-m-d H:i:s'),
  138. 'user_type' => $user->user_type,
  139. ]]);
  140. }
  141. } else {
  142. //永久绑定
  143. $store_brokerage_binding_status = sys_config('store_brokerage_binding_status', 1);
  144. if ($userInfo->spread_uid && $store_brokerage_binding_status == 1 && !isset($user['is_staff'])) {
  145. $data['login_type'] = $user['login_type'] ?? $userInfo->login_type;
  146. } else {
  147. //绑定分销关系 = 所有用户
  148. if (sys_config('brokerage_bindind', 1) == 1) {
  149. //分销绑定类型为时间段且过期 ||临时
  150. $store_brokerage_binding_time = sys_config('store_brokerage_binding_time', 30);
  151. if (!$userInfo['spread_uid'] || $store_brokerage_binding_status == 3 || ($store_brokerage_binding_status == 2 && ($userInfo['spread_time'] + $store_brokerage_binding_time * 24 * 3600) < time())) {
  152. if ($spreadUid && $user['code'] != $userInfo->uid && $userInfo->uid != $this->dao->value(['uid' => $spreadUid], 'spread_uid')) {
  153. $spreadInfo = $this->dao->get($spreadUid);
  154. $spreadUid = (int)$spreadUid;
  155. $data['spread_uid'] = $spreadUid;
  156. $data['spread_time'] = time();
  157. $data['agent_id'] = $spreadInfo->agent_id;
  158. $data['division_id'] = $spreadInfo->division_id;
  159. $data['staff_id'] = $spreadInfo->staff_id;
  160. //绑定用户后置事件
  161. event('UserRegisterListener', [$spreadUid, $userInfo['user_type'], $userInfo['nickname'], $userInfo['uid'], 0]);
  162. //推送消息
  163. event('NoticeListener', [['spreadUid' => $spreadUid, 'user_type' => $userInfo['user_type'], 'nickname' => $userInfo['nickname']], 'bind_spread_uid']);
  164. //自定义事件-绑定关系
  165. event('CustomEventListener', ['user_spread', [
  166. 'uid' => $userInfo['uid'],
  167. 'nickname' => $userInfo['nickname'],
  168. 'spread_uid' => $spreadUid,
  169. 'spread_time' => date('Y-m-d H:i:s'),
  170. 'user_type' => $user->user_type,
  171. ]]);
  172. }
  173. }
  174. }
  175. }
  176. }
  177. }
  178. if (!$this->dao->update($userInfo['uid'], $data, 'uid')) {
  179. throw new ApiException(100007);
  180. }
  181. return true;
  182. }
  183. public function verify(SmsService $services, $phone, $type, $time)
  184. {
  185. if ($this->dao->getOne(['account' => $phone, 'is_del' => 0]) && $type == 'register') {
  186. throw new ApiException(410028);
  187. }
  188. $code = rand(100000, 999999);
  189. $data['code'] = $code;
  190. $data['time'] = $time;
  191. $res = $services->send(true, $phone, $data, 'verify_code');
  192. if ($res !== true)
  193. throw new ApiException(410031);
  194. return $code;
  195. }
  196. /**
  197. * H5用户注册
  198. * @param $account
  199. * @param $password
  200. * @param $spread
  201. * @param string $user_type
  202. * @return mixed
  203. * @throws \think\db\exception\DataNotFoundException
  204. * @throws \think\db\exception\DbException
  205. * @throws \think\db\exception\ModelNotFoundException
  206. */
  207. public function register($account, $password, $spread, $user_type = 'h5')
  208. {
  209. if ($this->dao->getOne(['account|phone' => $account, 'is_del' => 0])) {
  210. throw new ApiException(410028);
  211. }
  212. /** @var UserServices $userServices */
  213. $userServices = app()->make(UserServices::class);
  214. $phone = $account;
  215. $data['account'] = $account;
  216. $data['pwd'] = md5((string)$password);
  217. $data['phone'] = $phone;
  218. if ($spread) {
  219. $data['spread_uid'] = $spread;
  220. $data['spread_time'] = time();
  221. $spreadInfo = $userServices->get($spread);
  222. $data['division_id'] = $spreadInfo['division_id'];
  223. $data['agent_id'] = $spreadInfo['agent_id'];
  224. $data['staff_id'] = $spreadInfo['staff_id'];
  225. }
  226. $data['real_name'] = '';
  227. $data['birthday'] = 0;
  228. $data['card_id'] = '';
  229. $data['mark'] = '';
  230. $data['addres'] = '';
  231. $data['user_type'] = $user_type;
  232. $data['add_time'] = time();
  233. $data['add_ip'] = app('request')->ip();
  234. $data['last_time'] = time();
  235. $data['last_ip'] = app('request')->ip();
  236. $data['nickname'] = substr_replace($account, '****', 3, 4);
  237. $data['avatar'] = sys_config('h5_avatar');
  238. $data['city'] = '';
  239. $data['language'] = '';
  240. $data['province'] = '';
  241. $data['country'] = '';
  242. $data['status'] = 1;
  243. if (!$re = $this->dao->save($data)) {
  244. throw new ApiException(410014);
  245. } else {
  246. $userServices->rewardNewUser((int)$re->uid);
  247. //用户生成后置事件
  248. event('UserRegisterListener', [$spread, $user_type, $data['nickname'], $re->uid, 1]);
  249. //自定义事件-用户注册
  250. event('CustomEventListener', ['user_register', [
  251. 'uid' => $re->uid,
  252. 'nickname' => $data['nickname'],
  253. 'phone' => $data['phone'],
  254. 'add_time' => date('Y-m-d H:i:s'),
  255. 'user_type' => $user_type,
  256. ]]);
  257. if ($spread) {
  258. //推送消息
  259. event('NoticeListener', [['spreadUid' => $spread, 'user_type' => $user_type, 'nickname' => $data['nickname']], 'bind_spread_uid']);
  260. //自定义事件-绑定关系
  261. event('CustomEventListener', ['user_spread', [
  262. 'uid' => $re->uid,
  263. 'nickname' => $data['nickname'],
  264. 'spread_uid' => $spread,
  265. 'spread_time' => date('Y-m-d H:i:s'),
  266. 'user_type' => $user_type,
  267. ]]);
  268. }
  269. return $re;
  270. }
  271. }
  272. /**
  273. * 重置密码
  274. * @param $account
  275. * @param $password
  276. * @return bool
  277. * @throws \think\db\exception\DataNotFoundException
  278. * @throws \think\db\exception\DbException
  279. * @throws \think\db\exception\ModelNotFoundException
  280. */
  281. public function reset($account, $password)
  282. {
  283. $user = $this->dao->getOne(['account|phone' => $account, 'is_del' => 0], 'uid');
  284. if (!$user) {
  285. throw new ApiException(410032);
  286. }
  287. if (!$this->dao->update($user['uid'], ['pwd' => md5((string)$password)], 'uid')) {
  288. throw new ApiException(410033);
  289. }
  290. return true;
  291. }
  292. /**
  293. * 手机号登录
  294. * @param $phone
  295. * @param $spread
  296. * @param string $user_type
  297. * @return array
  298. * @throws \think\db\exception\DataNotFoundException
  299. * @throws \think\db\exception\DbException
  300. * @throws \think\db\exception\ModelNotFoundException
  301. */
  302. public function mobile($phone, $spread, string $user_type = 'h5')
  303. {
  304. //数据库查询
  305. $user = $this->dao->getOne(['account|phone' => $phone, 'is_del' => 0]);
  306. if (!$user) {
  307. $user = $this->register($phone, '123456', $spread, $user_type);
  308. if (!$user) {
  309. throw new ApiException(410034);
  310. }
  311. }
  312. if (!$user->status)
  313. throw new ApiException(410027);
  314. // 设置推广关系
  315. $this->updateUserInfo(['code' => $spread], $user);
  316. $token = $this->createToken((int)$user['uid'], 'api');
  317. if ($token) {
  318. return ['token' => $token['token'], 'expires_time' => $token['params']['exp']];
  319. } else {
  320. throw new ApiException(410019);
  321. }
  322. }
  323. /**
  324. * 切换登录
  325. * @param $user
  326. * @param $from
  327. * @return array
  328. * @throws \think\db\exception\DataNotFoundException
  329. * @throws \think\db\exception\DbException
  330. * @throws \think\db\exception\ModelNotFoundException
  331. */
  332. public function switchAccount($user, $from)
  333. {
  334. if ($from === 'h5') {
  335. $where = [['phone', '=', $user['phone']], ['user_type', '<>', 'h5'], ['is_del', '=', 0]];
  336. $login_type = 'wechat';
  337. } else {
  338. //数据库查询
  339. $where = [['account|phone', '=', $user['phone']], ['user_type', '=', 'h5'], ['is_del', '=', 0]];
  340. $login_type = 'h5';
  341. }
  342. $switch_user = $this->dao->getOne($where);
  343. if (!$switch_user) {
  344. return app('json')->fail(410035);
  345. }
  346. if (!$switch_user->status) {
  347. return app('json')->fail(410027);
  348. }
  349. $edit_data = ['login_type' => $login_type];
  350. if (!$this->dao->update($switch_user['uid'], $edit_data, 'uid')) {
  351. throw new ApiException(410036);
  352. }
  353. $token = $this->createToken((int)$switch_user['uid'], 'api');
  354. if ($token) {
  355. return ['token' => $token['token'], 'expires_time' => $token['params']['exp']];
  356. } else {
  357. throw new ApiException(410019);
  358. }
  359. }
  360. /**
  361. * 绑定手机号(静默还没写入用户信息)
  362. * @param $phone
  363. * @param string $key
  364. * @return array
  365. * @throws \Psr\SimpleCache\InvalidArgumentException
  366. * @throws \think\db\exception\DataNotFoundException
  367. * @throws \think\db\exception\ModelNotFoundException
  368. */
  369. public function bindind_phone($phone, string $key = '')
  370. {
  371. if (!$key) {
  372. throw new ApiException(410037);
  373. }
  374. [$openid, $wechatInfo, $spreadId, $login_type, $userType] = $createData = CacheService::get($key);
  375. if (!$createData) {
  376. throw new ApiException(410037);
  377. }
  378. $wechatInfo['phone'] = $phone;
  379. /** @var WechatUserServices $wechatUser */
  380. $wechatUser = app()->make(WechatUserServices::class);
  381. //更新用户信息
  382. $user = $wechatUser->wechatOauthAfter([$openid, $wechatInfo, $spreadId, $login_type, $userType]);
  383. $token = $this->createToken((int)$user['uid'], 'api');
  384. if ($token) {
  385. return [
  386. 'token' => $token['token'],
  387. 'userInfo' => $user,
  388. 'expires_time' => $token['params']['exp'],
  389. ];
  390. } else
  391. return app('json')->fail(410019);
  392. }
  393. /**
  394. * 用户绑定手机号
  395. * @param int $uid
  396. * @param $phone
  397. * @param $step
  398. * @return array
  399. * @throws \think\db\exception\DataNotFoundException
  400. * @throws \think\db\exception\DbException
  401. * @throws \think\db\exception\ModelNotFoundException
  402. */
  403. public function userBindindPhone(int $uid, $phone, $step)
  404. {
  405. $userInfo = $this->dao->get($uid);
  406. if (!$userInfo) {
  407. throw new ApiException(410113);
  408. }
  409. if ($this->dao->getOne([['phone', '=', $phone], ['user_type', '<>', 'h5'], ['is_del', '=', 0]])) {
  410. throw new ApiException(410039);
  411. }
  412. if ($userInfo->phone) {
  413. throw new ApiException(410040);
  414. }
  415. $data = [];
  416. if ($this->dao->getOne(['account' => $phone, 'phone' => $phone, 'user_type' => 'h5', 'is_del' => 0])) {
  417. if (!$step) return ['msg' => 410041, 'data' => ['is_bind' => 1]];
  418. } else {
  419. $data['account'] = $phone;
  420. }
  421. $data['phone'] = $phone;
  422. if ($this->dao->update($userInfo['uid'], $data, 'uid') || $userInfo->phone == $phone)
  423. return ['msg' => 410016, 'data' => []];
  424. else
  425. throw new ApiException(410017);
  426. }
  427. /**
  428. * 用户绑定手机号
  429. * @param int $uid
  430. * @param $phone
  431. * @return array
  432. * @throws \think\db\exception\DataNotFoundException
  433. * @throws \think\db\exception\DbException
  434. * @throws \think\db\exception\ModelNotFoundException
  435. */
  436. public function updateBindindPhone(int $uid, $phone)
  437. {
  438. $userInfo = $this->dao->get(['uid' => $uid, 'is_del' => 0]);
  439. if (!$userInfo) {
  440. throw new ApiException(410113);
  441. }
  442. if ($userInfo->phone == $phone) {
  443. throw new ApiException(410042);
  444. }
  445. if ($this->dao->getOne([['phone', '=', $phone], ['is_del', '=', 0]])) {
  446. throw new ApiException(410043);
  447. }
  448. $data = [];
  449. $data['phone'] = $phone;
  450. $data['account'] = $phone;
  451. if ($this->dao->update($userInfo['uid'], $data, 'uid'))
  452. return ['msg' => 100001, 'data' => []];
  453. else
  454. throw new ApiException(100007);
  455. }
  456. /**
  457. * 远程注册登录
  458. * @param string $out_token
  459. * @return array
  460. * @throws \Psr\SimpleCache\InvalidArgumentException
  461. * @throws \think\db\exception\DataNotFoundException
  462. * @throws \think\db\exception\DbException
  463. * @throws \think\db\exception\ModelNotFoundException
  464. * @author wuhaotian
  465. * @email 442384644@qq.com
  466. * @date 2024/5/21
  467. */
  468. public function remoteRegister(string $out_token = '')
  469. {
  470. $info = JWT::jsonDecode(JWT::urlsafeB64Decode($out_token));
  471. $userInfo = $this->dao->get(['uid' => $info->uid]);
  472. $data = [];
  473. if (!$userInfo) {
  474. $data['uid'] = $info->uid;
  475. $data['account'] = $info->phone != '' ? $info->phone : 'out_' . $info->uid;
  476. $data['phone'] = $info->phone;
  477. $data['pwd'] = md5('123456');
  478. $data['real_name'] = $info->nickname;
  479. $data['birthday'] = 0;
  480. $data['card_id'] = '';
  481. $data['mark'] = '';
  482. $data['addres'] = '';
  483. $data['user_type'] = 'h5';
  484. $data['add_time'] = time();
  485. $data['add_ip'] = app('request')->ip();
  486. $data['last_time'] = time();
  487. $data['last_ip'] = app('request')->ip();
  488. $data['nickname'] = $info->nickname;
  489. $data['avatar'] = $info->avatar;
  490. $data['city'] = '';
  491. $data['language'] = '';
  492. $data['province'] = '';
  493. $data['country'] = '';
  494. $data['status'] = 1;
  495. $data['now_money'] = $info->now_money;
  496. $data['integral'] = $info->integral;
  497. $data['exp'] = $info->exp;
  498. $this->dao->save($data);
  499. } else {
  500. $data['nickname'] = $info->nickname;
  501. $data['avatar'] = $info->avatar;
  502. $data['now_money'] = $info->now_money;
  503. $data['integral'] = $info->integral;
  504. $data['exp'] = $info->exp;
  505. $this->dao->update($info->uid, $data);
  506. }
  507. $token = $this->createToken((int)$info->uid, 'api');
  508. if ($token) {
  509. return ['token' => $token['token'], 'expires_time' => $token['params']['exp']];
  510. } else {
  511. throw new ApiException('登录失败');
  512. }
  513. }
  514. }