Table.php 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
  1. <?php
  2. namespace app\admin\controller\famysql;
  3. use app\common\controller\Backend;
  4. use addons\famysql\library\Backup;
  5. use think\Db;
  6. use think\Config;
  7. use think\Exception;
  8. use think\exception\PDOException;
  9. use ZipArchive;
  10. /**
  11. * 数据库管理
  12. *
  13. * @icon fa fa-database
  14. * @remark 可在线进行数据库表优化或修复,查看表结构和数据等
  15. */
  16. class Table extends Backend
  17. {
  18. protected $dbName = '';
  19. protected $prefix = '';
  20. protected $noNeedRight = ['selectnames', 'getCollation', 'get_table_list', 'check'];
  21. /**
  22. * 读取字符集
  23. * @return array
  24. */
  25. protected $charsetList = ['utf8mb4', 'utf8', 'latin1', 'utf16'];
  26. /**
  27. * 读取排序规则
  28. * @return array
  29. */
  30. protected $collationList = [
  31. 'utf8mb4' => ['utf8mb4_general_ci', 'utf8mb4_unicode_ci'],
  32. 'utf8' => ['utf8_general_ci', 'utf8_unicode_ci'],
  33. 'latin1' => ['latin1_general_ci'],
  34. 'utf16' => ['utf16_general_ci', 'utf16_unicode_ci'],
  35. ];
  36. public function _initialize()
  37. {
  38. parent::_initialize();
  39. if (!config("app_debug")) {
  40. $this->error("数据库管理插件只允许在开发环境下使用");
  41. }
  42. if (!$this->auth->isSuperAdmin()) {
  43. $this->error(__('Access is allowed only to the super management group'));
  44. }
  45. $this->dbName = Config::get("database.database");
  46. $this->prefix = Config::get('database.prefix');
  47. $this->view->assign("charsetList", $this->charsetList);
  48. $this->view->assign("groups", $this->getGroups(true));
  49. $this->view->assign("groupsList", $this->getGroups());
  50. }
  51. /**
  52. * 查看
  53. */
  54. public function index()
  55. {
  56. $group = $this->request->get("group");
  57. $offset = $this->request->get("offset");
  58. $limit = $this->request->get("limit");
  59. $config = get_addon_config('famysql');
  60. if ($this->request->isAjax()) {
  61. $group = $group ?? 'system';
  62. $tables = $this->getTables($group);
  63. $list = [];
  64. if (count($tables) > 0) {
  65. $tableInfos = [];
  66. foreach ($tables as $k => $v) {
  67. $tableInfos[] = Db::table("information_schema.TABLES")->field("*")->where(['TABLE_SCHEMA' => $this->dbName, 'TABLE_NAME' => $v])->find();
  68. }
  69. $i = 1;
  70. foreach ($tableInfos as $key => $tableInfo) {
  71. $list[$key]['id'] = $i++;
  72. $list[$key]['group'] = $group;
  73. $list[$key]['is_admin'] = ($group == 'system' && !$config['is_admin']) ? 0 : 1;
  74. $list[$key]['is_has'] = $this->prefix !== '' ? 1 : 0;
  75. $list[$key]['name'] = $tableInfo['TABLE_NAME'];
  76. $list[$key]['engine'] = $tableInfo['ENGINE'];
  77. $list[$key]['rows'] = Db::table($tableInfo['TABLE_NAME'])->count();
  78. $list[$key]['field_nums'] = count(Db::getFields($tableInfo['TABLE_NAME']));
  79. $list[$key]['charset'] = substr($tableInfo['TABLE_COLLATION'], 0, strpos($tableInfo['TABLE_COLLATION'], '_'));
  80. $list[$key]['collation'] = $tableInfo['TABLE_COLLATION'];
  81. $list[$key]['comment'] = $tableInfo['TABLE_COMMENT'];
  82. $list[$key]['createtime'] = $tableInfo['CREATE_TIME'];
  83. $list[$key]['updatetime'] = $tableInfo['UPDATE_TIME'];
  84. }
  85. }
  86. $result = array("total" => count($list), "rows" => array_slice($list, $offset, $limit));
  87. return json($result);
  88. }
  89. $this->view->assign("group", $group);
  90. $this->assignconfig("group", $group);
  91. return $this->view->fetch();
  92. }
  93. /**
  94. * 添加
  95. */
  96. public function table_add()
  97. {
  98. $group = $this->request->get("group");
  99. if ($this->request->isPost()) {
  100. $params = $this->request->post("row/a");
  101. if ($params) {
  102. $result = false;
  103. $sql = [];
  104. $name = $this->prefix . $params['addon'] . '_' . $params['name'];
  105. Db::startTrans();
  106. try {
  107. $sql = "SHOW TABLES LIKE '{$name}'";
  108. $result = Db::query($sql);
  109. if ($result) {
  110. $this->error("表 {$name} 已存在于数据库 {$this->dbName} 中");
  111. } else {
  112. //在此执行创建表的操作
  113. $sql = "CREATE TABLE IF NOT EXISTS `{$name}` (
  114. `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
  115. PRIMARY KEY (`id`)
  116. ) ENGINE={$params['engine']} DEFAULT CHARSET={$params['charset']} COLLATE={$params['collation']} COMMENT='" . $params['comment'] . "';";
  117. $result = Db::execute($sql);
  118. }
  119. if (Db::getPdo()->inTransaction() == true) {
  120. Db::commit();
  121. }
  122. $this->success();
  123. } catch (\think\exception\PDOException $e) {
  124. Db::rollback();
  125. $this->error($e->getMessage());
  126. } catch (\think\Exception $e) {
  127. Db::rollback();
  128. $this->error($e->getMessage());
  129. }
  130. if ($result !== false) {
  131. $this->success();
  132. } else {
  133. $this->error(__('No rows were inserted'));
  134. }
  135. }
  136. }
  137. $this->view->assign("group", $group);
  138. return $this->view->fetch();
  139. }
  140. /**
  141. * 快速建表
  142. */
  143. public function table_batch_add()
  144. {
  145. $group = $this->request->get("group");
  146. if ($this->request->isPost()) {
  147. $params = $this->request->post("row/a");
  148. if ($params) {
  149. $result = false;
  150. $sql = [];
  151. $prefix = $this->prefix . $params['addon'] . '_';
  152. Db::startTrans();
  153. try {
  154. $templates = $this->template();
  155. $names = explode(',', $params['name']);
  156. foreach ($templates as $template) {
  157. if (in_array($template['table_name'], $names)) {
  158. $sql[] = str_replace("__PREFIX__", $prefix, $template['sql']) . ";";
  159. }
  160. }
  161. $result = Db::batchQuery($sql);
  162. if (!$result) {
  163. $this->error();
  164. }
  165. if (Db::getPdo()->inTransaction() == true) {
  166. Db::commit();
  167. }
  168. $this->success();
  169. } catch (\think\exception\PDOException $e) {
  170. Db::rollback();
  171. $this->error($e->getMessage());
  172. } catch (\think\Exception $e) {
  173. Db::rollback();
  174. $this->error($e->getMessage());
  175. }
  176. if ($result !== false) {
  177. $this->success();
  178. } else {
  179. $this->error(__('No rows were inserted'));
  180. }
  181. }
  182. $this->error(__('Parameter %s can not be empty', ''));
  183. }
  184. $this->view->assign("group", $group);
  185. return $this->view->fetch();
  186. }
  187. /**
  188. * 备份列表
  189. */
  190. public function backuplist()
  191. {
  192. $group = $this->request->get("group");
  193. $offset = $this->request->get("offset");
  194. $limit = $this->request->get("limit");
  195. if ($this->request->isAjax()) {
  196. $filter = $this->request->request("filter", '', 'trim');
  197. $filter = (array) json_decode($filter, true);
  198. $addon = !isset($filter['addon']) ? 'all' : $filter['addon'];
  199. $type = !isset($filter['type']) ? 'all' : $filter['type'];
  200. $backupDir = ADDON_PATH . 'famysql' . DS . 'backup' . DS;
  201. $backuplist = [];
  202. $files = [];
  203. foreach (glob($backupDir . "*.*") as $key => $filename) {
  204. $basename = basename($filename);
  205. $file_arr = stripos($basename, '-') !== FALSE ? explode('-', $basename) : $basename;
  206. $_addon = (is_array($file_arr) && $file_arr[0] == 'backup') ? $file_arr[2] : 'all';
  207. $_type = (is_array($file_arr) && $file_arr[0] == 'backup') ? $file_arr[3] : 'all';
  208. $time = filemtime($filename);
  209. if (!in_array($basename, $files)) {
  210. $backuplist[$time] =
  211. [
  212. 'file' => $basename,
  213. 'addon' => $_addon,
  214. 'addon_name' => $_addon !== 'all' ? get_addon_info($_addon)['title'] : '全部',
  215. 'type' => $_type,
  216. 'date' => date("Y-m-d H:i:s", $time),
  217. 'size' => format_bytes(filesize($filename))
  218. ];
  219. array_push($files, $basename);
  220. if ($addon !== 'all' && $addon !== $_addon) {
  221. unset($backuplist[$time]);
  222. } elseif ($type !== 'all' && $type !== $_type) {
  223. unset($backuplist[$time]);
  224. }
  225. }
  226. }
  227. krsort($backuplist);
  228. $result = array("total" => count($backuplist), "rows" => array_slice($backuplist, $offset, $limit));
  229. return json($result);
  230. }
  231. $this->view->assign("group", $group);
  232. $this->assignconfig("group", $group);
  233. return $this->view->fetch();
  234. }
  235. /**
  236. * 备份下载
  237. */
  238. public function download()
  239. {
  240. $file = $this->request->request('file');
  241. $backupDir = ADDON_PATH . 'famysql' . DS . 'backup' . DS;
  242. if (!preg_match("/^backup\-([a-z0-9\-_\.]+)\.zip$/i", $file)) {
  243. $this->error(__("Invalid parameters"));
  244. }
  245. $file = $backupDir . $file;
  246. if (!is_file($file)) {
  247. $this->error(__('File not found'));
  248. } else {
  249. header('Content-Type:text/html;charset=utf-8');
  250. header('Content-disposition:attachment; filename=' . basename($file));
  251. $result = readfile($file);
  252. header('Content-length:' . filesize($file));
  253. $this->success(__('Download completed'));
  254. }
  255. }
  256. /**
  257. * 恢复
  258. */
  259. public function restore($ids = '')
  260. {
  261. $backupDir = ADDON_PATH . 'famysql' . DS . 'backup' . DS;
  262. if ($this->request->isPost()) {
  263. $action = $this->request->request('action');
  264. $file = $this->request->request('file');
  265. if (!preg_match("/\.(zip|sql?)$/", $file)) {
  266. $this->error(__("Invalid parameters"));
  267. }
  268. $file = $backupDir . $file;
  269. $ext = pathinfo($file, PATHINFO_EXTENSION);
  270. if ($action == 'restore') {
  271. if (!class_exists('ZipArchive')) {
  272. $this->error(__("Zip tips 1"));
  273. }
  274. try {
  275. if ($ext == 'zip') {
  276. $dir = RUNTIME_PATH . 'database' . DS;
  277. if (!is_dir($dir)) {
  278. @mkdir($dir, 0755);
  279. }
  280. $zip = new ZipArchive;
  281. if ($zip->open($file) !== true) {
  282. throw new Exception(__('Can not open zip file'));
  283. }
  284. if (!$zip->extractTo($dir)) {
  285. $zip->close();
  286. throw new Exception(__('Can not unzip file'));
  287. }
  288. $zip->close();
  289. $filename = basename($file);
  290. $sqlFile = $dir . str_replace('.zip', '.sql', $filename);
  291. } else {
  292. $sqlFile = $file;
  293. }
  294. if (!is_file($sqlFile)) {
  295. throw new Exception(__('Sql file not found'));
  296. }
  297. $filesize = filesize($sqlFile);
  298. $list = Db::query('SELECT @@global.max_allowed_packet');
  299. if (isset($list[0]['@@global.max_allowed_packet']) && $filesize >= $list[0]['@@global.max_allowed_packet']) {
  300. Db::execute('SET @@global.max_allowed_packet = ' . ($filesize + 1024));
  301. //throw new Exception('备份文件超过配置max_allowed_packet大小,请修改Mysql服务器配置');
  302. }
  303. $sql = file_get_contents($sqlFile);
  304. Db::clear();
  305. //必须重连一次
  306. Db::connect([], true)->query("select 1");
  307. Db::getPdo()->exec($sql);
  308. } catch (Exception $e) {
  309. $this->error($e->getMessage());
  310. } catch (PDOException $e) {
  311. $this->error($e->getMessage());
  312. }
  313. $this->success(__('Restore successful'));
  314. } elseif ($action == 'delete') {
  315. unlink($file);
  316. $this->success(__('Delete successful'));
  317. }
  318. }
  319. }
  320. /**
  321. * 备份
  322. */
  323. public function backup()
  324. {
  325. $group = $this->request->get("group");
  326. $backupDir = ADDON_PATH . 'famysql' . DS . 'backup' . DS;
  327. if ($this->request->isPost()) {
  328. $params = $this->request->post('row/a');
  329. $tableList = [];
  330. $list = \think\Db::query("SHOW TABLES");
  331. foreach ($list as $key => $row) {
  332. if ($params['addon'] == 'all') {
  333. $tableList[] = reset($row);
  334. } else {
  335. $tmp = explode('_', reset($row));
  336. if ($this->prefix !== '' && $tmp[1] == $params['addon']) {
  337. $tableList[] = reset($row);
  338. } elseif ($this->prefix == '' && $tmp[0] == $params['addon']) {
  339. $tableList[] = reset($row);
  340. }
  341. }
  342. }
  343. if (!class_exists('ZipArchive')) {
  344. $this->error(__("Zip tips 2"));
  345. }
  346. $database = config('database');
  347. try {
  348. $backup = new Backup($database['hostname'], $database['username'], $database['database'], $database['password'], $database['hostport']);
  349. $backup->setTable($tableList)->setIgnoreTable($params['ignore_tables'])->backup($params['addon'], $params['type'], $backupDir);
  350. } catch (Exception $e) {
  351. $this->error($e->getMessage());
  352. }
  353. $this->success(__('Backup successful'));
  354. }
  355. $this->view->assign("group", $group);
  356. return $this->view->fetch();
  357. }
  358. /**
  359. * 上传文件
  360. */
  361. public function upload()
  362. {
  363. $group = $this->request->get("group");
  364. //默认普通上传文件
  365. $file = $this->request->file('file');
  366. $backupDir = ADDON_PATH . 'famysql' . DS . 'backup' . DS;
  367. if ($file) {
  368. try {
  369. $info = $file->rule('uniqid')->move($backupDir, $file->getInfo()['name']);
  370. if ($info) {
  371. $this->success(__('Uploaded successful'));
  372. }
  373. } catch (Exception $e) {
  374. $this->error($file->getError());
  375. }
  376. }
  377. }
  378. /**
  379. * 字段选择
  380. * @internal
  381. */
  382. public function selectnames()
  383. {
  384. //当前页
  385. $page = $this->request->request("pageNumber");
  386. //分页大小
  387. $pagesize = $this->request->request("pageSize");
  388. $q_word = (array) $this->request->request("q_word/a");
  389. $word = $q_word[0];
  390. $custom = (array) $this->request->request("custom/a");
  391. if ($custom && is_array($custom)) {
  392. $addon = $custom['addon'];
  393. }
  394. $tables = $this->template();
  395. if (!empty($word)) {
  396. $res_arr = [];
  397. foreach ($tables as $table) {
  398. if (!in_array($this->prefix . $addon . '_' . $table['table_name'], $this->getTables($addon))) {
  399. $res_arr[] = $table['table_name'] . '-' . $table['comment'];
  400. }
  401. }
  402. $res_arr = array_filter($res_arr, function ($v) use ($word) {
  403. return stripos($v, $word) !== false;
  404. });
  405. $res_arrs = array_values($res_arr);
  406. $tableLists_arr = [];
  407. foreach ($res_arrs as $res) {
  408. $tableLists_arr[] = [
  409. 'table_name' => explode('-', $res)[0],
  410. 'comment' => explode('-', $res)[1]
  411. ];
  412. }
  413. $tables = $tableLists_arr;
  414. } else {
  415. $res_arr = [];
  416. foreach ($tables as $table) {
  417. if (!in_array($this->prefix . $addon . '_' . $table['table_name'], $this->getTables($addon))) {
  418. $res_arr[] = $table['table_name'] . '-' . $table['comment'];
  419. }
  420. }
  421. $res_arrs = array_values($res_arr);
  422. $tableLists_arr = [];
  423. foreach ($res_arrs as $res) {
  424. $tableLists_arr[] = [
  425. 'table_name' => explode('-', $res)[0],
  426. 'comment' => explode('-', $res)[1]
  427. ];
  428. }
  429. $tables = $tableLists_arr;
  430. }
  431. $result = array("total" => count($tables), "list" => array_slice($tables, ($page - 1) * $pagesize, $pagesize));
  432. return json($result);
  433. }
  434. /**
  435. * 编辑
  436. */
  437. public function table_edit()
  438. {
  439. $name = $this->request->get('name');
  440. if ($name == NULL) {
  441. $this->error(__('Parameter %s can not be empty', 'name'));
  442. }
  443. $tableInfo = Db::table("information_schema.TABLES")->field("*")->where(['TABLE_SCHEMA' => $this->dbName, 'TABLE_NAME' => $name])->find();
  444. $row['name'] = $tableInfo['TABLE_NAME'];
  445. $row['engine'] = $tableInfo['ENGINE'];
  446. $row['charset'] = substr($tableInfo['TABLE_COLLATION'], 0, strpos($tableInfo['TABLE_COLLATION'], '_'));
  447. $row['collation'] = $tableInfo['TABLE_COLLATION'];
  448. $row['comment'] = $tableInfo['TABLE_COMMENT'];
  449. if ($this->request->isPost()) {
  450. $params = $this->request->post("row/a");
  451. if ($params) {
  452. $result = false;
  453. $sql = [];
  454. Db::startTrans();
  455. try {
  456. if ($params['comment'] != $row['comment'])
  457. $sql[] = "ALTER TABLE `{$name}` COMMENT='{$params['comment']}'";
  458. if ($params['engine'] != $row['engine'])
  459. $sql[] = "ALTER TABLE `{$name}` ENGINE='{$params['engine']}'";
  460. if ($params['charset'] != $row['charset'])
  461. $sql[] = "ALTER TABLE `{$name}` CONVERT TO CHARACTER SET '{$params['charset']}' COLLATE '{$params['collation']}'";
  462. if ($params['collation'] != $row['collation'])
  463. $sql[] = "ALTER TABLE `{$name}` CONVERT TO CHARACTER SET '{$params['charset']}' COLLATE '{$params['collation']}'";
  464. if ($params['name'] != $row['name'])
  465. $sql[] = "ALTER TABLE `{$name}` RENAME TO `{$params['name']}`;";
  466. $result = Db::batchQuery($sql);
  467. if (Db::getPdo()->inTransaction() == true) {
  468. Db::commit();
  469. }
  470. } catch (\think\exception\PDOException $e) {
  471. Db::rollback();
  472. $this->error($e->getMessage());
  473. } catch (\think\Exception $e) {
  474. Db::rollback();
  475. $this->error($e->getMessage());
  476. }
  477. if ($result !== false) {
  478. $this->success();
  479. } else {
  480. $this->error(__('No rows were inserted'));
  481. }
  482. }
  483. $this->error(__('Parameter %s can not be empty', ''));
  484. }
  485. $this->view->assign("row", $row);
  486. return $this->view->fetch();
  487. }
  488. /**
  489. * 删除
  490. */
  491. public function table_del()
  492. {
  493. $name = $this->request->get('name');
  494. if ($name == NULL) {
  495. $this->error(__('Parameter %s can not be empty', 'name'));
  496. }
  497. $result = false;
  498. Db::startTrans();
  499. try {
  500. $sql = "DROP TABLE IF EXISTS `{$name}`;";
  501. $result = Db::execute($sql);
  502. if (Db::getPdo()->inTransaction() == true) {
  503. Db::commit();
  504. }
  505. } catch (\think\exception\PDOException $e) {
  506. Db::rollback();
  507. $this->error($e->getMessage());
  508. } catch (\think\Exception $e) {
  509. Db::rollback();
  510. $this->error($e->getMessage());
  511. }
  512. if ($result !== false) {
  513. $group = $this->prefix !== '' ? explode('_', $name)[1] : explode('_', $name)[0];
  514. $tables = $this->getTables($group);
  515. $this->success('删除成功', null, count($tables));
  516. } else {
  517. $this->error(__('No rows were deleted'));
  518. }
  519. }
  520. /**
  521. * 添加
  522. * @internal
  523. */
  524. public function add()
  525. {
  526. $this->error('禁止访问');
  527. }
  528. /**
  529. * 编辑
  530. * @param string $ids
  531. * @internal
  532. */
  533. public function edit($ids = null)
  534. {
  535. $this->error('禁止访问');
  536. }
  537. /**
  538. * 删除
  539. * @param string $ids
  540. * @internal
  541. */
  542. public function del($ids = null)
  543. {
  544. $this->error('禁止访问');
  545. }
  546. /**
  547. * 批量更新
  548. * @internal
  549. * @param string $ids
  550. * @return void
  551. */
  552. public function multi($ids = null)
  553. {
  554. $this->error('禁止访问');
  555. }
  556. /**
  557. * 截/断表
  558. */
  559. public function truncate()
  560. {
  561. $name = $this->request->get('name');
  562. if ($name == NULL) {
  563. $this->error(__('Parameter %s can not be empty', $name));
  564. }
  565. $result = false;
  566. Db::startTrans();
  567. try {
  568. $sql = "TRUNCATE TABLE `{$name}`;";
  569. $result = Db::execute($sql);
  570. if (Db::getPdo()->inTransaction() == true) {
  571. Db::commit();
  572. }
  573. } catch (\think\exception\PDOException $e) {
  574. Db::rollback();
  575. $this->error($e->getMessage());
  576. } catch (\think\Exception $e) {
  577. Db::rollback();
  578. $this->error($e->getMessage());
  579. }
  580. if ($result !== false) {
  581. $this->success(__('Truncate table %s done', $name));
  582. } else {
  583. $this->error(__('Truncate table %s fail', $name));
  584. }
  585. }
  586. /**
  587. * 优化表
  588. */
  589. public function optimize()
  590. {
  591. $name = $this->request->get('name');
  592. if ($name == NULL) {
  593. $this->error(__('Parameter %s can not be empty', $name));
  594. }
  595. $result = false;
  596. Db::startTrans();
  597. try {
  598. $sql = "OPTIMIZE TABLE `{$name}`;";
  599. $result = Db::execute($sql);
  600. if (Db::getPdo()->inTransaction() == true) {
  601. Db::commit();
  602. }
  603. } catch (\think\exception\PDOException $e) {
  604. Db::rollback();
  605. $this->error($e->getMessage());
  606. } catch (\think\Exception $e) {
  607. Db::rollback();
  608. $this->error($e->getMessage());
  609. }
  610. if ($result !== false) {
  611. $this->success(__('Optimize table %s done', $name));
  612. } else {
  613. $this->error(__('Optimize table %s fail', $name));
  614. }
  615. }
  616. /**
  617. * 修复表
  618. */
  619. public function repair()
  620. {
  621. $name = $this->request->get('name');
  622. if ($name == NULL) {
  623. $this->error(__('Parameter %s can not be empty', $name));
  624. }
  625. $result = false;
  626. Db::startTrans();
  627. try {
  628. $sql = "REPAIR TABLE `{$name}`;";
  629. $result = Db::execute($sql);
  630. if (Db::getPdo()->inTransaction() == true) {
  631. Db::commit();
  632. }
  633. } catch (\think\exception\PDOException $e) {
  634. Db::rollback();
  635. $this->error($e->getMessage());
  636. } catch (\think\Exception $e) {
  637. Db::rollback();
  638. $this->error($e->getMessage());
  639. }
  640. if ($result !== false) {
  641. $this->success(__('Repair table %s done', $name));
  642. } else {
  643. $this->error(__('Repair table %s fail', $name));
  644. }
  645. }
  646. /**
  647. * 复制表格/结构/数据
  648. */
  649. public function copy()
  650. {
  651. $name = $this->request->get('name');
  652. $type = $this->request->get('type');
  653. if ($name == NULL) {
  654. $this->error(__('Parameter %s can not be empty', $name));
  655. }
  656. if ($this->request->isPost()) {
  657. $table = $this->request->post("table");
  658. if ($table) {
  659. $result = false;
  660. $sql = [];
  661. if ($this->prefix !== '' && strpos($table, $this->prefix) !== 0) {
  662. $table = $this->prefix . $table;
  663. }
  664. Db::startTrans();
  665. try {
  666. $_sql = "SHOW TABLES LIKE '{$table}'";
  667. $result = Db::query($_sql);
  668. if ($result) {
  669. $this->error("表 {$table} 已存在于数据库 {$this->dbName} 中");
  670. } else {
  671. //在此执行复制表的操作
  672. if ($type == 1) {
  673. $sql[] = "CREATE TABLE `{$table}` LIKE `{$name}`;";
  674. } else {
  675. $sql[] = "CREATE TABLE `{$table}` LIKE `{$name}`;";
  676. $sql[] = "INSERT INTO `{$table}` SELECT * FROM `{$name}`;";
  677. }
  678. $result = Db::batchQuery($sql);
  679. }
  680. if (Db::getPdo()->inTransaction() == true) {
  681. Db::commit();
  682. }
  683. $this->success(__('Copy table %s done', $name));
  684. } catch (\think\exception\PDOException $e) {
  685. Db::rollback();
  686. $this->error($e->getMessage());
  687. } catch (\think\Exception $e) {
  688. Db::rollback();
  689. $this->error($e->getMessage());
  690. }
  691. if ($result !== false) {
  692. $this->success(__('Copy table %s done', $name));
  693. } else {
  694. $this->error(__('Copy table %s fail', $name));
  695. }
  696. }
  697. $this->error(__('Parameter %s can not be empty', $table));
  698. }
  699. }
  700. /**
  701. * 字符集
  702. * @internal
  703. */
  704. public function getCollation()
  705. {
  706. $custom = (array) $this->request->request("custom/a");
  707. $keyValue = $this->request->request('keyValue');
  708. if ($custom && is_array($custom)) {
  709. $charset = $custom['charset'];
  710. }
  711. if (!$keyValue) {
  712. $list = $this->collationList[$charset];
  713. foreach ($list as $k => $v) {
  714. $list[$k] = ['collation' => $v];
  715. }
  716. } else {
  717. $list[] = ['collation' => $keyValue];
  718. }
  719. $result = array("total" => count($list), "list" => $list);
  720. return json($result);
  721. }
  722. /**
  723. * 获取数据表
  724. * @internal
  725. */
  726. public function get_table_list()
  727. {
  728. //当前页
  729. $page = $this->request->request("pageNumber");
  730. //分页大小
  731. $pagesize = $this->request->request("pageSize");
  732. $custom = (array) $this->request->request("custom/a");
  733. $addon = 'all';
  734. if ($custom && is_array($custom)) {
  735. $addon = $custom['addon'];
  736. }
  737. $tableList = [];
  738. $list = \think\Db::query("SHOW TABLES");
  739. foreach ($list as $key => $row) {
  740. if ($addon == 'all') {
  741. $tableList[$key] = ['table_name' => reset($row)];
  742. } else {
  743. $tmp = explode('_', reset($row));
  744. if ($this->prefix !== '' && $tmp[1] == $addon) {
  745. $tableList[] = ['table_name' => reset($row)];
  746. } elseif ($this->prefix == '' && $tmp[0] == $addon) {
  747. $tableList[] = ['table_name' => reset($row)];
  748. }
  749. }
  750. }
  751. array_values($tableList);
  752. $result = array("total" => count($tableList), "rows" => array_slice($tableList, ($page - 1) * $pagesize, $pagesize));
  753. return json($result);
  754. }
  755. /**
  756. * 获取数据库表
  757. */
  758. protected function getTables($group = 'all')
  759. {
  760. $tables = Db::getTables();
  761. //数据表分组
  762. $addons = get_addon_list();
  763. $result = [];
  764. $result['system'] = [];
  765. foreach ($tables as $index => $table) {
  766. foreach ($addons as $key => $value) {
  767. $tmp = explode('_', $table);
  768. if ($this->prefix !== '' && $tmp[1] == $key) {
  769. if ($value['state'] == 1) {
  770. $result[$key][] = $table;
  771. }
  772. unset($tables[$index]);
  773. } elseif ($this->prefix == '' && $tmp[0] == $key) {
  774. if ($value['state'] == 1) {
  775. $result[$key][] = $table;
  776. }
  777. unset($tables[$index]);
  778. }
  779. }
  780. }
  781. $result['system'] = array_values($tables);
  782. return $group === 'all' ? $result : (isset($result[$group]) ? $result[$group] : []);
  783. }
  784. /**
  785. * 获取数据库分组
  786. */
  787. protected function getGroups($is_has = false)
  788. {
  789. $keyNames = array_keys($this->getTables());
  790. //数据表分组
  791. $addons = get_addon_list();
  792. $groups = [];
  793. foreach ($addons as $key => $value) {
  794. if ($value['state'] == 1 && !in_array($value['name'], ['famysql', 'fadeveloper'])) {
  795. $groups[$key] = $value['title'];
  796. if ($is_has && !in_array($key, $keyNames)) {
  797. unset($groups[$key]);
  798. }
  799. }
  800. }
  801. return $groups;
  802. }
  803. private function template()
  804. {
  805. $sqlFile = ADDON_PATH . 'famysql' . DS . 'data' . DS . 'tables.ini';
  806. $file_handle = fopen($sqlFile, "r");
  807. $file_content = fread($file_handle, filesize($sqlFile));
  808. $sqls = explode(';', $file_content);
  809. array_pop($sqls);
  810. $result = [];
  811. foreach ($sqls as $key => $sql) {
  812. preg_match('/CREATE TABLE IF NOT EXISTS `([^`]+)`/i', $sql, $matches);
  813. preg_match("/COMMENT='([^`]+)'/i", $sql, $cmatches);
  814. $result[$key]['table_name'] = $matches ? str_replace("__PREFIX__", '', $matches[1]) : '';
  815. $result[$key]['comment'] = $cmatches ? $cmatches[1] : '';
  816. $result[$key]['sql'] = ltrim($sql);
  817. }
  818. fclose($file_handle);
  819. return $result;
  820. }
  821. /**
  822. * 检查插件依赖
  823. * @internal
  824. * @return void
  825. */
  826. public function check()
  827. {
  828. $table_name = $this->request->request('table_name');
  829. $addonname = $this->request->request('addon_name');
  830. $addon_name = 'fadeveloper';
  831. $info = get_addon_info($addon_name);
  832. $addonArr = [
  833. 'fadeveloper' => 'FastAdmin插件开发工具'
  834. ];
  835. if (!$info || !$info['state']) {
  836. $this->error('请检查对应插件' . (isset($addonArr[$addon_name]) ? "《{$addonArr[$addon_name]}》" : "") . '是否安装且启动', 'addon/index');
  837. }
  838. $this->redirect('fadeveloper/command/crud?addon_name=' . $addonname . '&table_name=' . $table_name);
  839. }
  840. }