控制台应用,yzncms本身基于tp5.1框架,里面的队列用不了,bug,坑
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

User.php 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | Yzncms [ 御宅男工作室 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2018 http://yzncms.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: 御宅男 <530765310@qq.com>
  10. // +----------------------------------------------------------------------
  11. // +----------------------------------------------------------------------
  12. // | 前台用户服务
  13. // +----------------------------------------------------------------------
  14. namespace app\member\service;
  15. use app\member\library\Token;
  16. use app\member\model\Member as Member_Model;
  17. use think\Db;
  18. use think\facade\Hook;
  19. use think\facade\Validate;
  20. use util\Random;
  21. class User
  22. {
  23. protected static $instance = null;
  24. protected $_error = null;
  25. protected $_logined = false;
  26. //当前登录会员详细信息
  27. protected $_user = null;
  28. protected $_token = '';
  29. //Token默认有效时长
  30. protected $keeptime = 2592000;
  31. //默认配置
  32. protected $config = [];
  33. protected $options = [];
  34. protected $allowFields = ['id', 'username', 'nickname', 'mobile', 'avatar', 'point', 'amount'];
  35. public function __construct($options = [])
  36. {
  37. if ($config = get_addon_config("member")) {
  38. $this->config = array_merge($this->config, $config);
  39. }
  40. $this->options = array_merge($this->config, $options);
  41. }
  42. /**
  43. * 获取示例
  44. * @param array $options 实例配置
  45. * @return static
  46. */
  47. public static function instance($options = [])
  48. {
  49. if (is_null(self::$instance)) {
  50. self::$instance = new self($options);
  51. }
  52. return self::$instance;
  53. }
  54. /**
  55. * 获取User模型
  56. * @return User
  57. */
  58. public function getUser()
  59. {
  60. return $this->_user;
  61. }
  62. /**
  63. * 兼容调用user模型的属性
  64. *
  65. * @param string $name
  66. * @return mixed
  67. */
  68. public function __get($name)
  69. {
  70. return $this->_user ? $this->_user->$name : null;
  71. }
  72. /**
  73. * 兼容调用user模型的属性
  74. */
  75. public function __isset($name)
  76. {
  77. return isset($this->_user) ? isset($this->_user->$name) : false;
  78. }
  79. /**
  80. * 根据Token初始化
  81. *
  82. * @param string $token Token
  83. * @return boolean
  84. */
  85. public function init($token)
  86. {
  87. if ($this->_logined) {
  88. return true;
  89. }
  90. if ($this->_error) {
  91. return false;
  92. }
  93. $data = Token::get($token);
  94. if (!$data) {
  95. return false;
  96. }
  97. $user_id = intval($data['user_id']);
  98. if ($user_id > 0) {
  99. $user = Member_Model::get($user_id);
  100. if (!$user) {
  101. $this->setError('账户不存在');
  102. return false;
  103. }
  104. if ($user['status'] !== 1) {
  105. $this->setError('账户已经被锁定');
  106. return false;
  107. }
  108. $this->_user = $user;
  109. $this->_logined = true;
  110. $this->_token = $token;
  111. //初始化成功的事件
  112. Hook::listen("user_init_successed", $this->_user);
  113. return true;
  114. } else {
  115. $this->setError('你当前还未登录');
  116. return false;
  117. }
  118. }
  119. /**
  120. * 注册一个新用户
  121. * @param $username 用户名
  122. * @param $password 密码
  123. * @param string $email 邮箱
  124. * @param string $mobile 手机号
  125. * @param array $extend 扩展参数
  126. * @return bool|mixed
  127. */
  128. public function userRegister($username, $password, $email = '', $mobile = '', $extend = [])
  129. {
  130. $passwordinfo = encrypt_password($password); //对密码进行处理
  131. $data = [
  132. "mobile" => $mobile,
  133. "username" => $username,
  134. "email" => $email,
  135. "encrypt" => $passwordinfo['encrypt'],
  136. "amount" => 0,
  137. ];
  138. $nickname = $extend['nickname'] ?? $username;
  139. $extend['nickname'] = preg_match("/^1[3-9]\d{9}$/", $nickname) ? substr_replace($nickname, '****', 3, 4) : $nickname;
  140. //新注册用户积分
  141. $data['point'] = $this->config['defualtpoint'] ? $this->config['defualtpoint'] : 0;
  142. //新会员注册默认赠送资金
  143. $data['amount'] = $this->config['defualtamount'] ? $this->config['defualtamount'] : 0;
  144. //新会员注册需要管理员审核
  145. $data['status'] = $this->config['registerverify'] ? 0 : 1;
  146. //计算用户组
  147. $data['groupid'] = $this->get_usergroup_bypoint($data['point']);
  148. $params = array_merge($data, $extend);
  149. $params['password'] = $passwordinfo['password'];
  150. //账号注册时需要开启事务,避免出现垃圾数据
  151. Db::startTrans();
  152. try {
  153. $model = new Member_Model();
  154. $user = $model->allowField(true)->save($params);
  155. $this->_user = $model->get($model->id);
  156. //设置Token
  157. $this->_token = Random::uuid();
  158. Token::set($this->_token, $model->id, $this->keeptime);
  159. //设置登录状态
  160. $this->_logined = true;
  161. //注册成功的事件
  162. Hook::listen("user_register_successed", $this->_user, $data);
  163. Db::commit();
  164. } catch (\Exception $e) {
  165. Db::rollback();
  166. $this->setError($e->getMessage());
  167. return false;
  168. }
  169. return $model->id;
  170. }
  171. /**
  172. * 会员登录
  173. * @param $account 账户
  174. * @param $password 明文密码,填写表示验证密码
  175. * @return boolean
  176. */
  177. public function loginLocal($account, $password = null)
  178. {
  179. $field = Validate::is($account, 'email') ? 'email' : (Validate::regex($account, '/^1\d{10}$/') ? 'mobile' : 'username');
  180. $user = Member_Model::get([$field => $account]);
  181. if (!$user) {
  182. $this->setError('账户不正确');
  183. return false;
  184. }
  185. if ($user->status !== 1) {
  186. $this->setError('账户已经被锁定');
  187. return false;
  188. }
  189. if ($user->password != encrypt_password($password, $user->encrypt)) {
  190. $this->setError('密码不正确');
  191. return false;
  192. }
  193. //直接登录会员
  194. $this->direct($user->id);
  195. return true;
  196. }
  197. /**
  198. * 删除一个指定会员
  199. * @param int $user_id 会员ID
  200. * @return boolean
  201. */
  202. public function delete($user_id)
  203. {
  204. $user = Member_Model::get($user_id);
  205. if (!$user) {
  206. return false;
  207. }
  208. Db::startTrans();
  209. try {
  210. // 删除会员
  211. Member_Model::destroy($user_id);
  212. // 删除会员指定的所有Token
  213. Token::clear($user_id);
  214. Hook::listen("user_delete_successed", $user);
  215. Db::commit();
  216. } catch (Exception $e) {
  217. Db::rollback();
  218. $this->setError($e->getMessage());
  219. return false;
  220. }
  221. return true;
  222. }
  223. /**
  224. * 直接登录账号
  225. * @param int $user_id
  226. * @return boolean
  227. */
  228. public function direct($user_id)
  229. {
  230. $user = Member_Model::get($user_id);
  231. if ($user) {
  232. Db::startTrans();
  233. try {
  234. $ip = request()->ip();
  235. $time = time();
  236. //vip过期,更新vip和会员组
  237. if ($user->overduedate < $time && intval($user->vip)) {
  238. $user->vip = 0;
  239. }
  240. //检查用户积分,更新新用户组,除去邮箱认证、禁止访问、游客组用户、vip用户,如果该用户组不允许自助升级则不进行该操作
  241. if ($user->point >= 0 && !in_array($user->groupid, ['1', '7', '8']) && empty($user->vip)) {
  242. $grouplist = cache("Member_Group");
  243. if (!empty($grouplist[$user->groupid]['allowupgrade'])) {
  244. $check_groupid = $this->get_usergroup_bypoint($user->point);
  245. if ($check_groupid != $user->groupid) {
  246. $user->groupid = $check_groupid;
  247. }
  248. }
  249. }
  250. //记录本次登录的IP和时间
  251. $user->last_login_ip = $ip;
  252. $user->last_login_time = $time;
  253. $user->login = $user->login + 1;
  254. $user->save();
  255. $this->_user = $user;
  256. $this->_token = Random::uuid();
  257. Token::set($this->_token, $user->id, $this->keeptime);
  258. $this->_logined = true;
  259. //登录成功的事件
  260. Hook::listen("user_login_successed", $this->_user);
  261. Db::commit();
  262. } catch (Exception $e) {
  263. Db::rollback();
  264. $this->setError($e->getMessage());
  265. return false;
  266. }
  267. return true;
  268. } else {
  269. return false;
  270. }
  271. }
  272. /**
  273. * 修改密码
  274. * @param string $newpassword 新密码
  275. * @param string $oldpassword 旧密码
  276. * @param bool $ignoreoldpassword 忽略旧密码
  277. * @return boolean
  278. */
  279. public function changepwd($newpassword, $oldpassword = '', $ignoreoldpassword = false)
  280. {
  281. if (!$this->_logined) {
  282. $this->setError('你当前还未登录');
  283. return false;
  284. }
  285. //判断旧密码是否正确
  286. if ($this->_user->password == encrypt_password($oldpassword, $this->_user->encrypt) || $ignoreoldpassword) {
  287. Db::startTrans();
  288. try {
  289. $encrypt = Random::alnum();
  290. $newpassword = encrypt_password($newpassword, $encrypt);
  291. $this->_user->save(['password' => $newpassword, 'encrypt' => $encrypt]);
  292. Token::delete($this->_token);
  293. //修改密码成功的事件
  294. Hook::listen("user_changepwd_successed", $this->_user);
  295. Db::commit();
  296. } catch (Exception $e) {
  297. Db::rollback();
  298. $this->setError($e->getMessage());
  299. return false;
  300. }
  301. return true;
  302. } else {
  303. $this->setError('密码不正确');
  304. return false;
  305. }
  306. }
  307. /**
  308. * 获取会员基本信息
  309. */
  310. public function getUserinfo()
  311. {
  312. $data = $this->_user->toArray();
  313. $allowFields = $this->getAllowFields();
  314. $userinfo = array_intersect_key($data, array_flip($allowFields));
  315. $userinfo = array_merge($userinfo, Token::get($this->_token));
  316. return $userinfo;
  317. }
  318. /**
  319. * 根据积分算出用户组
  320. * @param int $point 积分数
  321. * @return int|string|null
  322. */
  323. public function get_usergroup_bypoint($point = 0)
  324. {
  325. $groupid = 2;
  326. if (empty($point)) {
  327. //新会员默认点数
  328. $point = $this->config['defualtpoint'] ? $this->config['defualtpoint'] : 0;
  329. }
  330. //获取会有组缓存
  331. $grouplist = cache("Member_Group");
  332. foreach ($grouplist as $k => $v) {
  333. $grouppointlist[$k] = $v['point'];
  334. }
  335. //对数组进行逆向排序
  336. arsort($grouppointlist);
  337. //如果超出用户组积分设置则为积分最高的用户组
  338. if ($point > max($grouppointlist)) {
  339. $groupid = key($grouppointlist);
  340. } else {
  341. $tmp_k = key($grouppointlist);
  342. foreach ($grouppointlist as $k => $v) {
  343. if ($point >= $v) {
  344. $groupid = $tmp_k;
  345. break;
  346. }
  347. $tmp_k = $k;
  348. }
  349. }
  350. return $groupid;
  351. }
  352. /**
  353. * 检验用户是否已经登陆
  354. */
  355. public function isLogin()
  356. {
  357. if ($this->_logined) {
  358. return true;
  359. }
  360. return false;
  361. }
  362. /**
  363. * 获取允许输出的字段
  364. * @return array
  365. */
  366. public function getAllowFields()
  367. {
  368. return $this->allowFields;
  369. }
  370. /**
  371. * 设置允许输出的字段
  372. * @param array $fields
  373. */
  374. public function setAllowFields($fields)
  375. {
  376. $this->allowFields = $fields;
  377. }
  378. /**
  379. * 获取当前Token
  380. * @return string
  381. */
  382. public function getToken()
  383. {
  384. return $this->_token;
  385. }
  386. /**
  387. * 设置会话有效时间
  388. * @param int $keeptime 默认为永久
  389. */
  390. public function keeptime($keeptime = 0)
  391. {
  392. $this->keeptime = $keeptime;
  393. }
  394. /**
  395. * 注销登录状态
  396. * @return boolean
  397. */
  398. public function logout()
  399. {
  400. if (!$this->_logined) {
  401. $this->setError('你当前还未登录');
  402. return false;
  403. }
  404. //设置登录标识
  405. $this->_logined = false;
  406. //删除Token
  407. Token::delete($this->_token);
  408. //退出成功的事件
  409. Hook::listen("user_logout_successed", $this->_user);
  410. return true;
  411. }
  412. /**
  413. * 检测当前控制器和方法是否匹配传递的数组
  414. *
  415. * @param array $arr 需要验证权限的数组
  416. * @return boolean
  417. */
  418. public function match($arr = [])
  419. {
  420. $arr = is_array($arr) ? $arr : explode(',', $arr);
  421. if (!$arr) {
  422. return false;
  423. }
  424. $arr = array_map('strtolower', $arr);
  425. // 是否存在
  426. if (in_array(strtolower(request()->action()), $arr) || in_array('*', $arr)) {
  427. return true;
  428. }
  429. // 没找到匹配
  430. return false;
  431. }
  432. public function getConfig()
  433. {
  434. return $this->config;
  435. }
  436. /**
  437. * 设置错误信息
  438. *
  439. * @param $error 错误信息
  440. * @return Auth
  441. */
  442. public function setError($error)
  443. {
  444. $this->_error = $error;
  445. return $this;
  446. }
  447. /**
  448. * 获取错误信息
  449. * @return type
  450. */
  451. public function getError()
  452. {
  453. return $this->_error;
  454. }
  455. }