diff --git a/app/Http/Controllers/Api/AbstractController.php b/app/Http/Controllers/Api/AbstractController.php new file mode 100644 index 00000000..77d1fd20 --- /dev/null +++ b/app/Http/Controllers/Api/AbstractController.php @@ -0,0 +1,14 @@ + 'need']); + } + if (empty($key)) { + if (!Captcha::check($code)) { + return Base::retError('请输入正确的验证码!', ['code' => 'need']); + } + } else { + if (!Captcha::check_api($code, $key)) { + return Base::retError('请输入正确的验证码!', ['code' => 'need']); + } + } + } + // + $retError = function ($msg) use ($email) { + Cache::forever("code::" . $email, "need"); + $needCode = !Base::isError(User::needCode($email)); + $needData = [ 'code' => $needCode ? 'need' : 'no' ]; + return Base::retError($msg, $needData); + }; + $user = User::whereEmail($email)->first(); + if (empty($user)) { + return $retError('账号或密码错误!'); + } + if ($user->userpass != Base::md52($userpass, $user->encrypt)) { + return $retError('账号或密码错误!'); + } + Cache::forget("code::" . $email); + } + // + $array = [ + 'loginnum' => $user['loginnum'] + 1, + 'lastip' => Base::getIp(), + 'lastdate' => time(), + 'lineip' => Base::getIp(), + 'linedate' => time(), + ]; + foreach ($array as $key => $value) { + $user->$key = $value; + } + $user->save(); + // + $user->token = User::token($user); + return Base::retSuccess($type == 'reg' ? "注册成功" : "登录成功", $user); + } + + /** + * @api {get} api/users/login/needcode 02. 是否需要验证码 + * + * @apiDescription 用于判断是否需要登录验证码 + * @apiVersion 1.0.0 + * @apiGroup users + * @apiName login__needcode + * + * @apiParam {String} email 用户名 + * + * @apiSuccess {Number} ret 返回状态码(1需要、0不需要) + * @apiSuccess {String} msg 返回信息(错误描述) + * @apiSuccess {Object} data 返回数据 + */ + public function login__needcode() + { + return User::needCode(trim(Request::input('email'))); + } + + /** + * @api {get} api/users/login/codeimg 03. 验证码图片 + * + * @apiDescription 用于判断是否需要登录验证码 + * @apiVersion 1.0.0 + * @apiGroup users + * @apiName login__codeimg + * + * @apiParam {String} email 用户名 + * + * @apiSuccess {Image} data 返回数据(直接输出图片) + */ + public function login__codeimg() + { + return Captcha::create(); + } + + /** + * @api {get} api/users/info 04. 获取我的信息 + * + * @apiDescription 需要token身份 + * @apiVersion 1.0.0 + * @apiGroup users + * @apiName info + * + * @apiParam {String} [callback] jsonp返回字段 + * + * @apiSuccess {Number} ret 返回状态码(1正确、0错误) + * @apiSuccess {String} msg 返回信息(错误描述) + * @apiSuccess {Object} data 返回数据 + * @apiSuccessExample {json} data: + { + "userid":1, + "agentid":0, + "identity":[ + "admin" + ], + "token":"MUBhZG1pbkAwRWFGSFhAMTYwODY5MjM0MUBXcVJpQ1Q=", + "az":"G", + "username":"admin", + "nickname":"管理员", + "userimg":"http://127.0.0.1:6006/images/other/avatar.png", + "loginnum":10, + "changepass":0, + "lastip":"172.18.0.1", + "lastdate":1608692341, + "lineip":"172.18.0.1", + "linedate":1608704450, + "regip":"127.0.0.1", + "regdate":1600856611, + "setting":null, + "time":1608704450, + "setpass":1, + + "service":{ + "id":1, + "type":1, + "create_id":1, + "created_at":"2020-12-23 14:39:51", + "expire_at":"2021-01-23 14:39:51", + "type_name":"商业版", + "member":10, + "network":3 + }, + "num_member":3, + "num_network":2 + } + */ + public function info() + { + $callback = Request::input('callback'); + // + $user = User::authE(); + if (Base::isError($user)) { + if (strlen($callback) > 3) { + return $callback . '(' . json_encode($user) . ')'; + } + return $user; + } else { + $user = User::IDE($user['data']); + } + // + if (strlen($callback) > 3) { + return $callback . '(' . json_encode(Base::retSuccess('success', $user)) . ')'; + } + return Base::retSuccess('success', $user); + } + + /** + * @api {get} api/users/editdata 05. 修改自己的资料 + * + * @apiDescription 需要token身份 + * @apiVersion 1.0.0 + * @apiGroup users + * @apiName editdata + * + * @apiParam {Object} [userimg] 会员头像(地址) + * @apiParam {String} [nickname] 昵称 + * + * @apiSuccess {Number} ret 返回状态码(1正确、0错误) + * @apiSuccess {String} msg 返回信息(错误描述) + * @apiSuccess {Object} data 返回数据(同"获取我的信息"接口) + */ + public function editdata() + { + $user = User::authE(); + if (Base::isError($user)) { + return $user; + } else { + $user = User::IDE($user['data']); + } + // + //头像 + $userimg = Request::input('userimg'); + if ($userimg) { + $userimg = is_array($userimg) ? $userimg[0]['path'] : $userimg; + $user->userimg = Base::unFillUrl($userimg); + } + //昵称 + $nickname = trim(Request::input('nickname')); + if ($nickname) { + if (mb_strlen($nickname) < 2) { + return Base::retError('昵称不可以少于2个字!'); + } elseif (mb_strlen($nickname) > 8) { + return Base::retError('昵称最多只能设置8个字!'); + } else { + $user->nickname = $nickname; + } + } + // + $user->save(); + return Base::retSuccess('修改成功!', $user); + } + + /** + * @api {get} api/users/editpass 06. 修改自己的密码 + * + * @apiDescription 需要token身份 + * @apiVersion 1.0.0 + * @apiGroup users + * @apiName editpass + * + * @apiParam {String} oldpass 旧密码 + * @apiParam {String} newpass 新密码 + * + * @apiSuccess {Number} ret 返回状态码(1正确、0错误) + * @apiSuccess {String} msg 返回信息(错误描述) + * @apiSuccess {Object} data 返回数据(同"获取我的信息"接口) + */ + public function editpass() + { + $user = User::authE(); + if (Base::isError($user)) { + return $user; + } else { + $user = User::IDE($user['data']); + } + // + $oldpass = trim(Request::input('oldpass')); + $newpass = trim(Request::input('newpass')); + if (strlen($newpass) < 6) { + return Base::retError('密码设置不能小于6位数!'); + } elseif (strlen($newpass) > 32) { + return Base::retError('密码最多只能设置32位数!'); + } + if ($oldpass == $newpass) { + return Base::retError('新旧密码一致!'); + } + // + if (env("PASSWORD_ADMIN") == 'disabled') { + if ($user->userid == 1) { + return Base::retError('当前环境禁止修改密码!'); + } + } + if (env("PASSWORD_OWNER") == 'disabled') { + return Base::retError('当前环境禁止修改密码!'); + } + // + $verify = User::whereUserid($user->userid)->whereUserpass(Base::md52($oldpass, User::token2encrypt()))->count(); + if (empty($verify)) { + return Base::retError('请填写正确的旧密码!'); + } + // + $user->encrypt = Base::generatePassword(6); + $user->userpass = Base::md52($newpass, $user->encrypt); + $user->changepass = 0; + $user->save(); + return Base::retSuccess('修改成功!', $user); + } + + /** + * @api {get} api/users/login/codejson 07. 验证码json + * + * @apiDescription 用于判断是否需要登录验证码 + * @apiVersion 1.0.0 + * @apiGroup users + * @apiName login__codejson + * + * @apiSuccess {Number} ret 返回状态码(1正确、0错误) + * @apiSuccess {String} msg 返回信息(错误描述) + * @apiSuccess {Object} data 返回数据 + */ + public function login__codejson() + { + $captcha = Captcha::create('default', true); + return Base::retSuccess('请求成功', $captcha); + } + + /** + * @api {get} api/users/searchinfo 08. 搜索会员列表 + * + * @apiDescription 搜索会员列表 + * @apiVersion 1.0.0 + * @apiGroup users + * @apiName searchinfo + * + * @apiParam {Object} where 搜索条件 + * - where.email + * - where.noemail + * - where.username + * - where.nousername + * - where.usernameequal + * - where.noidentity + * - where.identity + * @apiParam {Number} [take] 获取数量,10-100 + * + * @apiSuccess {Number} ret 返回状态码(1正确、0错误) + * @apiSuccess {String} msg 返回信息(错误描述) + * @apiSuccess {Object} data 返回数据 + */ + public function searchinfo() + { + $keys = Request::input('where'); + $whereArr = []; + $whereRaw = null; + if ($keys['email']) $whereArr[] = ['email', '=', $keys['email']]; + if ($keys['usernameequal']) $whereArr[] = ['username', '=', $keys['usernameequal']]; + if ($keys['identity']) $whereArr[] = ['identity', 'like', '%,' . $keys['identity'] . ',%']; + if ($keys['noidentity']) $whereArr[] = ['identity', 'not like', '%,' . $keys['noidentity'] . ',%']; + if ($keys['username']) { + $whereRaw.= $whereRaw ? ' AND ' : ''; + $whereRaw.= "(`username` LIKE '%" . $keys['username'] . "%' OR `nickname` LIKE '%" . $keys['username'] . "%')"; + } + if ($keys['nousername']) { + $nousername = []; + foreach (explode(",", $keys['nousername']) AS $name) { + $name = trim($name); + if ($name && !in_array($name, $nousername)) { + $nousername[] = $name; + } + } + if ($nousername) { + $whereRaw.= $whereRaw ? ' AND ' : ''; + $whereRaw.= "(`username` NOT IN ('" . implode("','", $nousername) . "'))"; + } + } + if ($keys['noemail']) { + $noemail = []; + foreach (explode(",", $keys['noemail']) AS $email) { + $email = trim($email); + if ($email && !in_array($email, $noemail)) { + $noemail[] = $email; + } + } + if ($noemail) { + $whereRaw.= $whereRaw ? ' AND ' : ''; + $whereRaw.= "(`email` NOT IN ('" . implode("','", $noemail) . "'))"; + } + } + // + $list = User::select(['userid', 'email', 'username', 'nickname', 'userimg']) + ->where($whereArr) + ->whereRaw($whereRaw) + ->orderBy('userid') + ->take(Base::getPaginate(100, 10, 'take')) + ->get(); + return Base::retSuccess('success', $list); + } + + /** + * @api {get} api/users/basic 09. 获取指定会员基本信息 + * + * @apiDescription 需要token身份 + * @apiVersion 1.0.0 + * @apiGroup users + * @apiName basic + * + * @apiParam {String} email 会员用户名(多个格式:jsonArray,一次最多30个) + * + * @apiSuccess {Number} ret 返回状态码(1正确、0错误) + * @apiSuccess {String} msg 返回信息(错误描述) + * @apiSuccess {Object} data 返回数据 + */ + public function basic() + { + $email = trim(Request::input('email')); + $array = Base::json2array($email); + if (empty($array)) { + $array[] = $email; + } + if (count($array) > 50) { + return Base::retError(['一次最多只能获取%条数据!', 50]); + } + $retArray = []; + foreach ($array AS $name) { + $basic = User::email2basic($name); + if ($basic) { + $retArray[] = $basic; + } + } + return Base::retSuccess('success', $retArray); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 80bb70ff..868b3a0b 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -13,6 +13,7 @@ use Cache; * @property int $userid * @property array $identity 身份 * @property string|null $az A-Z + * @property string|null $email 邮箱 * @property string|null $username 用户名 * @property string $nickname 昵称 * @property string|null $userimg 头像 @@ -33,6 +34,7 @@ use Cache; * @method static \Illuminate\Database\Eloquent\Builder|User newQuery() * @method static \Illuminate\Database\Eloquent\Builder|User query() * @method static \Illuminate\Database\Eloquent\Builder|User whereAz($value) + * @method static \Illuminate\Database\Eloquent\Builder|User whereEmail($value) * @method static \Illuminate\Database\Eloquent\Builder|User whereChangepass($value) * @method static \Illuminate\Database\Eloquent\Builder|User whereCreatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|User whereEncrypt($value) @@ -54,6 +56,8 @@ use Cache; */ class User extends AbstractModel { + protected $primaryKey = 'userid'; + protected $hidden = [ 'encrypt', 'userpass', @@ -66,7 +70,13 @@ class User extends AbstractModel */ public function getNicknameAttribute($value) { - return $value ?: $this->username; + if ($value) { + return $value; + } + if ($this->username) { + return $this->username; + } + return Base::getMiddle($this->email, null, "@"); } /** @@ -90,6 +100,49 @@ class User extends AbstractModel } + /** ***************************************************************************************** */ + /** ***************************************************************************************** */ + /** ***************************************************************************************** */ + + /** + * 注册会员 + * @param $email + * @param $userpass + * @param array $other + * @return array + */ + public static function reg($email, $userpass, $other = []) + { + //邮箱 + if (!Base::isMail($email)) { + return Base::retError('请输入正确的邮箱地址!'); + } + if (User::email2userid($email) > 0) { + return Base::retError('邮箱地址已存在!'); + } + //密码 + if (strlen($userpass) < 6) { + return Base::retError(['密码设置不能小于%位数!', 6]); + } elseif (strlen($userpass) > 32) { + return Base::retError(['密码最多只能设置%位数!', 32]); + } + //开始注册 + $encrypt = Base::generatePassword(6); + $inArray = [ + 'encrypt' => $encrypt, + 'email' => $email, + 'userpass' => Base::md52($userpass, $encrypt), + 'regip' => Base::getIp(), + 'regdate' => time() + ]; + if ($other) { + $inArray = array_merge($inArray, $other); + } + $user = User::createInstance($inArray); + $user->save(); + User::AZUpdate($user->userid); + return Base::retSuccess('success', $user); + } /** * userid获取用户名 @@ -104,6 +157,19 @@ class User extends AbstractModel return self::whereUserid(intval($userid))->value('username'); } + /** + * 邮箱获取userid + * @param $email + * @return int + */ + public static function email2userid($email) + { + if (empty($email)) { + return 0; + } + return intval(self::whereUsername($email)->value('userid')); + } + /** * 用户名获取userid * @param $username @@ -234,12 +300,12 @@ class User extends AbstractModel /** * 生成token - * @param $userinfo + * @param self $userinfo * @return string */ public static function token($userinfo) { - return base64_encode($userinfo['userid'] . '@' . $userinfo['username'] . '@' . $userinfo['encrypt'] . '@' . time() . '@' . Base::generatePassword(6)); + return base64_encode($userinfo->userid . '@' . $userinfo->username . '@' . $userinfo->encrypt . '@' . time() . '@' . Base::generatePassword(6)); } /** @@ -289,47 +355,60 @@ class User extends AbstractModel /** * userid 获取 基本信息 * @param int $userid 会员ID - * @return array + * @return self */ public static function userid2basic(int $userid) { global $_A; if (empty($userid)) { - return []; + return null; } if (isset($_A["__static_userid2basic_" . $userid])) { return $_A["__static_userid2basic_" . $userid]; } - $fields = ['userid', 'username', 'nickname', 'userimg']; + $fields = ['userid', 'email', 'username', 'nickname', 'userimg']; $userInfo = self::whereUserid($userid)->select($fields)->first(); - if ($userInfo) { - $userInfo->userimg = self::userimg($userInfo->userimg); - } return $_A["__static_userid2basic_" . $userid] = ($userInfo ?: []); } /** * username 获取 基本信息 * @param string $username 用户名 - * @return array + * @return self */ public static function username2basic(string $username) { global $_A; if (empty($username)) { - return []; + return null; } if (isset($_A["__static_username2basic_" . $username])) { return $_A["__static_username2basic_" . $username]; } - $fields = ['userid', 'username', 'nickname', 'userimg']; + $fields = ['userid', 'email', 'username', 'nickname', 'userimg']; $userInfo = self::whereUsername($username)->select($fields)->first(); - if ($userInfo) { - $userInfo->userimg = self::userimg($userInfo->userimg); - } return $_A["__static_username2basic_" . $username] = ($userInfo ?: []); } + /** + * email 获取 基本信息 + * @param string $email 邮箱地址 + * @return self + */ + public static function email2basic(string $email) + { + global $_A; + if (empty($email)) { + return null; + } + if (isset($_A["__static_email2basic_" . $email])) { + return $_A["__static_email2basic_" . $email]; + } + $fields = ['userid', 'email', 'username', 'nickname', 'userimg']; + $userInfo = self::whereEmail($email)->select($fields)->first(); + return $_A["__static_email2basic_" . $email] = ($userInfo ?: []); + } + /** * 用户头像,不存在时返回默认 * @param string $var 头像地址 或 会员用户名 @@ -342,7 +421,9 @@ class User extends AbstractModel $var = ""; } else { $userInfo = self::username2basic($var); - $var = $userInfo['userimg']; + if ($userInfo) { + $var = $userInfo->userimg; + } } } return $var ? Base::fillUrl($var) : url('images/other/avatar.png'); @@ -354,7 +435,7 @@ class User extends AbstractModel */ public static function AZUpdate($userid) { - $row = self::whereUserid($userid)->select(['username', 'nickname'])->first(); + $row = self::whereUserid($userid)->select(['email', 'username', 'nickname'])->first(); if ($row) { $row->az = Base::getFirstCharter($row->nickname); $row->save(); diff --git a/app/Module/Base.php b/app/Module/Base.php index ac182a27..0d409e52 100755 --- a/app/Module/Base.php +++ b/app/Module/Base.php @@ -2023,6 +2023,18 @@ class Base ) * 6380'; } + /** + * 获取每页数量 + * @param $max + * @param $default + * @param string $inputName + * @return mixed + */ + public static function getPaginate($max, $default, $inputName = 'pagesize') + { + return Min(Max(Base::nullShow(Request::input($inputName), $default), 1), $max); + } + /** * image64图片保存 * @param array $param [ image64=带前缀的base64, path=>文件路径, fileName=>文件名称, scale=>[压缩原图宽,高, 压缩方式] ] diff --git a/config/captcha.php b/config/captcha.php new file mode 100644 index 00000000..5bb64e50 --- /dev/null +++ b/config/captcha.php @@ -0,0 +1,48 @@ + ['2', '3', '4', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'm', 'n', 'p', 'q', 'r', 't', 'u', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'M', 'N', 'P', 'Q', 'R', 'T', 'U', 'X', 'Y', 'Z'], + 'default' => [ + 'length' => 5, + 'width' => 130, + 'height' => 36, + 'quality' => 90, + 'math' => false, + ], + 'math' => [ + 'length' => 9, + 'width' => 120, + 'height' => 36, + 'quality' => 90, + 'math' => true, + ], + + 'flat' => [ + 'length' => 6, + 'width' => 180, + 'height' => 46, + 'quality' => 90, + 'lines' => 6, + 'bgImage' => false, + 'bgColor' => '#ecf2f4', + 'fontColors' => ['#2c3e50', '#c0392b', '#16a085', '#c0392b', '#8e44ad', '#303f9f', '#f57c00', '#795548'], + 'contrast' => -5, + ], + 'mini' => [ + 'length' => 3, + 'width' => 60, + 'height' => 32, + ], + 'inverse' => [ + 'length' => 5, + 'width' => 120, + 'height' => 36, + 'quality' => 90, + 'sensitive' => true, + 'angle' => 12, + 'sharpen' => 10, + 'blur' => 2, + 'invert' => true, + 'contrast' => -5, + ] +]; diff --git a/resources/assets/js/pages/login.vue b/resources/assets/js/pages/login.vue index 4afd47b0..62c2dc73 100644 --- a/resources/assets/js/pages/login.vue +++ b/resources/assets/js/pages/login.vue @@ -1,25 +1,200 @@ + diff --git a/resources/assets/js/store/mutations.js b/resources/assets/js/store/mutations.js index 82e589d1..6c747b96 100644 --- a/resources/assets/js/store/mutations.js +++ b/resources/assets/js/store/mutations.js @@ -2,5 +2,10 @@ export default { projectChatShowToggle(state) { state.projectChatShow = !state.projectChatShow state.setStorage('projectChatShow', state.projectChatShow); + }, + userInfo(state, info) { + state.userInfo = info + state.setStorage('userInfo', info); + state.setStorage('token', state._isJson(info) ? info.token : ''); } } diff --git a/resources/assets/js/store/state.js b/resources/assets/js/store/state.js index 0f6179bb..5645eceb 100644 --- a/resources/assets/js/store/state.js +++ b/resources/assets/js/store/state.js @@ -23,6 +23,24 @@ const state = { return typeof value === "boolean" ? value : def; }, + getStorageArray(key, def = {}) { + let value = this._storage(key); + return this._isArray(value) ? value : def; + }, + + getStorageJson(key, def = {}) { + let value = this._storage(key); + return this._isJson(value) ? value : def; + }, + + _isArray(obj) { + return typeof (obj) == "object" && Object.prototype.toString.call(obj).toLowerCase() == '[object array]' && typeof obj.length == "number"; + }, + + _isJson(obj) { + return typeof (obj) == "object" && Object.prototype.toString.call(obj).toLowerCase() == "[object object]" && typeof obj.length == "undefined"; + }, + _storage(key, value) { let keyName = 'state'; if (typeof value === 'undefined') { @@ -71,4 +89,6 @@ const state = { export default Object.assign(state, { projectChatShow: state.getStorageBoolean('projectChatShow', true), + userInfo: state.getStorageJson('userInfo'), + token: state.getStorageString('token'), }) diff --git a/routes/web.php b/routes/web.php index 1616a9ee..15aa0c3d 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,5 +1,6 @@ middleware(['webapi'])->group(function () { - + // 会员 + Route::any('users/{method}', UsersController::class); + Route::any('users/{method}/{action}', UsersController::class); }); /**