LoginServices.php 16 KB

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