GenerateTablesLogic.php 16 KB

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