User.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753
  1. <?php
  2. namespace common\modules\user\models;
  3. use abei2017\wx\mini\qrcode\Qrcode;
  4. use backend\models\search\SearchModelTrait;
  5. use common\enums\DataTypeEnum;
  6. use common\enums\StatusEnum;
  7. use common\helpers\GrafikaHelper;
  8. use common\helpers\Regexp;
  9. use common\helpers\wx\Application;
  10. use common\models\Data;
  11. use common\models\DataRank;
  12. use common\models\ShareLevel;
  13. use common\models\Store;
  14. use common\models\UserCar;
  15. use common\models\UserLevel;
  16. use common\models\VipUser;
  17. use common\models\Voice;
  18. use common\modules\attachment\behaviors\UploadBehavior;
  19. use common\modules\attachment\models\Attachment;
  20. use common\modules\rbac\controllers\AssignmentController;
  21. use common\modules\user\Module;
  22. use common\modules\user\traits\ModuleTrait;
  23. use Exception;
  24. use Grafika\Color;
  25. use Grafika\Grafika;
  26. use Yii;
  27. use yii\behaviors\TimestampBehavior;
  28. use yii\db\ActiveRecord;
  29. use yii\helpers\ArrayHelper;
  30. use yii\web\IdentityInterface;
  31. use yii\web\ServerErrorHttpException;
  32. /**
  33. * User model.
  34. *
  35. * @property int $id
  36. * @property int $pid
  37. * @property int $jucai_id
  38. * @property string $username
  39. * @property string $password_hash
  40. * @property string $password_reset_token
  41. * @property string $access_token
  42. * @property int $expired_at
  43. * @property string $email
  44. * @property string $tel
  45. * @property string $auth_key
  46. * @property int $created_at
  47. * @property int $updated_at
  48. * @property int $confirmed_at
  49. * @property int $blocked_at
  50. * @property int $login_at
  51. * @property int $type
  52. * @property string $password write-only password
  53. * @property Profile $profile
  54. * @property string $privilege
  55. * @property string $openid
  56. * @property string $unionid
  57. * @property string $nickname
  58. * @property Module $module
  59. */
  60. class User extends ActiveRecord implements IdentityInterface
  61. {
  62. use ModuleTrait;
  63. use SearchModelTrait;
  64. public $password;
  65. const TYPE_WEB = 0;//后台用户
  66. const TYPE_PERSON = 1;//个人用户
  67. const TYPE_COMPANY = 2;//企业用户
  68. /**
  69. * {@inheritdoc}
  70. */
  71. public static function tableName()
  72. {
  73. return '{{%user}}';
  74. }
  75. /**
  76. * {@inheritdoc}
  77. */
  78. public function behaviors()
  79. {
  80. return [
  81. TimestampBehavior::className(),
  82. ];
  83. }
  84. public function scenarios()
  85. {
  86. $scenarios = parent::scenarios();
  87. return array_merge($scenarios, [
  88. 'register' => ['username', 'openid', 'password', 'email', 'tel', 'pid'],
  89. 'connect' => ['username', 'email'],
  90. 'create' => ['jucai_id', 'email', 'tel'],
  91. 'update' => ['username', 'password', 'email', 'tel', 'nickname', 'pid'],
  92. 'settings' => ['username', 'password', 'email', 'tel'],
  93. 'resetPassword' => ['password']
  94. ]);
  95. }
  96. /**
  97. * {@inheritdoc}
  98. */
  99. public function rules()
  100. {
  101. return [
  102. ['username', 'string', 'on' => 'search'],
  103. ['username', 'required', 'on' => 'create'],
  104. ['password', 'required', 'on' => ['register']],
  105. [['tel'], 'match', 'pattern' => Regexp::$mobile],
  106. [['email'], 'match', 'pattern' => Regexp::$email],
  107. [['privilege'], 'string', 'max' => 255],
  108. ];
  109. }
  110. public function attributeLabels()
  111. {
  112. return [
  113. 'pid' => '上级用户',
  114. 'jucai_id' => '聚才网ID',
  115. 'username' => '用户名',//openid共用字段
  116. 'openid' => '微信ID',
  117. 'nickname' => '昵称',
  118. 'password' => '密码',
  119. 'email' => ' 绑定邮箱',
  120. 'tel' => '绑定手机',
  121. 'avatar' => '头像',
  122. 'created_at' => '注册时间',
  123. 'login_at' => '最后登录时间',
  124. 'type' => '用户类型',
  125. 'privilege' => '特权',//微信用户属性
  126. 'unionid' => '联合ID',//微信用户属性
  127. ];
  128. }
  129. /**
  130. * 根据id查询用户
  131. * {@inheritdoc}
  132. */
  133. public static function findIdentity($id)
  134. {
  135. return static::findOne(['id' => $id, 'blocked_at' => null]);
  136. }
  137. /**
  138. * 根据访问令牌查询用户
  139. * api访问专用
  140. * {@inheritdoc}
  141. */
  142. public static function findIdentityByAccessToken($token, $type = null)
  143. {
  144. return static::find()->where(['access_token' => $token])->andWhere(['>', 'expired_at', time()])->one();
  145. }
  146. /**
  147. * 根据用户名查询用户
  148. *
  149. * @param string $username
  150. *
  151. * @return mixed
  152. */
  153. public static function findByUsername($username)
  154. {
  155. return static::find()->where(['username' => $username])
  156. ->andWhere(['blocked_at' => null])
  157. ->one();
  158. }
  159. /**
  160. * 根据用openid查询用户
  161. *
  162. * @param string $openId
  163. *
  164. * @return mixed
  165. */
  166. public static function findByOpenid($openId)
  167. {
  168. return static::find()->where(['openid' => $openId])
  169. ->andWhere(['blocked_at' => null])
  170. ->one();
  171. }
  172. /**
  173. * 根据绑定邮箱查询用户
  174. * @param $email
  175. * @return array|ActiveRecord|null
  176. * @author nodelog
  177. */
  178. public static function findByEmail($email)
  179. {
  180. return static::find()->where(['email' => $email])
  181. ->andWhere(['blocked_at' => null])
  182. ->one();
  183. }
  184. /**
  185. * 根据绑定手机号查询用户
  186. * @param $tel
  187. * @return array|ActiveRecord|null
  188. * @author nodelog
  189. */
  190. public static function findByTel($tel)
  191. {
  192. return static::find()->where(['email' => $tel])
  193. ->andWhere(['blocked_at' => null])
  194. ->one();
  195. }
  196. /**
  197. * 根据用户名、邮箱、手机号查询用户
  198. * @param $login
  199. * @return array|ActiveRecord|null
  200. * @author nodelog
  201. */
  202. public static function findByUsernameOrEmailTel($login)
  203. {
  204. return static::find()->where(['or', ['username' => $login], ['email' => $login], ['tel' => $login]])
  205. ->andWhere(['blocked_at' => null])
  206. ->one();
  207. }
  208. /**
  209. * 根据重置密码令牌查询用户
  210. * Finds user by password reset token.
  211. *
  212. * @param string $token password reset token
  213. *
  214. * @return static|null
  215. */
  216. public static function findByPasswordResetToken($token)
  217. {
  218. if (!static::isPasswordResetTokenValid($token)) {
  219. return;
  220. }
  221. return static::findOne([
  222. 'password_reset_token' => $token,
  223. 'blocked_at' => null
  224. ]);
  225. }
  226. /**
  227. * Finds out if password reset token is valid.
  228. *
  229. * @param string $token password reset token
  230. *
  231. * @return bool
  232. */
  233. public static function isPasswordResetTokenValid($token)
  234. {
  235. if (empty($token)) {
  236. return false;
  237. }
  238. $timestamp = (int)substr($token, strrpos($token, '_') + 1);
  239. $expire = Yii::$app->params['user.passwordResetTokenExpire'];
  240. return $timestamp + $expire >= time();
  241. }
  242. /**
  243. * 获取当前用户id,认证身份identity使用
  244. * {@inheritdoc}
  245. */
  246. public function getId()
  247. {
  248. return $this->getPrimaryKey();
  249. }
  250. /**
  251. * 获取当前身份验证密钥
  252. * {@inheritdoc}
  253. */
  254. public function getAuthKey()
  255. {
  256. return $this->auth_key;
  257. }
  258. /**
  259. * 验证身份验证密钥
  260. * {@inheritdoc}
  261. */
  262. public function validateAuthKey($authKey)
  263. {
  264. return $this->getAuthKey() === $authKey;
  265. }
  266. /**
  267. * 验证密码
  268. * @param string $password password to validate
  269. *
  270. * @return bool if password provided is valid for current user
  271. */
  272. public function validatePassword($password)
  273. {
  274. return Yii::$app->security->validatePassword($password, $this->password_hash);
  275. }
  276. /**
  277. * 从密码生成密码哈希并将其赋值给当前用户。
  278. * @param $password
  279. * @throws \yii\base\Exception
  280. * @author nodelog
  281. */
  282. public function setPassword($password)
  283. {
  284. $this->password_hash = Yii::$app->security->generatePasswordHash($password);
  285. }
  286. /**
  287. * 验证二维码哈希值
  288. * @param $qrcodeHash
  289. * @return bool
  290. * @author nodelog
  291. */
  292. public function validateQrcodeHash($qrcodeHash)
  293. {
  294. return $qrcodeHash == md5(self::generateQrcodeLoginUrl($this->access_token));
  295. }
  296. /**
  297. * 生成“记住我”身份验证密钥。
  298. */
  299. public function generateAuthKey()
  300. {
  301. $this->auth_key = Yii::$app->security->generateRandomString();
  302. }
  303. /**
  304. * 生成新的密码重置令牌。
  305. */
  306. public function generatePasswordResetToken()
  307. {
  308. $this->password_reset_token = Yii::$app->security->generateRandomString() . '_' . time();
  309. }
  310. /**
  311. * 删除密码重置令牌。
  312. */
  313. public function removePasswordResetToken()
  314. {
  315. $this->password_reset_token = null;
  316. }
  317. /**
  318. * 生成访问令牌和过期时间
  319. * @param null $access_token
  320. * @throws \yii\base\Exception
  321. * @author nodelog
  322. */
  323. public function generateAccessToken($access_token = null)
  324. {
  325. if (!$access_token) {
  326. // $access_token = Yii::$app->security->generateRandomString();
  327. }
  328. $this->access_token = $access_token;
  329. $this->expired_at = time() + 60 * 60 * 2;//2小时
  330. }
  331. /**
  332. * 删除访问令牌和过期时间
  333. * @author nodelog
  334. */
  335. public function removeAccessToken()
  336. {
  337. $this->access_token = null;
  338. $this->expired_at = null;
  339. }
  340. /**
  341. * 创建用户
  342. * @param int $type
  343. * @return bool
  344. * @author nodelog
  345. */
  346. public function create()
  347. {
  348. if ($this->getIsNewRecord() == false) {
  349. throw new \RuntimeException('Calling "' . __CLASS__ . '::' . __METHOD__ . '" on existing user');
  350. }
  351. $this->confirmed_at = time();
  352. $this->password = $this->password == null ? $this->getModule()->defaultPassword : $this->password;
  353. $this->generateAuthKey();
  354. if (!$this->save()) {
  355. return false;
  356. }
  357. return true;
  358. }
  359. /**
  360. * 创建微信用户 并授权公司管理员角色
  361. * @throws \Exception
  362. * @author nodelog
  363. */
  364. public function createWechat()
  365. {
  366. $this->type = self::TYPE_PERSON;
  367. if ($this->create()) {
  368. // try {
  369. // $auth = \Yii::$app->authManager;
  370. // //todo 具体授权
  371. // $authorRole = $auth->getRole('superAdmin');
  372. // $auth->assign($authorRole, $this->id);
  373. // } catch (Exception $e) {
  374. // $this->addError('username', $e->getMessage());
  375. // return false;
  376. // }
  377. //创建个人数据设置
  378. return true;
  379. }
  380. return false;
  381. }
  382. /**
  383. * 保存之前生成密码哈希
  384. * @param bool $insert
  385. * @return bool
  386. * @throws \yii\base\Exception
  387. * @author nodelog
  388. */
  389. public function beforeSave($insert)
  390. {
  391. if (!empty($this->password)) {
  392. $this->password_hash = Yii::$app->security->generatePasswordHash($this->password);
  393. }
  394. return parent::beforeSave($insert);
  395. }
  396. /**
  397. * 禁用用户
  398. * @return bool
  399. * @throws \yii\base\Exception
  400. * @author nodelog
  401. */
  402. public function block()
  403. {
  404. return (bool)$this->updateAttributes([
  405. 'blocked_at' => time(),
  406. 'auth_key' => \Yii::$app->security->generateRandomString(),
  407. ]);
  408. }
  409. /**
  410. * 解除禁用
  411. * UnBlocks the user by setting 'blocked_at' field to null.
  412. */
  413. public function unblock()
  414. {
  415. return (bool)$this->updateAttributes(['blocked_at' => null]);
  416. }
  417. /**
  418. * 发送绑定邮箱确认邮件
  419. * @param $email
  420. * @return array
  421. * @throws \yii\base\InvalidConfigException
  422. * @author nodelog
  423. */
  424. public function sendConfirm($email)
  425. {
  426. $exist = User::find()->where(['<>', 'id', Yii::$app->user->id])->andWhere(['email' => $email])->exists();
  427. $user = Yii::$app->user->identity;
  428. if (!$exist && !$user->isConfirmed) {
  429. /** @var Token $token */
  430. $token = \Yii::createObject([
  431. 'class' => Token::className(),
  432. 'user_id' => $user->id,
  433. 'type' => Token::TYPE_CONFIRMATION,
  434. ]);
  435. $token->save(false);
  436. $mailer = Yii::$app->mailer;
  437. $mailer->viewPath = '@common/modules/user/mail';
  438. try {
  439. $mailer->compose(['html' => 'confirmation'], ['user' => $user, 'token' => $token])
  440. ->setTo($email)
  441. ->setSubject(Yii::t('user', 'Confirm account on {0}', Yii::$app->config->get('name')))
  442. ->send();
  443. return [true, null];
  444. } catch (\Exception $e) {
  445. return [false, '发送失败,请确认邮箱是否存在!'];
  446. }
  447. } else {
  448. return [false, '邮箱已存在'];
  449. }
  450. }
  451. /**
  452. * 用户邮箱验证
  453. * @param $code
  454. * @return array
  455. * @throws \yii\db\StaleObjectException
  456. * @author nodelog
  457. */
  458. public function attemptConfirmation($code)
  459. {
  460. if ($this->getIsConfirmed()) {
  461. return [false, '该用户已验证邮箱'];
  462. }
  463. $token = Token::find()->where(['user_id' => $this->id, 'code' => $code, 'type' => Token::TYPE_CONFIRMATION])->one();
  464. if ($token !== null && !$token->isExpired) {
  465. $token->delete();
  466. if (($success = $this->confirm())) {
  467. $message = \Yii::t('user', 'Thank you, registration is now complete.');
  468. } else {
  469. $message = \Yii::t('user', 'Something went wrong and your account has not been confirmed.');
  470. }
  471. } else {
  472. $success = false;
  473. $message = \Yii::t('user', 'The confirmation link is invalid or expired. Please try requesting a new one.');
  474. }
  475. return [$success, $message];
  476. }
  477. /**
  478. * Confirms the user by setting 'confirmed_at' field to current time.
  479. */
  480. public function confirm()
  481. {
  482. $result = (bool)$this->updateAttributes(['confirmed_at' => time()]);
  483. return $result;
  484. }
  485. /**
  486. * 获取用户头像
  487. * @param int $width
  488. * @param int $height
  489. * @return string
  490. * @author nodelog
  491. */
  492. public function getAvatar($width = 96, $height = 0)
  493. {
  494. if (empty($height)) {
  495. $height = $width;
  496. }
  497. if ($this->profile->avatar) {
  498. return Yii::$app->storage->thumbnail($this->profile->avatar, $width, $height);
  499. }
  500. return $this->getDefaultAvatar($width, $height);
  501. }
  502. /**
  503. * 获取用户信息
  504. * @return \yii\db\ActiveQuery
  505. * @author nodelog
  506. */
  507. public function getProfile()
  508. {
  509. return $this->hasOne(Profile::className(), ['user_id' => 'id']);
  510. }
  511. /**
  512. * 认证车辆
  513. * @return \yii\db\ActiveQuery
  514. * @author nodelog
  515. */
  516. public function getCar()
  517. {
  518. return $this->hasOne(UserCar::className(), ['user_id' => 'id']);
  519. }
  520. /**
  521. * 获取用户签名
  522. * @return string
  523. * @author nodelog
  524. */
  525. public function getSignature()
  526. {
  527. return $this->profile->signature;
  528. }
  529. /**
  530. * 获取默认头像
  531. * @param $width
  532. * @param $height
  533. * @return string
  534. * @author nodelog
  535. */
  536. public static function getDefaultAvatar($width, $height)
  537. {
  538. list ($basePath, $baseUrl) = \Yii::$app->getAssetManager()->publish("@common/static");
  539. $name = "avatars/avatar_" . $width . "x" . $height . ".png";
  540. if (file_exists($basePath . DIRECTORY_SEPARATOR . $name)) {
  541. return $baseUrl . "/" . $name;
  542. }
  543. return $baseUrl . "/" . "avatar_200x200.png";
  544. }
  545. /**
  546. * 保存头像
  547. * @param $avatar
  548. * @author nodelog
  549. */
  550. public function saveAvatar($avatar)
  551. {
  552. $this->profile->updateAttributes(['avatar' => $avatar]);
  553. }
  554. /**
  555. * 初始化插入数据库后置事件
  556. * @author nodelog
  557. */
  558. public function init()
  559. {
  560. $this->on(self::EVENT_AFTER_INSERT, [$this, 'afterInsertInternal']);
  561. }
  562. /**
  563. * 设置用户信息的外键关联
  564. * @param $event
  565. * @author nodelog
  566. */
  567. public function afterInsertInternal($event)
  568. {
  569. $profile = new Profile();
  570. // $profile->voice_id = Voice::getDefaultId();
  571. $this->link('profile', $profile);
  572. //创建个人配置
  573. }
  574. /**
  575. * 是否管理员用户
  576. * @return bool
  577. */
  578. public function getIsAdmin()
  579. {
  580. return
  581. (\Yii::$app->getAuthManager() && $this->module->adminPermission ?
  582. Yii::$app->getAuthManager()->checkAccess($this->getId(), $this->module->adminPermission) : false)
  583. || in_array($this->username, $this->module->admins);
  584. }
  585. /**
  586. * 是否确认
  587. * @return bool Whether the user is confirmed or not.
  588. */
  589. public function getIsConfirmed()
  590. {
  591. return $this->confirmed_at != null;
  592. }
  593. /**
  594. * 是否禁用
  595. * @return bool Whether the user is blocked or not.
  596. */
  597. public function getIsBlocked()
  598. {
  599. return $this->blocked_at != null;
  600. }
  601. /**
  602. * 获取等级
  603. * @return string
  604. */
  605. public function getLevel()
  606. {
  607. return UserLevel::getLevel($this->profile->money);
  608. }
  609. /**
  610. * 生成二维码登录url
  611. * @param $access_token
  612. * @return string
  613. * @author nodelog
  614. */
  615. public static function generateQrcodeLoginUrl($access_token)
  616. {
  617. return Yii::$app->request->hostInfo . '/api/v1/weixin/code?access_token=' . $access_token;
  618. }
  619. public static $typeList = [
  620. self::TYPE_COMPANY => '企业用户',
  621. self::TYPE_PERSON => '个人用户',
  622. self::TYPE_WEB => '后台用户'
  623. ];
  624. public static $typeIconList = [
  625. self::TYPE_PERSON => '<i class="fa fa-fw fa-wechat"></i>',
  626. self::TYPE_WEB => '<i class="fa fa-fw fa-desktop"></i>'
  627. ];
  628. /**
  629. * 格式化昵称
  630. * @return string
  631. * @author nodelog
  632. */
  633. private function formatNickname()
  634. {
  635. // $name = $this->profile->nickname ?: $this->username;
  636. // if ($htmlFormat) {
  637. // return Html::tag('span', $name . ' ' . self::$typeIconList[$this->type], ['title' => self::$typeList[$this->type]]);
  638. // }
  639. return $this->nickname ? $this->nickname : $this->username;
  640. }
  641. /**
  642. * 获得用户所有角色描述
  643. * @return string
  644. * @author nodelog
  645. */
  646. public function getAllRoleDesc()
  647. {
  648. $roles = Yii::$app->authManager->getRolesByUser($this->id);
  649. return implode(' | ', ArrayHelper::getColumn($roles, 'description'));
  650. }
  651. /**
  652. * 查询 左连接 去除公共部分,如教练查询未绑定教练微信用户,销售查询未绑定销售微信用户,FOR 后台添加教练和销售 筛选选择列表数据
  653. * @param $model 模型类名
  654. * @param $inIdArray 过滤的id数组
  655. * @return array|User[]
  656. */
  657. public static function getAllSelectFromModel($model, $inIdArray = null)
  658. {
  659. if (empty($inIdArray)) {
  660. $list = User::find()->joinWith($model::relationName())->where([self::tableName() . '.blocked_at' => null, $model::tableName() . '.user_id' => null])->all();
  661. } else {
  662. $list = User::find()->joinWith($model::relationName())->where([self::tableName() . '.blocked_at' => null])->andWhere(['or', [$model::tableName() . '.user_id' => null], [$model::tableName() . '.user_id' => $inIdArray]])->all();
  663. }
  664. return $list;
  665. }
  666. /**
  667. * 获取可选管理员用户列表
  668. * @author nodelog
  669. */
  670. public static function lists()
  671. {
  672. if (Yii::$app->user->identity->getIsAdmin()) {
  673. $lists = User::find()->indexBy('id')->all();
  674. $lists = ArrayHelper::map($lists, 'id', 'nickname');
  675. return $lists;
  676. } else {
  677. return [
  678. Yii::$app->user->id => Yii::$app->user->identity->nickname
  679. ];
  680. }
  681. }
  682. }