GenerateTablesLogic.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | saiadmin [ saiadmin快速开发框架 ]
  4. // +----------------------------------------------------------------------
  5. // | Author: sai <1430792918@qq.com>
  6. // +----------------------------------------------------------------------
  7. namespace plugin\saiadmin\app\logic\tool;
  8. use plugin\saiadmin\app\cache\UserMenuCache;
  9. use plugin\saiadmin\app\logic\system\DatabaseLogic;
  10. use plugin\saiadmin\app\model\system\SystemMenu;
  11. use plugin\saiadmin\app\model\tool\GenerateTables;
  12. use plugin\saiadmin\app\model\tool\GenerateColumns;
  13. use plugin\saiadmin\exception\ApiException;
  14. use plugin\saiadmin\basic\think\BaseLogic;
  15. use plugin\saiadmin\utils\Helper;
  16. use plugin\saiadmin\utils\code\CodeZip;
  17. use plugin\saiadmin\utils\code\CodeEngine;
  18. /**
  19. * 代码生成业务逻辑层
  20. */
  21. class GenerateTablesLogic extends BaseLogic
  22. {
  23. protected $columnLogic = null;
  24. protected $dataLogic = null;
  25. /**
  26. * 构造函数
  27. */
  28. public function __construct()
  29. {
  30. $this->model = new GenerateTables();
  31. $this->columnLogic = new GenerateColumnsLogic();
  32. $this->dataLogic = new DatabaseLogic();
  33. }
  34. /**
  35. * 删除表和字段信息
  36. * @param $ids
  37. * @return bool
  38. */
  39. public function destroy($ids): bool
  40. {
  41. return $this->transaction(function () use ($ids) {
  42. parent::destroy($ids);
  43. GenerateColumns::destroy(function ($query) use ($ids) {
  44. $query->where('table_id', 'in', $ids);
  45. });
  46. return true;
  47. });
  48. }
  49. /**
  50. * 装载表信息
  51. * @param $names
  52. * @param $source
  53. * @return void
  54. */
  55. public function loadTable($names, $source): void
  56. {
  57. $data = config('think-orm.connections');
  58. $config = $data[$source];
  59. if (!$config) {
  60. throw new ApiException('数据库配置读取失败');
  61. }
  62. $prefix = $config['prefix'] ?? '';
  63. foreach ($names as $item) {
  64. $class_name = $item['name'];
  65. if (!empty($prefix)) {
  66. $class_name = Helper::str_replace_once($prefix, '', $class_name);
  67. }
  68. $class_name = Helper::camel($class_name);
  69. $tableInfo = [
  70. 'table_name' => $item['name'],
  71. 'table_comment' => $item['comment'],
  72. 'class_name' => $class_name,
  73. 'business_name' => Helper::get_business($item['name']),
  74. 'belong_menu_id' => 80,
  75. 'menu_name' => $item['comment'],
  76. 'tpl_category' => 'single',
  77. 'template' => 'app',
  78. 'stub' => 'think',
  79. 'namespace' => '',
  80. 'package_name' => '',
  81. 'source' => $source,
  82. 'generate_menus' => 'index,save,update,read,destroy',
  83. ];
  84. $model = GenerateTables::create($tableInfo);
  85. $columns = $this->dataLogic->getColumnList($item['name'], $source);
  86. foreach ($columns as &$column) {
  87. $column['table_id'] = $model->id;
  88. $column['is_cover'] = false;
  89. }
  90. $this->columnLogic->saveExtra($columns);
  91. }
  92. }
  93. /**
  94. * 同步表字段信息
  95. * @param $id
  96. * @return void
  97. */
  98. public function sync($id)
  99. {
  100. $model = $this->model->findOrEmpty($id);
  101. // 拉取已有数据表信息
  102. $queryModel = $this->columnLogic->model->where('table_id', $id);
  103. $columnLogicData = $this->columnLogic->getAll($queryModel);
  104. $columnLogicList = [];
  105. foreach ($columnLogicData as $item) {
  106. $columnLogicList[$item['column_name']] = $item;
  107. }
  108. GenerateColumns::destroy(function ($query) use ($id) {
  109. $query->where('table_id', $id);
  110. });
  111. $columns = $this->dataLogic->getColumnList($model->table_name, $model->source ?? '');
  112. foreach ($columns as &$column) {
  113. $column['table_id'] = $model->id;
  114. $column['is_cover'] = false;
  115. if (isset($columnLogicList[$column['column_name']])) {
  116. // 存在历史信息的情况
  117. $getcolumnLogicItem = $columnLogicList[$column['column_name']];
  118. if ($getcolumnLogicItem['column_type'] == $column['column_type']) {
  119. $column['is_cover'] = true;
  120. foreach ($getcolumnLogicItem as $key => $item) {
  121. $array = [
  122. 'column_comment',
  123. 'column_type',
  124. 'is_pk',
  125. 'is_required',
  126. 'is_insert',
  127. 'is_edit',
  128. 'is_list',
  129. 'is_query',
  130. 'is_sort',
  131. 'query_type',
  132. 'view_type',
  133. 'dict_type',
  134. 'options',
  135. 'sort',
  136. 'is_cover'
  137. ];
  138. if (in_array($key, $array)) {
  139. $column[$key] = $item;
  140. }
  141. }
  142. }
  143. }
  144. }
  145. $this->columnLogic->saveExtra($columns);
  146. }
  147. /**
  148. * 代码预览
  149. * @param $id
  150. * @return array
  151. */
  152. public function preview($id): array
  153. {
  154. $data = $this->renderData($id);
  155. $codeEngine = new CodeEngine($data);
  156. $controllerContent = $codeEngine->renderContent('php', 'controller.stub');
  157. $logicContent = $codeEngine->renderContent('php', 'logic.stub');
  158. $modelContent = $codeEngine->renderContent('php', 'model.stub');
  159. $validateContent = $codeEngine->renderContent('php', 'validate.stub');
  160. $sqlContent = $codeEngine->renderContent('sql', 'sql.stub');
  161. $indexContent = $codeEngine->renderContent('vue', 'index.stub');
  162. $editContent = $codeEngine->renderContent('vue', 'edit-dialog.stub');
  163. $searchContent = $codeEngine->renderContent('vue', 'table-search.stub');
  164. $apiContent = $codeEngine->renderContent('ts', 'api.stub');
  165. // 返回生成内容
  166. return [
  167. [
  168. 'tab_name' => 'controller.php',
  169. 'name' => 'controller',
  170. 'lang' => 'php',
  171. 'code' => $controllerContent
  172. ],
  173. [
  174. 'tab_name' => 'logic.php',
  175. 'name' => 'logic',
  176. 'lang' => 'php',
  177. 'code' => $logicContent
  178. ],
  179. [
  180. 'tab_name' => 'model.php',
  181. 'name' => 'model',
  182. 'lang' => 'php',
  183. 'code' => $modelContent
  184. ],
  185. [
  186. 'tab_name' => 'validate.php',
  187. 'name' => 'validate',
  188. 'lang' => 'php',
  189. 'code' => $validateContent
  190. ],
  191. [
  192. 'tab_name' => 'sql.sql',
  193. 'name' => 'sql',
  194. 'lang' => 'sql',
  195. 'code' => $sqlContent
  196. ],
  197. [
  198. 'tab_name' => 'index.vue',
  199. 'name' => 'index',
  200. 'lang' => 'html',
  201. 'code' => $indexContent
  202. ],
  203. [
  204. 'tab_name' => 'edit-dialog.vue',
  205. 'name' => 'edit-dialog',
  206. 'lang' => 'html',
  207. 'code' => $editContent
  208. ],
  209. [
  210. 'tab_name' => 'table-search.vue',
  211. 'name' => 'table-search',
  212. 'lang' => 'html',
  213. 'code' => $searchContent
  214. ],
  215. [
  216. 'tab_name' => 'api.ts',
  217. 'name' => 'api',
  218. 'lang' => 'javascript',
  219. 'code' => $apiContent
  220. ]
  221. ];
  222. }
  223. /**
  224. * 生成到模块
  225. * @param $id
  226. */
  227. public function genModule($id)
  228. {
  229. $data = $this->renderData($id);
  230. // 生成文件到模块
  231. $codeEngine = new CodeEngine($data);
  232. $codeEngine->generateBackend('controller', $codeEngine->renderContent('php', 'controller.stub'));
  233. $codeEngine->generateBackend('logic', $codeEngine->renderContent('php', 'logic.stub'));
  234. $codeEngine->generateBackend('model', $codeEngine->renderContent('php', 'model.stub'));
  235. $codeEngine->generateBackend('validate', $codeEngine->renderContent('php', 'validate.stub'));
  236. $codeEngine->generateFrontend('index', $codeEngine->renderContent('vue', 'index.stub'));
  237. $codeEngine->generateFrontend('edit-dialog', $codeEngine->renderContent('vue', 'edit-dialog.stub'));
  238. $codeEngine->generateFrontend('table-search', $codeEngine->renderContent('vue', 'table-search.stub'));
  239. $codeEngine->generateFrontend('api', $codeEngine->renderContent('ts', 'api.stub'));
  240. }
  241. /**
  242. * 处理数据
  243. * @param $id
  244. * @return array
  245. */
  246. protected function renderData($id): array
  247. {
  248. $table = $this->model->findOrEmpty($id);
  249. if (!in_array($table['template'], ["plugin", "app"])) {
  250. throw new ApiException('应用类型必须为plugin或者app');
  251. }
  252. if (empty($table['namespace'])) {
  253. throw new ApiException('请先设置应用名称');
  254. }
  255. $columns = $this->columnLogic->where('table_id', $id)
  256. ->order('sort', 'desc')
  257. ->select()
  258. ->toArray();
  259. $pk = 'id';
  260. foreach ($columns as &$column) {
  261. if ($column['is_pk'] == 2) {
  262. $pk = $column['column_name'];
  263. }
  264. if ($column['column_name'] == 'delete_time') {
  265. unset($column['column_name']);
  266. }
  267. }
  268. // 处理特殊变量
  269. if ($table['template'] == 'plugin') {
  270. $namespace_start = "plugin\\" . $table['namespace'] . "\\app\\admin\\";
  271. $namespace_start_model = "plugin\\" . $table['namespace'] . "\\app\\";
  272. $namespace_end = "\\" . $table['package_name'];
  273. $url_path = 'app/' . $table['namespace'] . '/admin/' . $table['package_name'] . '/' . $table['class_name'];
  274. $route = 'app/';
  275. } else {
  276. $namespace_start = "app\\" . $table['namespace'] . "\\";
  277. $namespace_start_model = "app\\" . $table['namespace'] . "\\";
  278. $namespace_end = "\\" . $table['package_name'];
  279. $url_path = $table['namespace'] . '/' . $table['package_name'] . '/' . $table['class_name'];
  280. $route = '';
  281. }
  282. $config = config('think-orm');
  283. $data = $table->toArray();
  284. $data['pk'] = $pk;
  285. $data['namespace_start'] = $namespace_start;
  286. $data['namespace_start_model'] = $namespace_start_model;
  287. $data['namespace_end'] = $namespace_end;
  288. $data['url_path'] = $url_path;
  289. $data['route'] = $route;
  290. $data['tables'] = [$data];
  291. $data['columns'] = $columns;
  292. $data['db_source'] = $config['default'] ?? 'mysql';
  293. return $data;
  294. }
  295. /**
  296. * 生成到模块
  297. */
  298. public function generateFile($id)
  299. {
  300. $table = $this->model->where('id', $id)->findOrEmpty();
  301. if ($table->isEmpty()) {
  302. throw new ApiException('请选择要生成的表');
  303. }
  304. $debug = config('app.debug', true);
  305. if (!$debug) {
  306. throw new ApiException('非调试模式下,不允许生成文件');
  307. }
  308. $this->updateMenu($table);
  309. $this->genModule($id);
  310. UserMenuCache::clearMenuCache();
  311. }
  312. /**
  313. * 代码生成下载
  314. */
  315. public function generate($idsArr): array
  316. {
  317. $zip = new CodeZip();
  318. $tables = $this->model->where('id', 'in', $idsArr)->select()->toArray();
  319. foreach ($idsArr as $table_id) {
  320. $data = $this->renderData($table_id);
  321. $data['tables'] = $tables;
  322. $codeEngine = new CodeEngine($data);
  323. $codeEngine->generateTemp();
  324. }
  325. $filename = 'saiadmin.zip';
  326. $download = $zip->compress();
  327. return compact('filename', 'download');
  328. }
  329. /**
  330. * 处理菜单列表
  331. * @param $tables
  332. */
  333. public function updateMenu($tables)
  334. {
  335. /*不存在的情况下进行新建操作*/
  336. $url_path = $tables['namespace'] . ":" . $tables['package_name'] . ':' . $tables['business_name'];
  337. $code = $tables['namespace'] . "/" . $tables['package_name'] . '/' . $tables['business_name'];
  338. $path = $tables['package_name'] . '/' . $tables['business_name'];
  339. $component = $tables['namespace'] . "/" . $tables['package_name'] . '/' . $tables['business_name'];
  340. /*先获取一下已有的路由中是否包含当前ID的路由的核心信息*/
  341. $model = new SystemMenu();
  342. $tableMenu = $model->where('generate_id', $tables['id'])->findOrEmpty();
  343. $fistMenu = [
  344. 'parent_id' => $tables['belong_menu_id'],
  345. 'name' => $tables['menu_name'],
  346. 'code' => $code,
  347. 'path' => $path,
  348. 'icon' => 'ri:home-2-line',
  349. 'component' => "/plugin/$component/index",
  350. 'type' => 2,
  351. 'sort' => 100,
  352. 'is_iframe' => 2,
  353. 'is_keep_alive' => 2,
  354. 'is_hidden' => 2,
  355. 'is_fixed_tab' => 2,
  356. 'is_full_page' => 2,
  357. 'generate_id' => $tables['id']
  358. ];
  359. if ($tableMenu->isEmpty()) {
  360. $temp = SystemMenu::create($fistMenu);
  361. $fistMenuId = $temp->id;
  362. } else {
  363. $fistMenu['id'] = $tableMenu['id'];
  364. $tableMenu->save($fistMenu);
  365. $fistMenuId = $tableMenu['id'];
  366. }
  367. /*开始进行子权限的判定操作*/
  368. $childNodes = [
  369. ['name' => '列表', 'key' => 'index'],
  370. ['name' => '保存', 'key' => 'save'],
  371. ['name' => '更新', 'key' => 'update'],
  372. ['name' => '读取', 'key' => 'read'],
  373. ['name' => '删除', 'key' => 'destroy'],
  374. ];
  375. foreach ($childNodes as $node) {
  376. $nodeData = $model->where('parent_id', $fistMenuId)->where('generate_key', $node['key'])->findOrEmpty();
  377. $childNodeData = [
  378. 'parent_id' => $fistMenuId,
  379. 'name' => $node['name'],
  380. 'slug' => "$url_path:{$node['key']}",
  381. 'type' => 3,
  382. 'sort' => 100,
  383. 'is_iframe' => 2,
  384. 'is_keep_alive' => 2,
  385. 'is_hidden' => 2,
  386. 'is_fixed_tab' => 2,
  387. 'is_full_page' => 2,
  388. 'generate_key' => $node['key']
  389. ];
  390. if (!empty($nodeData)) {
  391. $childNodeData['id'] = $nodeData['id'];
  392. $nodeData->save($childNodeData);
  393. } else {
  394. $menuModel = new SystemMenu();
  395. $menuModel->save($childNodeData);
  396. }
  397. }
  398. }
  399. /**
  400. * 获取数据表字段信息
  401. * @param $table_id
  402. * @return mixed
  403. */
  404. public function getTableColumns($table_id): mixed
  405. {
  406. $query = $this->columnLogic->where('table_id', $table_id);
  407. return $this->columnLogic->getAll($query);
  408. }
  409. /**
  410. * 编辑数据
  411. * @param $id
  412. * @param $data
  413. * @return mixed
  414. */
  415. public function edit($id, $data): mixed
  416. {
  417. $columns = $data['columns'];
  418. unset($data['columns']);
  419. if (!empty($data['belong_menu_id'])) {
  420. $data['belong_menu_id'] = is_array($data['belong_menu_id']) ? array_pop($data['belong_menu_id']) : $data['belong_menu_id'];
  421. } else {
  422. $data['belong_menu_id'] = 0;
  423. }
  424. $data['generate_menus'] = implode(',', $data['generate_menus']);
  425. if (empty($data['options'])) {
  426. unset($data['options']);
  427. }
  428. $data['options'] = json_encode($data['options'], JSON_UNESCAPED_UNICODE);
  429. // 更新业务表
  430. $this->update($data, ['id' => $id]);
  431. // 更新业务字段表
  432. foreach ($columns as $column) {
  433. if ($column['options']) {
  434. $column['options'] = json_encode($column['options'], JSON_NUMERIC_CHECK);
  435. }
  436. $this->columnLogic->update($column, ['id' => $column['id']]);
  437. }
  438. return true;
  439. }
  440. }