Backend.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. <?php
  2. namespace app\common\controller;
  3. use app\admin\library\Auth;
  4. use think\db\exception\PDOException;
  5. use think\exception\HttpResponseException;
  6. use think\facade\Cookie;
  7. use think\facade\Db;
  8. use think\facade\Event;
  9. class Backend extends Api
  10. {
  11. /**
  12. * 无需登录的方法
  13. * 访问本控制器的此方法,无需管理员登录
  14. */
  15. protected $noNeedLogin = [];
  16. /**
  17. * 无需鉴权的方法
  18. */
  19. protected $noNeedPermission = [];
  20. /**
  21. * 新增/编辑时,对前端发送的字段进行排除(忽略不入库)
  22. */
  23. protected $preExcludeFields = [];
  24. /**
  25. * 权限类实例
  26. * @var Auth
  27. */
  28. protected $auth = null;
  29. protected $model = null;
  30. /**
  31. * 权重字段
  32. */
  33. protected $weighField = 'weigh';
  34. /**
  35. * 默认排序
  36. */
  37. protected $defaultSortField = 'id,desc';
  38. /**
  39. * 表格拖拽排序时,两个权重相等则自动重新整理
  40. * config/buildadmin.php文件中的auto_sort_eq_weight为默认值
  41. * null=取默认值,false=关,true=开
  42. */
  43. protected $autoSortEqWeight = null;
  44. /**
  45. * 快速搜索字段
  46. */
  47. protected $quickSearchField = 'id';
  48. /**
  49. * 是否开启模型验证
  50. */
  51. protected $modelValidate = true;
  52. /**
  53. * 是否开启模型场景验证
  54. */
  55. protected $modelSceneValidate = false;
  56. /**
  57. * 关联查询方法名
  58. * 方法应定义在模型中
  59. */
  60. protected $withJoinTable = [];
  61. /**
  62. * 关联查询JOIN方式
  63. */
  64. protected $withJoinType = 'LEFT';
  65. /**
  66. * 开启数据限制
  67. * false=关闭
  68. * personal=仅限个人
  69. * allAuth=拥有某管理员所有的权限时
  70. * allAuthAndOthers=拥有某管理员所有的权限并且还有其他权限时
  71. * parent=上级分组中的管理员可查
  72. * 指定分组中的管理员可查,比如 $dataLimit = 2;
  73. * 启用请确保数据表内存在 admin_id 字段,可以查询/编辑数据的管理员为admin_id对应的管理员+数据限制所表示的管理员们
  74. */
  75. protected $dataLimit = false;
  76. /**
  77. * 数据限制字段
  78. */
  79. protected $dataLimitField = 'admin_id';
  80. /**
  81. * 数据限制开启时自动填充字段值为当前管理员id
  82. */
  83. protected $dataLimitFieldAutoFill = true;
  84. /**
  85. * 查看请求返回的主表字段控制
  86. */
  87. protected $indexField = ['*'];
  88. /**
  89. * 引入traits
  90. * traits内实现了index、add、edit等方法
  91. */
  92. use \app\admin\library\traits\Backend;
  93. public function initialize()
  94. {
  95. parent::initialize();
  96. // 检测数据库连接
  97. try {
  98. Db::execute("SELECT 1");
  99. } catch (PDOException $e) {
  100. $this->error(mb_convert_encoding($e->getMessage(), 'UTF-8', 'UTF-8,GBK,GB2312,BIG5'));
  101. }
  102. $this->auth = Auth::instance();
  103. $routePath = $this->app->request->controllerPath . '/' . $this->request->action(true);
  104. $token = $this->request->server('HTTP_BATOKEN', $this->request->request('batoken', Cookie::get('batoken') ?: false));
  105. if (!action_in_arr($this->noNeedLogin)) {
  106. $this->auth->init($token);
  107. if (!$this->auth->isLogin()) {
  108. $this->error(__('Please login first'), [
  109. 'routePath' => '/admin/login'
  110. ], 302);
  111. }
  112. if (!action_in_arr($this->noNeedPermission)) {
  113. if (!$this->auth->check($routePath)) {
  114. $this->error(__('You have no permission'), [], 401);
  115. }
  116. }
  117. } elseif ($token) {
  118. try {
  119. $this->auth->init($token);
  120. } catch (HttpResponseException $e) {
  121. }
  122. }
  123. // 管理员验权和登录标签位
  124. Event::trigger('backendInit', $this->auth);
  125. }
  126. public function queryBuilder(): array
  127. {
  128. if (empty($this->model)) {
  129. return [];
  130. }
  131. $pk = $this->model->getPk();
  132. $quickSearch = $this->request->get("quick_search/s", '');
  133. $limit = $this->request->get("limit/d", 10);
  134. $order = $this->request->get("order/s", '');
  135. $search = $this->request->get("search/a", []);
  136. $initKey = $this->request->get("initKey/s", $pk);
  137. $initValue = $this->request->get("initValue/a", '');
  138. $where = [];
  139. $modelTable = strtolower($this->model->getTable());
  140. $alias[$modelTable] = parse_name(basename(str_replace('\\', '/', get_class($this->model))));
  141. $tableAlias = $alias[$modelTable] . '.';
  142. // 快速搜索
  143. if ($quickSearch) {
  144. $quickSearchArr = is_array($this->quickSearchField) ? $this->quickSearchField : explode(',', $this->quickSearchField);
  145. foreach ($quickSearchArr as $k => $v) {
  146. $quickSearchArr[$k] = stripos($v, ".") === false ? $tableAlias . $v : $v;
  147. }
  148. $where[] = [implode("|", $quickSearchArr), "LIKE", "%{$quickSearch}%"];
  149. }
  150. if ($initValue) {
  151. $where[] = [$initKey, 'in', $initValue];
  152. $limit = 999999;
  153. }
  154. // 排序
  155. if ($order) {
  156. $order = explode(',', $order);
  157. if (isset($order[0]) && isset($order[1]) && ($order[1] == 'asc' || $order[1] == 'desc')) {
  158. $order = [$order[0] => $order[1]];
  159. }
  160. } else {
  161. if (is_array($this->defaultSortField)) {
  162. $order = $this->defaultSortField;
  163. } else {
  164. $order = explode(',', $this->defaultSortField);
  165. if (isset($order[0]) && isset($order[1])) {
  166. $order = [$order[0] => $order[1]];
  167. } else {
  168. $order = [$pk => 'desc'];
  169. }
  170. }
  171. }
  172. // 通用搜索组装
  173. foreach ($search as $field) {
  174. if (!is_array($field) || !isset($field['operator']) || !isset($field['field']) || !isset($field['val'])) {
  175. continue;
  176. }
  177. if (stripos($field['field'], '.') !== false) {
  178. $fieldArr = explode('.', $field['field']);
  179. $alias[$fieldArr[0]] = $fieldArr[0];
  180. $fieldName = $field['field'];
  181. } else {
  182. $fieldName = $tableAlias . $field['field'];
  183. }
  184. // 日期时间
  185. if (isset($field['render']) && $field['render'] == 'datetime') {
  186. if ($field['operator'] == 'RANGE') {
  187. $datetimeArr = explode(',', $field['val']);
  188. if (!isset($datetimeArr[1])) {
  189. continue;
  190. }
  191. $datetimeArr = array_filter(array_map("strtotime", $datetimeArr));
  192. $where[] = [$fieldName, str_replace('RANGE', 'BETWEEN', $field['operator']), $datetimeArr];
  193. continue;
  194. }
  195. $where[] = [$fieldName, '=', strtotime($field['val'])];
  196. continue;
  197. }
  198. // 范围查询
  199. if ($field['operator'] == 'RANGE' || $field['operator'] == 'NOT RANGE') {
  200. if (stripos($field['val'], ',') === false) {
  201. continue;
  202. }
  203. $arr = explode(',', $field['val']);
  204. // 重新确定操作符
  205. if (!isset($arr[0]) || $arr[0] === '') {
  206. $operator = $field['operator'] == 'RANGE' ? '<=' : '>';
  207. $arr = $arr[1];
  208. } elseif (!isset($arr[1]) || $arr[1] === '') {
  209. $operator = $field['operator'] == 'RANGE' ? '>=' : '<';
  210. $arr = $arr[0];
  211. } else {
  212. $operator = str_replace('RANGE', 'BETWEEN', $field['operator']);
  213. }
  214. $where[] = [$fieldName, $operator, $arr];
  215. continue;
  216. }
  217. switch ($field['operator']) {
  218. case '=':
  219. case '<>':
  220. $where[] = [$fieldName, $field['operator'], (string)$field['val']];
  221. break;
  222. case 'LIKE':
  223. case 'NOT LIKE':
  224. $where[] = [$fieldName, $field['operator'], "%{$field['val']}%"];
  225. break;
  226. case '>':
  227. case '>=':
  228. case '<':
  229. case '<=':
  230. $where[] = [$fieldName, $field['operator'], intval($field['val'])];
  231. break;
  232. case 'FIND_IN_SET':
  233. if (is_array($field['val'])) {
  234. foreach ($field['val'] as $val) {
  235. $where[] = [$fieldName, 'find in set', $val];
  236. }
  237. } else {
  238. $where[] = [$fieldName, 'find in set', $field['val']];
  239. }
  240. break;
  241. case 'IN':
  242. case 'NOT IN':
  243. $where[] = [$fieldName, $field['operator'], is_array($field['val']) ? $field['val'] : explode(',', $field['val'])];
  244. break;
  245. case 'NULL':
  246. case 'NOT NULL':
  247. $where[] = [$fieldName, strtolower($field['operator']), ''];
  248. break;
  249. }
  250. }
  251. // 数据权限
  252. $dataLimitAdminIds = $this->getDataLimitAdminIds();
  253. if ($dataLimitAdminIds) {
  254. $where[] = [$tableAlias . $this->dataLimitField, 'in', $dataLimitAdminIds];
  255. }
  256. return [$where, $alias, $limit, $order];
  257. }
  258. protected function getDataLimitAdminIds(): array
  259. {
  260. if (!$this->dataLimit || $this->auth->isSuperAdmin()) {
  261. return [];
  262. }
  263. $adminIds = [];
  264. if ($this->dataLimit == 'parent') {
  265. // 取得当前管理员的下级分组们
  266. $parentGroups = $this->auth->getAdminChildGroups();
  267. if ($parentGroups) {
  268. // 取得分组内的所有管理员
  269. $adminIds = $this->auth->getGroupAdmins($parentGroups);
  270. }
  271. } elseif (is_numeric($this->dataLimit) && $this->dataLimit > 0) {
  272. // 在组内,可查看所有,不在组内,可查看自己的
  273. $adminIds = $this->auth->getGroupAdmins([$this->dataLimit]);
  274. return in_array($this->auth->id, $adminIds) ? [] : [$this->auth->id];
  275. } elseif ($this->dataLimit == 'allAuth' || $this->dataLimit == 'allAuthAndOthers') {
  276. // 取得拥有他所有权限的分组
  277. $allAuthGroups = $this->auth->getAllAuthGroups($this->dataLimit);
  278. // 取得分组内的所有管理员
  279. $adminIds = $this->auth->getGroupAdmins($allAuthGroups);
  280. }
  281. $adminIds[] = $this->auth->id;
  282. return array_unique($adminIds);
  283. }
  284. }