diff --git a/application/admin/controller/auth/api/Rule.php b/application/admin/controller/auth/api/Rule.php index 6665fb6..f0c8cb6 100644 --- a/application/admin/controller/auth/api/Rule.php +++ b/application/admin/controller/auth/api/Rule.php @@ -3,6 +3,7 @@ namespace app\admin\controller\auth\api; use app\admin\model\api\AuthRule; +use app\admin\validate\ApiAuthRule; use app\common\controller\Backend; use fast\Tree; use think\Cache; @@ -116,11 +117,13 @@ class Rule extends Backend } } //这里需要针对name做唯一验证 - $ruleValidate = \think\Loader::validate('AuthRule'); - $ruleValidate->rule([ - 'name' => 'require|unique:AuthRule,name,' . $row->id, - ]); - $result = $row->validate()->save($params); +// $ruleValidate = \think\Loader::validate(ApiAuthRule::class); +// $ruleValidate->rule([ +// 'name' => 'require|unique:ApiAuthRule,name,' . $row->id, +// ]); +// $result = $row->validate($ruleValidate)->save($params); + $result = $row->save($params); + if ($result === false) { $this->error($row->getError()); } diff --git a/application/adminapi/controller/Admin.php b/application/adminapi/controller/Admin.php index a280f9b..e9ae706 100644 --- a/application/adminapi/controller/Admin.php +++ b/application/adminapi/controller/Admin.php @@ -4,6 +4,7 @@ namespace app\adminapi\controller; use app\adminapi\model\Admin as AdminModel; +use app\adminapi\model\AuthRule; use app\common\controller\AdminApi; use think\Cookie; use think\Hook; @@ -96,5 +97,53 @@ class Admin extends AdminApi } + /** + * 管理员菜单 + * + * @ApiMethod (GET) + * @ApiParams (name="is_tree", type="string", required=true, description="是否是树形结构") + */ + public function menu() + { + $admin_id = $this->auth->id; + $is_tree = $this->request->get('is_tree'); + + $menulist = (new AuthRule)->getMenulist($admin_id,["ismenu"=>1],$is_tree); + + $this->success('查询成功', $menulist); + } + + + /** + * 权限校验(接口校验版-用于前端自行显示隐藏) + * 返回null为无权限 + * @ApiMethod (GET) + * @ApiParams (name="auth_name", type="string", required=true, description="请求路径或权限标识") + */ + public function check_auth() + { + $admin_id = $this->auth->id; + $auth_name = $this->request->get('auth_name',"") ?: "***"; + $check = (new AuthRule)->authCheck($admin_id,$auth_name); + + $this->success('权限校验结果返回', $check); + } + + + + + /** + * 权限校验(直接返回拥有的所有权限,前端自行比对判断) + * + * @ApiMethod (GET) + */ + public function have_auth() + { + $admin_id = $this->auth->id; + $check = (new AuthRule)->getAllRules($admin_id); + + $this->success('权限列表返回', $check); + } + } \ No newline at end of file diff --git a/application/adminapi/controller/Rule.php b/application/adminapi/controller/Rule.php new file mode 100644 index 0000000..628663f --- /dev/null +++ b/application/adminapi/controller/Rule.php @@ -0,0 +1,80 @@ +model = new AuthRule; + parent::_initialize(); + } + + + + /** + * 所有权限列表 + * + * @ApiMethod (GET) + * @ApiParams (name="is_tree", type="string", required=true, description="是否是树形结构") + */ + public function rulelist() + { + $admin_id = $this->auth->id; + $is_tree = $this->request->get('is_tree'); + + $menulist = $this->model->getMenulist($admin_id,[],$is_tree,true); + + $this->success('查询成功', $menulist); + } + + + /** + * 添加权限节点(接口或菜单) + * + * @ApiMethod (POST) + * @ApiParams (name="pid", type="int", required=true, description="父ID") + * @ApiParams (name="ismenu", type="int", required=true, description="是否为菜单 0接口 1菜单") + * @ApiParams (name="name", type="string", required=true, description="节点URL(节点URL和外链选填其一)") + * @ApiParams (name="url", type="string", required=true, description="外链(节点URL和外链选填其一)") + * @ApiParams (name="rule_name", type="string", required=true, description="权限标识(菜单才需要)") + * @ApiParams (name="title", type="string", required=true, description="节点中文名") + * @ApiParams (name="icon", type="string", required=true, description="图标(菜单才需要)") + * @ApiParams (name="weigh", type="int", required=true, description="权重") + * @ApiParams (name="menutype", type="string", required=true, description="菜单类型:'addtabs','blank','dialog','ajax'") + * @ApiParams (name="extend", type="string", required=true, description="额外扩展属性(比如加类名做特特殊回调逻辑)") + * @ApiParams (name="status", type="string", required=true, description="状态(normal=正常,hidden=隐藏)") + * + */ + public function add() + { + $admin_id = $this->auth->id; + $params = $this->request->post(); + + $menulist = $this->model->add($params,true); + + $this->success('查询成功', $menulist); + } + + + + +} \ No newline at end of file diff --git a/application/adminapi/library/Auth.php b/application/adminapi/library/Auth.php index 744329d..eee4fd3 100644 --- a/application/adminapi/library/Auth.php +++ b/application/adminapi/library/Auth.php @@ -4,6 +4,8 @@ namespace app\adminapi\library; use app\admin\model\Admin; use app\admin\model\api\AuthRule; +use app\adminapi\model\AuthGroup; +use app\adminapi\model\AuthGroupAccess; use app\common\library\Token; use app\common\model\User; use fast\Random; @@ -90,6 +92,113 @@ class Auth } + public function isSuperAdmin() + { + return in_array('*', $this->getRuleIds()) ? true : false; + } + + public function getRuleIds($uid=null) + { + $uid = is_null($uid) ? $this->id : $uid; + //读取用户所属用户组 + $groups = $this->getGroups($uid); + $ids = []; //保存用户所属用户组设置的所有权限规则id + foreach ($groups as $g) { + $ids = array_merge($ids, explode(',', trim($g['rules'], ','))); + } + $ids = array_unique($ids); + return $ids; + } + + + public function getGroups($uid=null) + { + $uid = is_null($uid) ? $this->id : $uid; + //读取用户所属用户组 + $group_ids = AuthGroupAccess::where("uid",$uid)->column("group_id"); + $groups = []; + if($group_ids) $groups = AuthGroup::where('id', 'in', $group_ids)->select(); + return $groups; + } + + + /** + * 取出当前管理员所拥有权限的分组 + * @param boolean $withself 是否包含当前所在的分组 + * @return array + */ + public function getChildrenGroupIds($withself = false) + { + //取出当前管理员所有的分组 + $groups = $this->getGroups(); + $groupIds = []; + foreach ($groups as $k => $v) { + $groupIds[] = $v['id']; + } + $originGroupIds = $groupIds; + foreach ($groups as $k => $v) { + if (in_array($v['pid'], $originGroupIds)) { + $groupIds = array_diff($groupIds, [$v['id']]); + unset($groups[$k]); + } + } + // 取出所有分组 + $groupList = \app\adminapi\model\AuthGroup::where($this->isSuperAdmin() ? '1=1' : ['status' => 'normal'])->select(); + $objList = []; + foreach ($groups as $k => $v) { + if ($v['rules'] === '*') { + $objList = $groupList; + break; + } + // 取出包含自己的所有子节点 + $childrenList = Tree::instance()->init($groupList, 'pid')->getChildren($v['id'], true); + $obj = Tree::instance()->init($childrenList, 'pid')->getTreeArray($v['pid']); + $objList = array_merge($objList, Tree::instance()->getTreeList($obj)); + } + $childrenGroupIds = []; + foreach ($objList as $k => $v) { + $childrenGroupIds[] = $v['id']; + } + if (!$withself) { + $childrenGroupIds = array_diff($childrenGroupIds, $groupIds); + } + return $childrenGroupIds; + } + + /** + * 取出当前管理员所拥有权限的管理员 + * @param boolean $withself 是否包含自身 + * @return array + */ + public function getChildrenAdminIds($withself = false) + { + $childrenAdminIds = []; + if (!$this->isSuperAdmin()) { + $groupIds = $this->getChildrenGroupIds(false); + $authGroupList = \app\adminapi\model\AuthGroupAccess::field('uid,group_id') + ->where('group_id', 'in', $groupIds) + ->select(); + foreach ($authGroupList as $k => $v) { + $childrenAdminIds[] = $v['uid']; + } + } else { + //超级管理员拥有所有人的权限 + $childrenAdminIds = Admin::column('id'); + } + if ($withself) { + if (!in_array($this->id, $childrenAdminIds)) { + $childrenAdminIds[] = $this->id; + } + } else { + $childrenAdminIds = array_diff($childrenAdminIds, [$this->id]); + } + return $childrenAdminIds; + } + + + + + /** * 根据Token初始化 * @@ -344,8 +453,12 @@ class Auth foreach ($groups as $group){ $rules = array_merge($rules, explode(',', $group->rules)); } + if(in_array('*',$rules)){ + $this->rules = AuthRule::where('status', 'normal')->field('id,pid,rule_name,name,title,ismenu')->select(); + }else{ + $this->rules = AuthRule::where('status', 'normal')->where('id', 'in', $rules)->field('id,pid,rule_name,name,title,ismenu')->select(); + } - $this->rules = AuthRule::where('status', 'normal')->where('id', 'in', $rules)->field('id,pid,rule_name,name,title,ismenu')->select(); return $this->rules; } @@ -359,16 +472,21 @@ class Auth */ public function check($path = null, $module = null) { + if (!$this->_logined) { return false; } $ruleList = $this->getRuleList(); + + $rules = []; foreach ($ruleList as $k => $v) { $rules[] = $v['name']; } - $url = ($module ? $module : request()->module()) . '/' . (is_null($path) ? $this->getRequestUri() : $path); +// var_dump($rules); +// $url = ($module ? $module : request()->module()) . '/' . (is_null($path) ? $this->getRequestUri() : $path); + $url = (is_null($path) ? $this->getRequestUri() : $path); $url = strtolower(str_replace('.', '/', $url)); return in_array($url, $rules); } diff --git a/application/adminapi/model/AuthGroup.php b/application/adminapi/model/AuthGroup.php new file mode 100644 index 0000000..249d595 --- /dev/null +++ b/application/adminapi/model/AuthGroup.php @@ -0,0 +1,22 @@ + __('Addtabs'), 'dialog' => __('Dialog'), 'ajax' => __('Ajax'), 'blank' => __('Blank')]; + } + + public function setPyAttr($value, $data) + { + if (isset($data['title']) && $data['title']) { + return self::$pinyin->abbr(__($data['title'])); + } + return ''; + } + + public function setPinyinAttr($value, $data) + { + if (isset($data['title']) && $data['title']) { + return self::$pinyin->permalink(__($data['title']), ''); + } + return ''; + } + + + + /** + * 获取会员组别规则列表 + * @return array|bool|\PDOStatement|string|\think\Collection + */ + public function getRuleList($admin_id,$where=[],$full = false) + { + $group_ids = AuthGroupAccess::where("uid",$admin_id)->column("group_id"); + if(!$group_ids && !$full) return []; + $groups = AuthGroup::where('id', 'in', $group_ids)->select(); + if (!$groups && !$full ) { + return []; + } + $rules = []; + foreach ($groups as $group){ + $rules = array_merge($rules, explode(',', $group->rules)); + } + //包含*全查,否则按值查 + if ($full || in_array('*', $rules)) { + return AuthRule::where($where)->where('status', 'normal')->order('weigh desc,id desc')->select(); + } + return AuthRule::where($where)->where('status', 'normal')->where('id', 'in', $rules)->order('weigh desc,id desc')->select(); + } + + + //得到菜单列表 + public function getMenulist($admin_id,$where= ["ismenu"=>1],$is_tree=false,$full = false,$id_name='id',$pid_name='pid',$child_name='children') + { + $menu = $this->getRuleList($admin_id,$where,$full); + if(!$is_tree){ + return $menu; + } + // 生成菜单的树状结构 id pid ,pid=0为顶级菜单 + $menulist = []; + foreach ($menu as $k => $v) { + $v = $v->toArray(); + $v[$id_name] = $v['id']; + $v[$pid_name] = $v['pid']; + $menulist[$v[$id_name]] = $v; + } + $tree = []; + + foreach ($menulist as $k => $v) { + if (isset($menulist[$v[$pid_name]])) { + $menulist[$v[$pid_name]][$child_name][] = &$menulist[$v[$id_name]]; + } else { + $tree[] = &$menulist[$v[$id_name]]; + } + } + return $tree; + + + } + + + public function authCheck($admin_id,$auth_name) + { + $group_ids = AuthGroupAccess::where("uid",$admin_id)->column("group_id"); + if(!$group_ids) return null; + $groups = AuthGroup::where('id', 'in', $group_ids)->select(); + if (!$groups) { + return null; + } + $rules = []; + foreach ($groups as $group){ + $rules = array_merge($rules, explode(',', $group->rules)); + } + //包含*全查,否则按值查 + if (in_array('*', $rules)) { + return AuthRule::where('status', 'normal')->where( 'name|rule_name',$auth_name)->find() ; + } + return AuthRule::where('status', 'normal')->where( 'name|rule_name',$auth_name)->where('id', 'in', $rules)->find(); + + } + + public function getAllRules($admin_id) + { + $group_ids = AuthGroupAccess::where("uid",$admin_id)->column("group_id"); + if(!$group_ids) return null; + $groups = AuthGroup::where('id', 'in', $group_ids)->select(); + if (!$groups) { + return null; + } + $rules = []; + foreach ($groups as $group){ + $rules = array_merge($rules, explode(',', $group->rules)); + } + //包含*全查,否则按值查 + if (in_array('*', $rules)) { + return AuthRule::where('status', 'normal')->field("name,rule_name,ismenu")->select() ; + } + return AuthRule::where('status', 'normal')->where('id', 'in', $rules)->field("name,rule_name,ismenu")->select(); + + + } + + + + /** 通用新增(后台api版本) + * @param $params + * @param $trans + * @return $this + * @throws \Exception + */ + public function add($params,$trans=false){ + + if (empty($params)) { + throw new \Exception(__('Parameter %s can not be empty', '')); + } + + if ($this->dataLimit && $this->dataLimitFieldAutoFill) { + $params[$this->dataLimitField] = $this->auth->id; + } +//判断逻辑 + if($trans){ + self::beginTrans(); + } + $res = true; + try{ + + //是否采用模型验证 + if ($this->modelValidate) { + $name = str_replace("\\model\\", "\\validate\\", get_class($this)); + $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : $name) : $this->modelValidate; + $this->validateFailException()->validate($validate); + } + + + + $result = $this->allowField(true)->save($params); + + if($trans){ + self::commitTrans(); + } + }catch (\Exception $e){ + if($trans){ + self::rollbackTrans(); + } + throw new \Exception($e->getMessage().$e->getFile().$e->getLine()); + } + return $this; + } + + +} diff --git a/application/common/model/BaseModel.php b/application/common/model/BaseModel.php index 09f1620..b1c7aa4 100644 --- a/application/common/model/BaseModel.php +++ b/application/common/model/BaseModel.php @@ -32,6 +32,62 @@ class BaseModel extends Model + /** + * 是否开启Validate验证 + */ + protected $modelValidate = false; + + /** + * 是否开启模型场景验证 + */ + protected $modelSceneValidate = false; + + + + /** + * 是否开启数据限制 + * 支持auth/personal + * 表示按权限判断/仅限个人 + * 默认为禁用,若启用请务必保证表中存在admin_id字段 + */ + protected $dataLimit = false; + + + /** + * 数据限制字段 + */ + protected $dataLimitField = 'admin_id'; + + /** + * 数据限制开启时自动填充限制字段值 + */ + protected $dataLimitFieldAutoFill = true; + + + + + + /** + * 获取数据限制的管理员ID(后台api版本) + * 禁用数据限制时返回的是null + * @return mixed + */ + protected function getDataLimitAdminIds() + { + if (!$this->dataLimit) { + return null; + } + $auth = \app\adminapi\library\Auth::instance(); + if ($auth->isSuperAdmin()) { + return null; + } + $adminIds = []; + if (in_array($this->dataLimit, ['auth', 'personal'])) { + $adminIds = $this->dataLimit == 'auth' ? $auth->getChildrenAdminIds(true) : [$auth->id]; + } + return $adminIds; + } + public function checkAssemblyParameters($get=[],$exclude = []){ //得到所有get参数 @@ -288,4 +344,171 @@ class BaseModel extends Model } + /** 通用新增(后台api版本) + * @param $params + * @param $trans + * @return $this + * @throws \Exception + */ + public function add($params,$trans=false){ + + if (empty($params)) { + throw new \Exception(__('Parameter %s can not be empty', '')); + } + + if ($this->dataLimit && $this->dataLimitFieldAutoFill) { + $params[$this->dataLimitField] = $this->auth->id; + } +//判断逻辑 + if($trans){ + self::beginTrans(); + } + $res = true; + try{ + + //是否采用模型验证 + if ($this->modelValidate) { + $name = str_replace("\\model\\", "\\validate\\", get_class($this)); + $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.add' : $name) : $this->modelValidate; + $this->validateFailException()->validate($validate); + } + + $result = $this->allowField(true)->save($params); + + if($trans){ + self::commitTrans(); + } + }catch (\Exception $e){ + if($trans){ + self::rollbackTrans(); + } + throw new \Exception($e->getMessage().$e->getFile().$e->getLine()); + } + return $this; + } + + + /** 通用编辑(后台api版本) + * @param $params + * @param $trans + * @return $this + * @throws \Exception + */ + public function edit($id,$params,$trans=false){ + + $row = $this->get($id); + if (!$row) { + throw new \Exception(__('No Results were found')); + } + + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds) && !in_array($row[$this->dataLimitField], $adminIds)) { + throw new \Exception(__('You have no permission')); + } + + if (empty($params)) { + throw new \Exception(__('Parameter %s can not be empty', '')); + } +//判断逻辑 + if($trans){ + self::beginTrans(); + } + $res = true; + try{ + + //是否采用模型验证 + if ($this->modelValidate) { + $name = str_replace("\\model\\", "\\validate\\", get_class($this)); + $validate = is_bool($this->modelValidate) ? ($this->modelSceneValidate ? $name . '.edit' : $name) : $this->modelValidate; + $row->validateFailException()->validate($validate); + } + $result = $row->allowField(true)->save($params); + + if($trans){ + self::commitTrans(); + } + }catch (\Exception $e){ + if($trans){ + self::rollbackTrans(); + } + throw new \Exception($e->getMessage().$e->getFile().$e->getLine()); + } + return $row; + } + + + /** 通用详情(后台api版本) + * @param $params + * @param $trans + * @return $this + * @throws \Exception + */ + public function detail($id,$show_field=[],$except_field=[]){ + $row = $this->get($id); + if (!$row) { + throw new \Exception(__('No Results were found')); + } + if($show_field){ + $row->visible($show_field); + } + if($except_field){ + $row->hidden($except_field); + } + return $row; + } + +// public function index($page,$limit,$where=[]) +// { +// $adminIds = $this->getDataLimitAdminIds(); +// $aliasName = "" ; +// if (is_array($adminIds)) { +// $where[] = [$aliasName . $this->dataLimitField, 'in', $adminIds]; +// } +// +// } + + + /** 通用删除(后台api版本) + * @param $params + * @param $trans + * @return $this + * @throws \Exception + */ + public function del($ids = null,$trans=false){ + if (empty($ids)) { + throw new \Exception(__('Parameter %s can not be empty', 'ids')); + } +//判断逻辑 + + $pk = $this->getPk(); + $adminIds = $this->getDataLimitAdminIds(); + if (is_array($adminIds)) { + $this->where($this->dataLimitField, 'in', $adminIds); + } + $list = $this->where($pk, 'in', $ids)->select(); + $count = 0; + if($trans){ + self::beginTrans(); + } + $res = true; + try{ + + foreach ($list as $item) { + $count += $item->delete(); + } + + if($trans){ + self::commitTrans(); + } + }catch (\Exception $e){ + if($trans){ + self::rollbackTrans(); + } + throw new \Exception($e->getMessage().$e->getFile().$e->getLine()); + } + return $count; + } + + + } diff --git a/public/assets/js/backend/school/classes/classes_lib.js b/public/assets/js/backend/school/classes/classes_lib.js index 850586a..f8df6b9 100644 --- a/public/assets/js/backend/school/classes/classes_lib.js +++ b/public/assets/js/backend/school/classes/classes_lib.js @@ -37,7 +37,7 @@ define(['jquery', 'bootstrap', 'backend', 'csmtable', 'form'], function ($, unde {checkbox: true}, - {field: 'id', title: __('Id')}, + {field: 'id', title: __('Id') ,sortable:true}, {field: 'title', title: __('Title'), operate: 'LIKE', table: table, class: 'autocontent', formatter: Table.api.formatter.content}, {field: 'headimage', title: __('Headimage'), operate: false, events: Table.api.events.image, formatter: Table.api.formatter.image}, {field: 'has_expire', title: __('Has_expire'), searchList: {"1":__('Has_expire 1'),"2":__('Has_expire 2')}, formatter: Table.api.formatter.normal}, diff --git a/public/assets/js/manystore/school/classes/classes_lib.js b/public/assets/js/manystore/school/classes/classes_lib.js index 8da0adc..9c58878 100644 --- a/public/assets/js/manystore/school/classes/classes_lib.js +++ b/public/assets/js/manystore/school/classes/classes_lib.js @@ -36,7 +36,7 @@ define(['jquery', 'bootstrap', 'backend', 'csmtable', 'form'], function ($, unde [ {checkbox: true}, - {field: 'id', title: __('Id')}, + {field: 'id', title: __('Id') ,sortable:true}, {field: 'title', title: __('Title'), operate: 'LIKE', table: table, class: 'autocontent', formatter: Table.api.formatter.content}, {field: 'headimage', title: __('Headimage'), operate: false, events: Table.api.events.image, formatter: Table.api.formatter.image},