From cb05c2d4870f2a4a647763910bd4522a03daa3c6 Mon Sep 17 00:00:00 2001 From: 15090180611 <215509543@qq.com> Date: Fri, 28 Mar 2025 18:06:59 +0800 Subject: [PATCH] =?UTF-8?q?=E7=99=BB=E5=BD=95=E9=80=80=E5=87=BAapi?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/admin/view/command/add.html | 1 + application/adminapi/behavior/AdminLog.php | 14 ++ application/adminapi/controller/Admin.php | 100 +++++++++ application/adminapi/library/Auth.php | 47 +++- application/adminapi/model/Admin.php | 118 ++++++++++ application/adminapi/model/AdminLog.php | 119 +++++++++++ application/adminapi/tags.php | 18 ++ application/common/controller/AdminApi.php | 238 ++++++++++++++++++++- 8 files changed, 650 insertions(+), 5 deletions(-) create mode 100644 application/adminapi/behavior/AdminLog.php create mode 100644 application/adminapi/controller/Admin.php create mode 100644 application/adminapi/model/Admin.php create mode 100644 application/adminapi/model/AdminLog.php create mode 100644 application/adminapi/tags.php diff --git a/application/admin/view/command/add.html b/application/admin/view/command/add.html index 2180096..b2b330f 100644 --- a/application/admin/view/command/add.html +++ b/application/admin/view/command/add.html @@ -353,6 +353,7 @@ + diff --git a/application/adminapi/behavior/AdminLog.php b/application/adminapi/behavior/AdminLog.php new file mode 100644 index 0000000..f2563ae --- /dev/null +++ b/application/adminapi/behavior/AdminLog.php @@ -0,0 +1,14 @@ +isPost() && config('fastadmin.auto_record_log')) { + \app\adminapi\model\AdminLog::record(); + } + } +} diff --git a/application/adminapi/controller/Admin.php b/application/adminapi/controller/Admin.php new file mode 100644 index 0000000..a280f9b --- /dev/null +++ b/application/adminapi/controller/Admin.php @@ -0,0 +1,100 @@ +model = new AdminModel; + parent::_initialize(); + + $auth = $this->auth; + //监听注册登录退出的事件 + Hook::add('admin_login_successed', function ($user) use ($auth) { + $expire = input('post.keeplogin') ? 30 * 86400 : 0; + Cookie::set('adminid', $user->id, $expire); + Cookie::set('admintoken', $auth->getToken(), $expire); + }); + Hook::add('admin_register_successed', function ($user) use ($auth) { + Cookie::set('adminid', $user->id); + Cookie::set('admintoken', $auth->getToken()); + }); + Hook::add('admin_delete_successed', function ($user) use ($auth) { + Cookie::delete('adminid'); + Cookie::delete('admintoken'); + }); + Hook::add('admin_logout_successed', function ($user) use ($auth) { + Cookie::delete('adminid'); + Cookie::delete('admintoken'); + }); + + + } + + + + /** + * 管理员登录 + * + * @ApiMethod (POST) + * @ApiParams (name="account", type="string", required=true, description="账号") + * @ApiParams (name="password", type="string", required=true, description="密码") + */ + public function login() + { + $account = $this->request->post('account'); + $password = $this->request->post('password'); + if (!$account || !$password) { + $this->error(__('Invalid parameters')); + } + $ret = $this->auth->login($account, $password); + if ($ret) { + $data = ['userinfo' => $this->auth->getUserinfo()]; + $this->success(__('Logged in successful'), $data); + } else { + $this->error($this->auth->getError()); + } + } + + + + /** + * 退出登录 + * @ApiMethod (POST) + */ + public function logout() + { +// //设置contenttype不为表单格式防止触发宝塔防火墙 +// header('Content-Type:application/json; charset=utf-8'); + if (!$this->request->isPost()) { + $this->error(__('Invalid parameters')); + } +// $password = $this->request->post('datatime'); + + $this->auth->logout(); + $this->success(__('Logout successful')); + } + + + +} \ No newline at end of file diff --git a/application/adminapi/library/Auth.php b/application/adminapi/library/Auth.php index a93f12d..744329d 100644 --- a/application/adminapi/library/Auth.php +++ b/application/adminapi/library/Auth.php @@ -1,6 +1,6 @@ rules) { return $this->rules; } - $group = $this->_user->groups; - if (!$group) { + $groups = $this->_user->groups; + if (!$groups) { return []; } - $rules = explode(',', $group->rules); + $rules = []; + foreach ($groups as $group){ + $rules = array_merge($rules, explode(',', $group->rules)); + } + $this->rules = AuthRule::where('status', 'normal')->where('id', 'in', $rules)->field('id,pid,rule_name,name,title,ismenu')->select(); return $this->rules; } @@ -462,4 +467,38 @@ class Auth return $this->_error ? __($this->_error) : ''; } + + + /** + * 获得面包屑导航 + * @param string $path + * @return array + */ + public function getBreadCrumb($path = '') + { + if ($this->breadcrumb || !$path) { + return $this->breadcrumb; + } + $titleArr = []; + $menuArr = []; + $urlArr = explode('/', $path); + foreach ($urlArr as $index => $item) { + $pathArr[implode('/', array_slice($urlArr, 0, $index + 1))] = $index; + } + if (!$this->rules && $this->id) { + $this->getRuleList(); + } + foreach ($this->rules as $rule) { + if (isset($pathArr[$rule['name']])) { + $rule['title'] = __($rule['title']); + $rule['url'] = url($rule['name']); + $titleArr[$pathArr[$rule['name']]] = $rule['title']; + $menuArr[$pathArr[$rule['name']]] = $rule; + } + } + ksort($menuArr); + $this->breadcrumb = $menuArr; + return $this->breadcrumb; + } + } \ No newline at end of file diff --git a/application/adminapi/model/Admin.php b/application/adminapi/model/Admin.php new file mode 100644 index 0000000..81f336f --- /dev/null +++ b/application/adminapi/model/Admin.php @@ -0,0 +1,118 @@ +column("group_id"); + if(!$group_ids)return []; + + $groups = \app\admin\model\api\AuthGroup::where("id","in",$group_ids)->select(); + return $groups; + } + + + + + + + public static function init() + { + self::beforeWrite(function ($row) { + $changed = $row->getChangedData(); + //如果修改了用户或或密码则需要重新登录 + if (isset($changed['username']) || isset($changed['password']) || isset($changed['salt'])) { + $row->token = ''; + } + }); + } + + public function getAddressCityList(){ + return SearchCity::column("address_city","id"); + } + + + public static function getHaveShopId($adminId){ + $admin = self::get($adminId); + if(!$admin)throw new \Exception("管理员不存在"); + $area_json = $admin->area_json; + $shop_id = "*"; + if($area_json){ + $shop_id = []; + $searchCity = SearchCity::where("id","in",$area_json)->select(); + foreach ($searchCity as $item){ + $province = $item->province; + $city = $item->city; + $district = $item->district; + //查询在该区域的店铺id + $manystoreShop = new ManystoreShop; + if($province)$manystoreShop = $manystoreShop->where("province",$province); + if($city)$manystoreShop = $manystoreShop->where("city",$city); + if($district)$manystoreShop = $manystoreShop->where("district",$district); + $shop_id = array_merge($shop_id,$manystoreShop->column("id")); + } + } + return $shop_id; + } + + public static function getHaveCity($adminId){ + $provinces = $citys = $districts = $address_citys = []; + $admin = self::get($adminId); + if(!$admin)return compact("provinces","citys","districts","address_citys"); + $area_json = $admin->area_json; + + if($area_json){ + $searchCity = SearchCity::where("id","in",$area_json)->select(); + foreach ($searchCity as $item){ + $address_citys[] = $item->address_city; + $province = $item->province; + if($province)$provinces[] = $province; + $city = $item->city; + if($city)$citys[] = $city; + $district = $item->district; + if($district)$districts[] = $district; + } + } + if(!$provinces)$provinces="*"; + if(!$citys)$citys="*"; + if(!$districts)$districts="*"; + if(!$address_citys)$address_citys="*"; + return compact("provinces","citys","districts","address_citys"); + } + + + + public static function checkAuthMsg($adminId){ + ["provinces" =>$provinces,"citys"=> $citys, "districts"=>$districts,"address_citys"=>$address_citys] = self::getHaveCity($adminId); + if(is_array($address_citys))$address_citys = implode(",",$address_citys); + //必要信息已完善 + return '
+ 您当前的区域管理权限为: '.$address_citys.' (能管理该区域下的机构信息,如果是*则不限制)
'; + + } + + +} diff --git a/application/adminapi/model/AdminLog.php b/application/adminapi/model/AdminLog.php new file mode 100644 index 0000000..37da958 --- /dev/null +++ b/application/adminapi/model/AdminLog.php @@ -0,0 +1,119 @@ +isLogin() ? $auth->id : 0; + $username = $auth->isLogin() ? $auth->username : __('Unknown'); + + // 设置过滤函数 + request()->filter('trim,strip_tags,htmlspecialchars'); + $modulename = request()->module(); + $controllername = Loader::parseName(request()->controller()); + $actionname = strtolower(request()->action()); + $path = $modulename. '/' . str_replace('.', '/', $controllername) . '/' . $actionname; + if (self::$ignoreRegex) { + foreach (self::$ignoreRegex as $index => $item) { + if (preg_match($item, $path)) { + return; + } + } + } + $content = $content ?: self::$content; + if (!$content) { + $content = request()->param('') ?: file_get_contents("php://input"); + $content = self::getPureContent($content); + } + $title = $title ?: self::$title; + if (!$title) { + $title = []; + $breadcrumb = Auth::instance()->getBreadcrumb($path); + foreach ($breadcrumb as $k => $v) { + $title[] = $v['title']; + } + $title = implode(' / ', $title); + } + self::create([ + 'title' => $title, + 'content' => !is_scalar($content) ? json_encode($content, JSON_UNESCAPED_UNICODE) : $content, + 'url' => substr(xss_clean(strip_tags(request()->url())), 0, 1500), + 'admin_id' => $admin_id, + 'username' => $username, + 'useragent' => substr(request()->server('HTTP_USER_AGENT'), 0, 255), + 'ip' => xss_clean(strip_tags(request()->ip())) + ]); + } + + /** + * 获取已屏蔽关键信息的数据 + * @param $content + * @return array + */ + protected static function getPureContent($content) + { + if (!is_array($content)) { + return $content; + } + foreach ($content as $index => &$item) { + if (preg_match("/(password|salt|token)/i", $index)) { + $item = "***"; + } else { + if (is_array($item)) { + $item = self::getPureContent($item); + } + } + } + return $content; + } + + public function admin() + { + return $this->belongsTo('Admin', 'admin_id')->setEagerlyType(0); + } +} diff --git a/application/adminapi/tags.php b/application/adminapi/tags.php new file mode 100644 index 0000000..882fdfb --- /dev/null +++ b/application/adminapi/tags.php @@ -0,0 +1,18 @@ + +// +---------------------------------------------------------------------- +// 应用行为扩展定义文件 +return [ + // 应用结束 + 'app_end' => [ + 'app\\adminapi\\behavior\\AdminLog', + ], +]; diff --git a/application/common/controller/AdminApi.php b/application/common/controller/AdminApi.php index 26266a9..8295574 100644 --- a/application/common/controller/AdminApi.php +++ b/application/common/controller/AdminApi.php @@ -3,7 +3,7 @@ namespace app\common\controller; -use app\common\library\Auth; +use app\adminapi\library\Auth; use app\common\library\Virtual; use think\Config; use think\exception\HttpResponseException; @@ -125,6 +125,242 @@ class AdminApi //移除HTML标签 $this->request->filter('trim,strip_tags,htmlspecialchars'); + + $this->auth = Auth::instance(); + + $modulename = $this->request->module(); + $controllername = Loader::parseName($this->request->controller()); + $actionname = strtolower($this->request->action()); + // token + $token = $this->request->server('HTTP_ADMINTOKEN', $this->request->request('admintoken', \think\Cookie::get('admintoken'))); + + $path = $modulename. '/' .str_replace('.', '/', $controllername) . '/' . $actionname; + // 设置当前请求的URI + $this->auth->setRequestUri($path); + + // 检测是否需要验证登录 + if (!$this->auth->match($this->noNeedLogin)) { + //初始化 + $this->auth->init($token); + //检测是否登录 + if (!$this->auth->isLogin()) { + $this->error(__('Please login first'), ['errcode'=>30002], 401); + } + // 判断是否需要验证权限 + if (!$this->auth->match($this->noNeedRight)) { + // 判断控制器和方法判断是否有对应权限 + if (!$this->auth->check($path)) { + $this->error(__('You have no permission'), null, 403); + } + } + } else { + // 如果有传递token才验证是否登录状态 + if ($token) { + $this->auth->init($token); + } + } + + + $upload = \app\common\model\Config::upload(); + + // 上传信息配置后 + Hook::listen("upload_config_init", $upload); + + Config::set('upload', array_merge(Config::get('upload'), $upload)); + + // 加载当前控制器语言包 + $this->loadlang($controllername); +// Virtual::visitNnmberUpdate(); } + + /** + * 加载语言文件 + * @param string $name + */ + protected function loadlang($name) + { + $name = Loader::parseName($name); + $name = preg_match("/^([a-zA-Z0-9_\.\/]+)\$/i", $name) ? $name : 'index'; + $lang = $this->request->langset(); + $lang = preg_match("/^([a-zA-Z\-_]{2,10})\$/i", $lang) ? $lang : 'zh-cn'; + Lang::load(APP_PATH . $this->request->module() . '/lang/' . $lang . '/' . str_replace('.', '/', $name) . '.php'); + } + + + /** + * 操作成功返回的数据 + * @param string $msg 提示信息 + * @param mixed $data 要返回的数据 + * @param int $code 错误码,默认为1 + * @param string $type 输出类型 + * @param array $header 发送的 Header 信息 + */ + protected function success($msg = '', $data = null, $code = 1, $type = null, array $header = []) + { + $this->result($msg, $data, $code, $type, $header); + } + + /** + * 操作失败返回的数据 + * @param string $msg 提示信息 + * @param mixed $data 要返回的数据 + * @param int $code 错误码,默认为0 + * @param string $type 输出类型 + * @param array $header 发送的 Header 信息 + */ + protected function error($msg = '', $data = null, $code = 0, $type = null, array $header = []) + { + $this->result($msg, $data, $code, $type, $header); + } + + /** + * 返回封装后的 API 数据到客户端 + * @access protected + * @param mixed $msg 提示信息 + * @param mixed $data 要返回的数据 + * @param int $code 错误码,默认为0 + * @param string $type 输出类型,支持json/xml/jsonp + * @param array $header 发送的 Header 信息 + * @return void + * @throws HttpResponseException + */ + protected function result($msg, $data = null, $code = 0, $type = null, array $header = []) + { + $result = [ + 'code' => $code, + 'msg' => $msg, + 'time' => Request::instance()->server('REQUEST_TIME'), + 'data' => $data, + ]; + // 如果未设置类型则使用默认类型判断 + $type = $type ? : $this->responseType; + + if (isset($header['statuscode'])) { + $code = $header['statuscode']; + unset($header['statuscode']); + } else { + //未设置状态码,根据code值判断 + $code = $code >= 1000 || $code < 200 ? 200 : $code; + } + $response = Response::create($result, $type, $code)->header($header); + throw new HttpResponseException($response); + } + + + /** + * 前置操作 + * @access protected + * @param string $method 前置操作方法名 + * @param array $options 调用参数 ['only'=>[...]] 或者 ['except'=>[...]] + * @return void + */ + protected function beforeAction($method, $options = []) + { + if (isset($options['only'])) { + if (is_string($options['only'])) { + $options['only'] = explode(',', $options['only']); + } + + if (!in_array($this->request->action(), $options['only'])) { + return; + } + } elseif (isset($options['except'])) { + if (is_string($options['except'])) { + $options['except'] = explode(',', $options['except']); + } + + if (in_array($this->request->action(), $options['except'])) { + return; + } + } + + call_user_func([$this, $method]); + } + + /** + * 设置验证失败后是否抛出异常 + * @access protected + * @param bool $fail 是否抛出异常 + * @return $this + */ + protected function validateFailException($fail = true) + { + $this->failException = $fail; + + return $this; + } + + + + /** + * 验证数据 + * @access protected + * @param array $data 数据 + * @param string|array $validate 验证器名或者验证规则数组 + * @param array $message 提示信息 + * @param bool $batch 是否批量验证 + * @param mixed $callback 回调方法(闭包) + * @return array|string|true + * @throws ValidateException + */ + protected function validate($data, $validate, $message = [], $batch = false, $callback = null) + { + if (is_array($validate)) { + $v = Loader::validate(); + $v->rule($validate); + } else { + // 支持场景 + if (strpos($validate, '.')) { + list($validate, $scene) = explode('.', $validate); + } + + $v = Loader::validate($validate); + + !empty($scene) && $v->scene($scene); + } + + // 批量验证 + if ($batch || $this->batchValidate) { + $v->batch(true); + } + // 设置错误信息 + if (is_array($message)) { + $v->message($message); + } + // 使用回调验证 + if ($callback && is_callable($callback)) { + call_user_func_array($callback, [$v, &$data]); + } + + if (!$v->check($data)) { + if ($this->failException) { + throw new ValidateException($v->getError()); + } + + return $v->getError(); + } + + return true; + } + + + /** + * 刷新Token + */ + protected function token() + { + $token = $this->request->param('__token__'); + + //验证Token + if (!Validate::make()->check(['__token__' => $token], ['__token__' => 'require|token'])) { + $this->error(__('Token verification error'), ['__token__' => $this->request->token()]); + } + + //刷新Token + $this->request->token(); + } + + + } \ No newline at end of file