UploadBehavior::className(), 'attribute' => 'qrcode', 'entity' => __CLASS__ ], ]; } public function scenarios() { $scenarios = parent::scenarios(); return array_merge($scenarios, [ 'register' => ['username', 'openid', 'password', 'email', 'tel', 'pid'], 'connect' => ['username', 'email'], 'create' => ['jucai_id', 'email', 'tel'], 'update' => ['username', 'password', 'email', 'tel', 'nickname', 'level_id', 'pid'], 'settings' => ['username', 'password', 'email', 'tel'], 'resetPassword' => ['password'] ]); } /** * {@inheritdoc} */ public function rules() { return [ ['username', 'string', 'on' => 'search'], ['username', 'required', 'on' => 'create'], ['password', 'required', 'on' => ['register']], [['tel'], 'match', 'pattern' => Regexp::$mobile], [['email'], 'match', 'pattern' => Regexp::$email], [['privilege'], 'string', 'max' => 255], [['qrcode', 'dataCount'], 'safe'], ]; } public function attributeLabels() { return [ 'pid' => '上级用户', 'jucai_id' => '聚才网ID', 'username' => '用户名',//openid共用字段 'openid' => '微信ID', 'nickname' => '昵称', 'password' => '密码', 'email' => ' 绑定邮箱', 'tel' => '绑定手机', 'avatar' => '头像', 'created_at' => '注册时间', 'login_at' => '最后登录时间', 'type' => '用户类型', 'privilege' => '特权',//微信用户属性 'unionid' => '联合ID',//微信用户属性 'level_id' => '分销等级',//微信用户属性 'qrcode' => '分销码', //小程序码 'is_vip' => '是否vip', //是否vip 'vip_time' => 'vip有效期', //是否vip 'dataCount' => '数据统计', ]; } /** * 根据id查询用户 * {@inheritdoc} */ public static function findIdentity($id) { return static::findOne(['id' => $id, 'blocked_at' => null]); } /** * 根据访问令牌查询用户 * api访问专用 * {@inheritdoc} */ public static function findIdentityByAccessToken($token, $type = null) { return static::find()->where(['access_token' => $token])->andWhere(['>', 'expired_at', time()])->one(); } /** * 根据用户名查询用户 * * @param string $username * * @return mixed */ public static function findByUsername($username) { return static::find()->where(['username' => $username]) ->andWhere(['blocked_at' => null]) ->one(); } /** * 根据用openid查询用户 * * @param string $openId * * @return mixed */ public static function findByOpenid($openId) { return static::find()->where(['openid' => $openId]) ->andWhere(['blocked_at' => null]) ->one(); } /** * 根据绑定邮箱查询用户 * @param $email * @return array|ActiveRecord|null * @author nodelog */ public static function findByEmail($email) { return static::find()->where(['email' => $email]) ->andWhere(['blocked_at' => null]) ->one(); } /** * 根据绑定手机号查询用户 * @param $tel * @return array|ActiveRecord|null * @author nodelog */ public static function findByTel($tel) { return static::find()->where(['email' => $tel]) ->andWhere(['blocked_at' => null]) ->one(); } /** * 根据用户名、邮箱、手机号查询用户 * @param $login * @return array|ActiveRecord|null * @author nodelog */ public static function findByUsernameOrEmailTel($login) { return static::find()->where(['or', ['username' => $login], ['email' => $login], ['tel' => $login]]) ->andWhere(['blocked_at' => null]) ->one(); } /** * 根据重置密码令牌查询用户 * Finds user by password reset token. * * @param string $token password reset token * * @return static|null */ public static function findByPasswordResetToken($token) { if (!static::isPasswordResetTokenValid($token)) { return; } return static::findOne([ 'password_reset_token' => $token, 'blocked_at' => null ]); } /** * Finds out if password reset token is valid. * * @param string $token password reset token * * @return bool */ public static function isPasswordResetTokenValid($token) { if (empty($token)) { return false; } $timestamp = (int)substr($token, strrpos($token, '_') + 1); $expire = Yii::$app->params['user.passwordResetTokenExpire']; return $timestamp + $expire >= time(); } /** * 获取当前用户id,认证身份identity使用 * {@inheritdoc} */ public function getId() { return $this->getPrimaryKey(); } /** * 获取当前身份验证密钥 * {@inheritdoc} */ public function getAuthKey() { return $this->auth_key; } /** * 验证身份验证密钥 * {@inheritdoc} */ public function validateAuthKey($authKey) { return $this->getAuthKey() === $authKey; } /** * 验证密码 * @param string $password password to validate * * @return bool if password provided is valid for current user */ public function validatePassword($password) { return Yii::$app->security->validatePassword($password, $this->password_hash); } /** * 从密码生成密码哈希并将其赋值给当前用户。 * @param $password * @throws \yii\base\Exception * @author nodelog */ public function setPassword($password) { $this->password_hash = Yii::$app->security->generatePasswordHash($password); } /** * 验证二维码哈希值 * @param $qrcodeHash * @return bool * @author nodelog */ public function validateQrcodeHash($qrcodeHash) { return $qrcodeHash == md5(self::generateQrcodeLoginUrl($this->access_token)); } /** * 生成“记住我”身份验证密钥。 */ public function generateAuthKey() { $this->auth_key = Yii::$app->security->generateRandomString(); } /** * 生成新的密码重置令牌。 */ public function generatePasswordResetToken() { $this->password_reset_token = Yii::$app->security->generateRandomString() . '_' . time(); } /** * 删除密码重置令牌。 */ public function removePasswordResetToken() { $this->password_reset_token = null; } /** * 生成访问令牌和过期时间 * @param null $access_token * @throws \yii\base\Exception * @author nodelog */ public function generateAccessToken($access_token = null) { if (!$access_token) { // $access_token = Yii::$app->security->generateRandomString(); } $this->access_token = $access_token; $this->expired_at = time() + 60 * 60 * 2;//2小时 } /** * 删除访问令牌和过期时间 * @author nodelog */ public function removeAccessToken() { $this->access_token = null; $this->expired_at = null; } /** * 创建用户 * @param int $type * @return bool * @author nodelog */ public function create() { if ($this->getIsNewRecord() == false) { throw new \RuntimeException('Calling "' . __CLASS__ . '::' . __METHOD__ . '" on existing user'); } $this->confirmed_at = time(); $this->password = $this->password == null ? $this->getModule()->defaultPassword : $this->password; $this->generateAuthKey(); if (!$this->save()) { return false; } return true; } /** * 创建微信用户 并授权公司管理员角色 * @throws \Exception * @author nodelog */ public function createWechat() { $this->type = self::TYPE_PERSON; if ($this->create()) { // try { // $auth = \Yii::$app->authManager; // //todo 具体授权 // $authorRole = $auth->getRole('superAdmin'); // $auth->assign($authorRole, $this->id); // } catch (Exception $e) { // $this->addError('username', $e->getMessage()); // return false; // } //创建个人数据设置 return true; } return false; } /** * 保存之前生成密码哈希 * @param bool $insert * @return bool * @throws \yii\base\Exception * @author nodelog */ public function beforeSave($insert) { if (!empty($this->password)) { $this->password_hash = Yii::$app->security->generatePasswordHash($this->password); } return parent::beforeSave($insert); } /** * 禁用用户 * @return bool * @throws \yii\base\Exception * @author nodelog */ public function block() { return (bool)$this->updateAttributes([ 'blocked_at' => time(), 'auth_key' => \Yii::$app->security->generateRandomString(), ]); } /** * 解除禁用 * UnBlocks the user by setting 'blocked_at' field to null. */ public function unblock() { return (bool)$this->updateAttributes(['blocked_at' => null]); } /** * 发送绑定邮箱确认邮件 * @param $email * @return array * @throws \yii\base\InvalidConfigException * @author nodelog */ public function sendConfirm($email) { $exist = User::find()->where(['<>', 'id', Yii::$app->user->id])->andWhere(['email' => $email])->exists(); $user = Yii::$app->user->identity; if (!$exist && !$user->isConfirmed) { /** @var Token $token */ $token = \Yii::createObject([ 'class' => Token::className(), 'user_id' => $user->id, 'type' => Token::TYPE_CONFIRMATION, ]); $token->save(false); $mailer = Yii::$app->mailer; $mailer->viewPath = '@common/modules/user/mail'; try { $mailer->compose(['html' => 'confirmation'], ['user' => $user, 'token' => $token]) ->setTo($email) ->setSubject(Yii::t('user', 'Confirm account on {0}', Yii::$app->config->get('name'))) ->send(); return [true, null]; } catch (\Exception $e) { return [false, '发送失败,请确认邮箱是否存在!']; } } else { return [false, '邮箱已存在']; } } /** * 用户邮箱验证 * @param $code * @return array * @throws \yii\db\StaleObjectException * @author nodelog */ public function attemptConfirmation($code) { if ($this->getIsConfirmed()) { return [false, '该用户已验证邮箱']; } $token = Token::find()->where(['user_id' => $this->id, 'code' => $code, 'type' => Token::TYPE_CONFIRMATION])->one(); if ($token !== null && !$token->isExpired) { $token->delete(); if (($success = $this->confirm())) { $message = \Yii::t('user', 'Thank you, registration is now complete.'); } else { $message = \Yii::t('user', 'Something went wrong and your account has not been confirmed.'); } } else { $success = false; $message = \Yii::t('user', 'The confirmation link is invalid or expired. Please try requesting a new one.'); } return [$success, $message]; } /** * Confirms the user by setting 'confirmed_at' field to current time. */ public function confirm() { $result = (bool)$this->updateAttributes(['confirmed_at' => time()]); return $result; } /** * 获取用户头像 * @param int $width * @param int $height * @return string * @author nodelog */ public function getAvatar($width = 96, $height = 0) { if (empty($height)) { $height = $width; } if ($this->profile->avatar) { return Yii::$app->storage->thumbnail($this->profile->avatar, $width, $height); } return $this->getDefaultAvatar($width, $height); } /** * 获取用户信息 * @return \yii\db\ActiveQuery * @author nodelog */ public function getProfile() { return $this->hasOne(Profile::className(), ['user_id' => 'id']); } /** * 认证车辆 * @return \yii\db\ActiveQuery * @author nodelog */ public function getCar() { return $this->hasOne(UserCar::className(), ['user_id' => 'id']); } /** * 获取用户签名 * @return string * @author nodelog */ public function getSignature() { return $this->profile->signature; } /** * 获取默认头像 * @param $width * @param $height * @return string * @author nodelog */ public static function getDefaultAvatar($width, $height) { list ($basePath, $baseUrl) = \Yii::$app->getAssetManager()->publish("@common/static"); $name = "avatars/avatar_" . $width . "x" . $height . ".png"; if (file_exists($basePath . DIRECTORY_SEPARATOR . $name)) { return $baseUrl . "/" . $name; } return $baseUrl . "/" . "avatar_200x200.png"; } /** * 保存头像 * @param $avatar * @author nodelog */ public function saveAvatar($avatar) { $this->profile->updateAttributes(['avatar' => $avatar]); } /** * 初始化插入数据库后置事件 * @author nodelog */ public function init() { $this->on(self::EVENT_AFTER_INSERT, [$this, 'afterInsertInternal']); } /** * 设置用户信息的外键关联 * @param $event * @author nodelog */ public function afterInsertInternal($event) { $profile = new Profile(); // $profile->voice_id = Voice::getDefaultId(); $this->link('profile', $profile); //创建个人配置 } /** * 是否管理员用户 * @return bool */ public function getIsAdmin() { return (\Yii::$app->getAuthManager() && $this->module->adminPermission ? Yii::$app->getAuthManager()->checkAccess($this->getId(), $this->module->adminPermission) : false) || in_array($this->username, $this->module->admins); } /** * 是否确认 * @return bool Whether the user is confirmed or not. */ public function getIsConfirmed() { return $this->confirmed_at != null; } /** * 是否禁用 * @return bool Whether the user is blocked or not. */ public function getIsBlocked() { return $this->blocked_at != null; } /** * 获取等级 * @return string */ public function getLevel() { return UserLevel::getLevel($this->profile->money); } /** * 生成二维码登录url * @param $access_token * @return string * @author nodelog */ public static function generateQrcodeLoginUrl($access_token) { return Yii::$app->request->hostInfo . '/api/v1/weixin/code?access_token=' . $access_token; } public static $typeList = [ self::TYPE_COMPANY => '企业用户', self::TYPE_PERSON => '个人用户', self::TYPE_WEB => '后台用户' ]; public static $typeIconList = [ self::TYPE_PERSON => '', self::TYPE_WEB => '' ]; /** * 格式化昵称 * @return string * @author nodelog */ private function formatNickname() { // $name = $this->profile->nickname ?: $this->username; // if ($htmlFormat) { // return Html::tag('span', $name . ' ' . self::$typeIconList[$this->type], ['title' => self::$typeList[$this->type]]); // } return $this->nickname ? $this->nickname : $this->username; } /** * 获得用户所有角色描述 * @return string * @author nodelog */ public function getAllRoleDesc() { $roles = Yii::$app->authManager->getRolesByUser($this->id); return implode(' | ', ArrayHelper::getColumn($roles, 'description')); } /** * 查询 左连接 去除公共部分,如教练查询未绑定教练微信用户,销售查询未绑定销售微信用户,FOR 后台添加教练和销售 筛选选择列表数据 * @param $model 模型类名 * @param $inIdArray 过滤的id数组 * @return array|User[] */ public static function getAllSelectFromModel($model, $inIdArray = null) { if (empty($inIdArray)) { $list = User::find()->joinWith($model::relationName())->where([self::tableName() . '.blocked_at' => null, $model::tableName() . '.user_id' => null])->all(); } else { $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(); } return $list; } /** * 获取可选管理员用户列表 * @author nodelog */ public static function lists() { if (Yii::$app->user->identity->getIsAdmin()) { //已经使用的用户ids // $usedUserIds = Company::find()->select('user_id')->distinct()->asArray()->indexBy('user_id')->column(); // //去掉当前包含的当前公司id // if (!empty($this->user_id) && isset($usedUserIds[$this->user_id])) { // unset($usedUserIds[$this->user_id]); // } // return User::find()->where(['not in', 'id', $usedUserIds])->asArray()->indexBy('id')->all(); $lists = User::find()->indexBy('id')->all(); $lists = ArrayHelper::map($lists, 'id', 'nickname'); return $lists; } else { return [ Yii::$app->user->id => Yii::$app->user->identity->nickname ]; } } /** * 获取用户的供应商 * @return \yii\db\ActiveQuery * @author nodelog */ public function getStore() { return $this->hasOne(Store::className(), ['user_id' => 'id']); } /** * 查询后对数据处理 * @author nodelog */ public function afterFind() { parent::afterFind(); // $this->nickname = $this->formatNickname(); //更新vip状态 // $this->isVip(); } /** * 获取分销等级 * @return \yii\db\ActiveQuery * @author nodelog */ public function getShareLevel() { return $this->hasOne(ShareLevel::className(), ['id' => 'level_id']); } /** * 分销海报 * * @param bool $force * @return bool|mixed|string * @throws ServerErrorHttpException * @author nodelog */ public function generateQrcode($force = false) { //海报存在直接返回 if ($this->qrcode && !$force) { return $this->qrcode->url; } //小程序码中 scene 放入用户id $scene = 'pid:' . \Yii::$app->user->id; $hash = md5($scene); $qrcodeFile = \Yii::getAlias('@storagePath/upload/qrcode/' . $hash . '.png');//path $qrcodeUrl = \Yii::getAlias('@storageUrl/upload/qrcode/' . $hash . '.png');//path try { ini_set('memory_limit', '256M'); $editor = Grafika::createEditor(GrafikaHelper::getSupportEditorLib()); //背景图 $editor->open($userPoster, Yii::$app->storage->getPath(\Yii::$app->config->get('share_poster_bg'))); //生成推广码 /* @var Qrcode $qrcode */ $qrcode = (new Application())->driver("mini.qrcode"); //小程序码 buffer,宽度220 $qrcodeStream = $qrcode->unLimit($scene, 'pages/index/index', $extra = []);//buffer $tempFile = \Yii::getAlias('@storagePath/upload/thumb/' . $hash . '.png'); if (!file_exists(dirname($tempFile))) { @mkdir(dirname($tempFile), 0755, true); } file_put_contents($tempFile, $qrcodeStream); // Yii::$app->storage->put($tempFile, $qrcodeStream); $editor->open($wxappQrcode, $tempFile); //测试二维码 // $editor->open($wxappQrcode, Yii::$app->storage->getPath('https://es.jiangzi.xin/storage/upload/20220408/OGGUn-K8HxojZfSesNv0OuVuAhRLYPzSJX0zEiyE.png')); $avatarPath = Yii::$app->storage->getPath($this->getAvatar(540)); if (strpos($avatarPath, 'http') !== false) { $avatarPath = Yii::$app->storage->thumbnail(\Yii::$app->config->get('share_image'), 540, 540); $editor->open($avatar, Yii::$app->storage->getPath($avatarPath)); } else { $editor->open($avatar, $avatarPath); } // $editor->resizeFill($goodsCover, 750, 750); $editor->resizeExactWidth($avatar, 540); //附加头像 $editor->blend($userPoster, $avatar, 'normal', 1.0, 'top-left', 105, 4900); //写入文字 $fontFile = Yii::getAlias('@root/web/assets/fonts/st-heiti-light.ttc'); //加用户昵称 $editor->text($userPoster, mb_substr($this->nickname, 0, 12), 100, 750, 5000, new Color('#ffffff'), $fontFile, 0); $editor->text($userPoster, Yii::$app->config->get('share_poster_text'), 70, 750, 5250, new Color('#ffffff'), $fontFile, 0); //调整小程序码图片 $editor->resizeExactWidth($wxappQrcode, 660); //附加小程序码图片 $editor->blend($userPoster, $wxappQrcode, 'normal', 1.0, 'top-right', -105, 4800); //保存图片 $editor->save($userPoster, $qrcodeFile, 85); //删除临时图片 unlink($tempFile); //上传分销码 list($attachment, $error) = Attachment::uploadFromFile('qrcode', $qrcodeFile); if ($error == null) { $this->qrcode = [ 'id' => $attachment->id, 'name' => $attachment->name, 'hash' => $attachment->hash, 'url' => $attachment->url, 'path' => $attachment->path, 'extension' => $attachment->extension, 'type' => $attachment->type, 'size' => $attachment->size ]; if ($this->save(false)) { return ArrayHelper::getValue($this, 'qrcode.url', ''); } } } catch (Exception $e) { throw new ServerErrorHttpException($e->getMessage()); } return $qrcodeUrl; } /** * 用户是否vip,并且未过期 * @author nodelog */ public function isVip() { //vip自动过期 if ($this->is_vip) { $vip = VipUser::find()->where(['user_id' => $this->id])->orderBy(['end_time' => SORT_DESC])->one(); if (!empty($vip) && $vip->isExpire()) { $this->is_vip = StatusEnum::STATUS_OFF; $this->save(false); } } return $this->is_vip; } /** * 获取当前vip * @author nodelog */ public function getCurrentVip() { $now = time(); return VipUser::find()->where(['user_id' => $this->id])->andWhere(['and', ['<=', 'start_time', $now], ['>', 'end_time', $now]])->one(); } // // public function fields() { // $fields = parent::fields(); // // $fields[] = 'dataCount'; // return $fields; // } /** * 数据统计 * @author nodelog */ public function getDataCount() { $data = [ [ 'text' => '日课', 'count' => 0 ], [ 'text' => '总课', 'count' => 0 ], [ 'text' => '辅助课', 'count' => 0 ], ]; //每日功课 // $data[0]['count'] = Data::find()->active()->andWhere(['user_id' => $this->id])->today()->count(); //累积功课 // $data[1]['count'] = Data::find()->active()->andWhere(['user_id' => $this->id])->count(); //辅助功课 // $userIds = VipUser::find()->active()->andWhere(['pay_user_id' => $this->id])->andWhere(['!=', 'user_id', $this->id])->select(['user_id'])->distinct()->column(); // $data[2]['count'] = Data::find()->active()->andWhere(['in', 'user_id', $userIds])->count(); //直接获取辅助榜数据 // $helpCount = DataRank::find()->active()->andWhere(['user_id' => $this->id, 'level' => DataTypeEnum::TYPE_HELP])->total()->select(['count'])->scalar(); // $data[2]['count'] = $helpCount ? $helpCount : 0; return $data; } }