LoginServices.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2016~2020 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\jobs\notice\SmsJob;
  14. use app\services\BaseServices;
  15. use app\dao\user\UserDao;
  16. use app\services\message\sms\SmsRecordServices;
  17. use app\services\wechat\WechatUserServices;
  18. use crmeb\services\CacheService;
  19. use think\exception\ValidateException;
  20. use think\facade\Config;
  21. /**
  22. *
  23. * Class LoginServices
  24. * @package app\services\user
  25. * @mixin UserDao
  26. */
  27. class LoginServices extends BaseServices
  28. {
  29. /**
  30. * LoginServices constructor.
  31. * @param UserDao $dao
  32. */
  33. public function __construct(UserDao $dao)
  34. {
  35. $this->dao = $dao;
  36. }
  37. /**
  38. * H5账号登陆
  39. * @param Request $request
  40. * @return mixed
  41. * @throws \think\db\exception\DataNotFoundException
  42. * @throws \think\db\exception\ModelNotFoundException
  43. * @throws \think\exception\DbException
  44. */
  45. public function login($account, $password, $spread_uid)
  46. {
  47. $user = $this->dao->getOne(['phone' => $account], 'uid,pwd,status');
  48. if ($user) {
  49. if ($user->pwd !== md5((string)$password))
  50. throw new ValidateException('账号或密码错误');
  51. if ($user->pwd === md5('123456'))
  52. throw new ValidateException('请修改您的初始密码,再尝试登录!');
  53. } else {
  54. throw new ValidateException('账号或密码错误');
  55. }
  56. if (!$user['status'])
  57. throw new ValidateException('已被禁止,请联系管理员');
  58. //更新用户信息
  59. $token = $this->createToken((int)$user['uid'], 'api', $user->pwd);
  60. if ($token) {
  61. // 用户登录成功事件
  62. $this->updateUserInfo(['spread_uid' => $spread_uid], $user);
  63. return ['token' => $token['token'], 'expires_time' => $token['params']['exp']];
  64. } else
  65. throw new ValidateException('登录失败');
  66. }
  67. /**
  68. * 更新用户信息
  69. * @param $user
  70. * @param $uid
  71. * @return bool
  72. * @throws \think\db\exception\DataNotFoundException
  73. * @throws \think\db\exception\DbException
  74. * @throws \think\db\exception\ModelNotFoundException
  75. */
  76. public function updateUserInfo($user, $userInfo)
  77. {
  78. $data = [];
  79. if (isset($userInfo['nickname']) && $userInfo['nickname']) {
  80. $data['nickname'] = !isset($user['nickname']) || !$user['nickname'] ? $userInfo->nickname : $user['nickname'];
  81. }
  82. if (isset($userInfo['avatar']) && $userInfo['avatar']) {
  83. $data['avatar'] = !isset($user['headimgurl']) || !$user['headimgurl'] ? $userInfo->avatar : $user['headimgurl'];
  84. }
  85. if (isset($userInfo['phone']) && $userInfo['phone']) {
  86. $data['phone'] = !isset($user['phone']) || !$user['phone'] ? $userInfo->phone : $user['phone'];
  87. }
  88. $data['last_time'] = time();
  89. $data['last_ip'] = app()->request->ip();
  90. //永久绑定
  91. $store_brokergae_binding_status = sys_config('store_brokerage_binding_status', 1);
  92. $spread_uid = isset($user['code']) && $user['code'] && $user['code'] != $userInfo->uid ? $user['code'] : ($user['spread_uid'] ?? 0);
  93. if ($userInfo->spread_uid && $store_brokergae_binding_status == 1) {
  94. $data['login_type'] = $user['login_type'] ?? $userInfo->login_type;
  95. } else {
  96. //绑定分销关系 = 所有用户
  97. if (sys_config('brokerage_bindind', 1) == 1) {
  98. //分销绑定类型为时间段且过期 ||临时
  99. $store_brokerage_binding_time = sys_config('store_brokerage_binding_time', 30);
  100. if (!$userInfo['spread_uid'] || $store_brokergae_binding_status == 3 || ($store_brokergae_binding_status == 2 && ($userInfo['spread_time'] + $store_brokerage_binding_time * 24 * 3600) < time())) {
  101. $spreadUid = $spread_uid;
  102. if ($spreadUid && $userInfo->uid == $this->dao->value(['uid' => $spreadUid], 'spread_uid')) {
  103. $spreadUid = 0;
  104. }
  105. if ($spreadUid && $this->dao->get((int)$spreadUid)) {
  106. $data['spread_uid'] = $spreadUid;
  107. $data['spread_time'] = time();
  108. }
  109. }
  110. }
  111. }
  112. if (!$this->dao->update($userInfo['uid'], $data, 'uid')) {
  113. throw new ValidateException('修改信息失败');
  114. }
  115. if (isset($data['spread_uid']) && $data['spread_uid']) {
  116. event('user.register', [$this->dao->get((int)$userInfo['uid']), false, $spread_uid]);
  117. //推送消息
  118. // event('notice.notice', [['spreadUid' => $spreadUid, 'user_type' => $userInfo['user_type'], 'nickname' => $userInfo['nickname']], 'bind_spread_uid']);
  119. }
  120. return true;
  121. }
  122. /**
  123. * 发送验证码
  124. * @param $phone
  125. * @param $type
  126. * @param $time
  127. * @param $ip
  128. * @return int
  129. * @throws \think\db\exception\DataNotFoundException
  130. * @throws \think\db\exception\DbException
  131. * @throws \think\db\exception\ModelNotFoundException
  132. */
  133. public function verify($phone, $type, $time, $ip)
  134. {
  135. if ($this->dao->getOne(['account' => $phone]) && $type == 'register') {
  136. throw new ValidateException('手机号已注册');
  137. }
  138. $default = Config::get('sms.default', 'yunxin');
  139. $defaultMaxPhoneCount = Config::get('sms.maxPhoneCount', 10);
  140. $defaultMaxIpCount = Config::get('sms.maxIpCount', 50);
  141. $maxPhoneCount = Config::get('sms.stores.' . $default . '.maxPhoneCount', $defaultMaxPhoneCount);
  142. $maxIpCount = Config::get('sms.stores.' . $default . '.maxIpCount', $defaultMaxIpCount);
  143. /** @var SmsRecordServices $smsRecord */
  144. $smsRecord = app()->make(SmsRecordServices::class);
  145. if ($smsRecord->count(['phone' => $phone, 'add_ip' => $ip, 'time' => 'today']) >= $maxPhoneCount) {
  146. throw new ValidateException('您今日发送得短信次数已经达到上限');
  147. }
  148. if ($smsRecord->count(['add_ip' => $ip, 'time' => 'today']) >= $maxIpCount) {
  149. throw new ValidateException('此IP今日发送次数已经达到上限');
  150. }
  151. if (CacheService::get('code_' . $phone))
  152. throw new ValidateException($time . '分钟内有效');
  153. mt_srand();
  154. $code = rand(100000, 999999);
  155. $data['code'] = $code;
  156. $data['time'] = $time;
  157. $res = SmsJob::dispatch([$phone, $data, 'VERIFICATION_CODE_TIME']);
  158. if (!$res)
  159. throw new ValidateException('短信平台验证码发送失败');
  160. return $code;
  161. }
  162. /**
  163. * H5用户注册
  164. * @param $account
  165. * @param $password
  166. * @param $spread_uid
  167. * @return User|\think\Model
  168. */
  169. public function register($account, $password, $spread_uid, $user_type = 'h5')
  170. {
  171. if ($this->dao->getOne(['phone' => $account])) {
  172. throw new ValidateException('用户已存在,请去修改密码');
  173. }
  174. $phone = $account;
  175. $data['account'] = $account;
  176. $data['pwd'] = md5((string)$password);
  177. $data['phone'] = $phone;
  178. if ($spread_uid && $this->dao->get((int)$spread_uid)) {
  179. $data['spread_uid'] = $spread_uid;
  180. $data['spread_time'] = time();
  181. }
  182. $data['real_name'] = '';
  183. $data['birthday'] = 0;
  184. $data['card_id'] = '';
  185. $data['mark'] = '';
  186. $data['addres'] = '';
  187. $data['user_type'] = $user_type;
  188. $data['add_time'] = time();
  189. $data['add_ip'] = app('request')->ip();
  190. $data['last_time'] = time();
  191. $data['last_ip'] = app('request')->ip();
  192. $data['nickname'] = substr(md5($account . time()), 0, 12);
  193. $data['avatar'] = $data['headimgurl'] = sys_config('h5_avatar');
  194. $data['city'] = '';
  195. $data['language'] = '';
  196. $data['province'] = '';
  197. $data['country'] = '';
  198. $data['status'] = 1;
  199. if (!$re = $this->dao->save($data)) {
  200. throw new ValidateException('注册失败');
  201. } else {
  202. //用户注册成功事件
  203. event('user.register', [$this->dao->get((int)$re->uid), true, $spread_uid]);
  204. //推送消息
  205. // event('notice.notice', [['spreadUid' => $spread, 'user_type' => $user_type, 'nickname' => $data['nickname']], 'bind_spread_uid']);
  206. return $re;
  207. }
  208. }
  209. /**
  210. * 重置密码
  211. * @param $account
  212. * @param $password
  213. */
  214. public function reset($account, $password)
  215. {
  216. $user = $this->dao->getOne(['phone' => $account]);
  217. if (!$user) {
  218. throw new ValidateException('用户不存在');
  219. }
  220. if (!$this->dao->update($user['uid'], ['pwd' => md5((string)$password)], 'uid')) {
  221. throw new ValidateException('修改密码失败');
  222. }
  223. return true;
  224. }
  225. /**
  226. * 手机号登录
  227. * @param $phone
  228. * @param $spread_uid
  229. * @return array
  230. * @throws \think\db\exception\DataNotFoundException
  231. * @throws \think\db\exception\DbException
  232. * @throws \think\db\exception\ModelNotFoundException
  233. */
  234. public function mobile($phone, $spread_uid, $user_type = 'h5')
  235. {
  236. //数据库查询
  237. $user = $this->dao->getOne(['phone' => $phone]);
  238. if (!$user) {
  239. $user = $this->register($phone, '123456', $spread_uid, $user_type);
  240. if (!$user) {
  241. throw new ValidateException('用户登录失败,无法生成新用户,请稍后再试!');
  242. }
  243. }
  244. if (!$user->status)
  245. throw new ValidateException('已被禁止,请联系管理员');
  246. // 设置推广关系
  247. $this->updateUserInfo(['spread_uid' => $spread_uid], $user);
  248. $token = $this->createToken((int)$user['uid'], 'api', $user->pwd);
  249. if ($token) {
  250. return ['token' => $token['token'], 'expires_time' => $token['params']['exp']];
  251. } else {
  252. throw new ValidateException('登录失败');
  253. }
  254. }
  255. /**
  256. * 切换登录
  257. * @param $user
  258. * @param $from
  259. */
  260. public function switchAccount($user, $from)
  261. {
  262. if ($from === 'h5') {
  263. $where = [['phone', '=', $user['phone']], ['user_type', '<>', 'h5']];
  264. $login_type = 'wechat';
  265. } else {
  266. //数据库查询
  267. $where = [['account|phone', '=', $user['phone']], ['user_type', '=', 'h5']];
  268. $login_type = 'h5';
  269. }
  270. $switch_user = $this->dao->getOne($where);
  271. if (!$switch_user) {
  272. return app('json')->fail('用户不存在,无法切换');
  273. }
  274. if (!$switch_user->status) {
  275. return app('json')->fail('已被禁止,请联系管理员');
  276. }
  277. $edit_data = ['login_type' => $login_type];
  278. if (!$this->dao->update($switch_user['uid'], $edit_data, 'uid')) {
  279. throw new ValidateException('修改新用户登录类型出错');
  280. }
  281. $token = $this->createToken((int)$switch_user['uid'], 'api', $switch_user['pwd']);
  282. if ($token) {
  283. return ['token' => $token['token'], 'expires_time' => $token['params']['exp']];
  284. } else {
  285. throw new ValidateException('切换失败');
  286. }
  287. }
  288. /**
  289. * 绑定手机号(静默还没写入用户信息)
  290. * @param $user
  291. * @param $phone
  292. * @param $step
  293. * @return mixed
  294. */
  295. public function bindind_phone($phone, $key = '')
  296. {
  297. if (!$key) {
  298. throw new ValidateException('请刷新页面或者重新授权');
  299. }
  300. [$openid, $wechatInfo, $spread_uid, $login_type, $userType] = $createData = CacheService::getTokenBucket($key);
  301. if (!$createData) {
  302. throw new ValidateException('请刷新页面或者重新授权');
  303. }
  304. $wechatInfo['phone'] = $phone;
  305. /** @var WechatUserServices $wechatUser */
  306. $wechatUser = app()->make(WechatUserServices::class);
  307. //更新用户信息
  308. $user = $wechatUser->wechatOauthAfter([$openid, $wechatInfo, $spread_uid, $login_type, $userType]);
  309. $token = $this->createToken((int)$user['uid'], $userType, $user['pwd']);
  310. if ($token) {
  311. return [
  312. 'token' => $token['token'],
  313. 'userInfo' => $user,
  314. 'expires_time' => $token['params']['exp'],
  315. ];
  316. } else
  317. return app('json')->fail('获取用户访问token失败!');
  318. }
  319. /**
  320. * 用户绑定手机号
  321. * @param $user
  322. * @param $phone
  323. * @param $step
  324. * @return mixed
  325. */
  326. public function userBindindPhone(int $uid, $phone, $step)
  327. {
  328. $userInfo = $this->dao->get($uid);
  329. if (!$userInfo) {
  330. throw new ValidateException('用户不存在');
  331. }
  332. if ($this->dao->getOne([['phone', '=', $phone], ['user_type', '<>', 'h5']])) {
  333. throw new ValidateException('此手机已经绑定,无法多次绑定!');
  334. }
  335. if ($userInfo->phone) {
  336. throw new ValidateException('您的账号已经绑定过手机号码!');
  337. }
  338. $data = [];
  339. if ($this->dao->getOne(['account' => $phone, 'phone' => $phone, 'user_type' => 'h5'])) {
  340. if (!$step) return ['msg' => 'H5已有账号是否绑定此账号上', 'data' => ['is_bind' => 1]];
  341. } else {
  342. $data['account'] = $phone;
  343. }
  344. $data['phone'] = $phone;
  345. if ($this->dao->update($userInfo['uid'], $data, 'uid') || $userInfo->phone == $phone)
  346. return ['msg' => '绑定成功', 'data' => []];
  347. else
  348. throw new ValidateException('绑定失败');
  349. }
  350. /**
  351. * 用户绑定手机号
  352. * @param $user
  353. * @param $phone
  354. * @param $step
  355. * @return mixed
  356. */
  357. public function updateBindindPhone(int $uid, $phone)
  358. {
  359. $userInfo = $this->dao->get($uid);
  360. if (!$userInfo) {
  361. throw new ValidateException('用户不存在');
  362. }
  363. if ($userInfo->phone == $phone) {
  364. throw new ValidateException('新手机号和原手机号相同,无需修改');
  365. }
  366. if ($this->dao->getOne([['phone', '=', $phone]])) {
  367. throw new ValidateException('此手机已经注册');
  368. }
  369. $data = [];
  370. $data['phone'] = $phone;
  371. if ($this->dao->update($userInfo['uid'], $data, 'uid'))
  372. return ['msg' => '修改成功', 'data' => []];
  373. else
  374. throw new ValidateException('修改失败');
  375. }
  376. }