CurdService.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. <?php
  2. /**
  3. * ----------------------------------------------------------------------------
  4. * 行到水穷处,坐看云起时
  5. * 开发软件,找贵阳云起信息科技,官网地址:https://www.56q7.com/
  6. * ----------------------------------------------------------------------------
  7. * Author: 老成
  8. * email:85556713@qq.com
  9. */
  10. declare(strict_types=1);
  11. namespace app\admin\service\curd;
  12. use app\common\service\BaseService;
  13. use app\common\model\AuthRule;
  14. use think\facade\Cache;
  15. use think\facade\Db;
  16. include __DIR__.DS.'eof.php';
  17. class CurdService extends BaseService
  18. {
  19. public static $prefix;
  20. private $table;
  21. private $controller;
  22. private $model;
  23. private $reduced;
  24. private $actions;
  25. private $actionList;
  26. private $fields;
  27. private $summary;
  28. private $expand;
  29. private $isTree;
  30. private $treeTitle;
  31. private $pagination;
  32. private $tabs;
  33. private $recyclebinField=[];
  34. private $relations;
  35. private $type;
  36. public function build()
  37. {
  38. $error='';
  39. Db::startTrans();
  40. try{
  41. $model=$this->createModel();
  42. $controller=$this->createController();
  43. $view=$this->createView();
  44. Db::commit();
  45. }catch (\Exception $e){
  46. $error=$e->getMessage();
  47. $this->rollback();
  48. }
  49. if($error){
  50. throw new \Exception($error);
  51. }
  52. $r=compact('model','controller','view');
  53. return $r;
  54. }
  55. public function volidate()
  56. {
  57. if($this->type=='file'){
  58. $filelist=$this->buildFile();
  59. foreach ($filelist as $key=>$file){
  60. if($key=='view'){
  61. foreach ($file as $f){
  62. if(file_exists($f)){
  63. throw new \Exception(__('%s文件已存在',['s'=>'view']));
  64. }
  65. }
  66. }else if(file_exists($file)){
  67. throw new \Exception(__('%s文件已存在',['s'=>$key]));
  68. }
  69. }
  70. }
  71. if($this->isTree && !$this->treeTitle){
  72. throw new \Exception(__('树形结构标题未定义'));
  73. }
  74. if(!key_exists('index',$this->actionList)){
  75. throw new \Exception(__('%s方法未定义',['s'=>'index']));
  76. }
  77. }
  78. public function clear()
  79. {
  80. $filelist=$this->buildFile();
  81. foreach ($filelist as $key=>$file){
  82. if($key=='view'){
  83. foreach ($file as $f){
  84. if(file_exists($f)){
  85. $time=filectime($f);
  86. if(time()-$time>3600){
  87. throw new \Exception(__('%s已创建超过一个小时,禁止删除',['s'=>'view']));
  88. }
  89. unlink($f);
  90. }
  91. }
  92. }else if(file_exists($file)){
  93. $time=filectime($file);
  94. if(time()-$time>3600){
  95. throw new \Exception(__('%s已创建超过一个小时,禁止删除',['s'=>$key]));
  96. }
  97. unlink($file);
  98. }
  99. }
  100. }
  101. protected function init()
  102. {
  103. $config = Db::getConfig();
  104. $default=$config['default'];
  105. self::$prefix=$config['connections'][$default]['prefix'];
  106. $recyclebinField=[];
  107. $relations=[];
  108. foreach ($this->fields as $key=>$value){
  109. $title=trim($value['title']);
  110. $this->fields[$key]['title']=$title?:$value['field'];
  111. if($this->actions['table'] && $value['visible']==='relation'){
  112. if(!$value['relation']){
  113. throw new \Exception(__('%s字段关联关系未定义',['s'=>$value['title']]));
  114. }
  115. $relation=json_decode($value['relation'],true);
  116. if($value['field']==$relation['table']){
  117. throw new \Exception(__('字段名与关联表名重名,禁止关联,建议修改字段名为%s',['s'=>$relation['table'].'_id']));
  118. }
  119. $relations[$value['field']]=$relation;
  120. }
  121. if(!empty($value['recyclebin'])){
  122. $type=$value['formatter'];
  123. if(!in_array($type,['text','image','images','date','datetime','tag','tags'])){
  124. $type='text';
  125. }
  126. $recyclebinField[]=[
  127. 'field'=>$value['field'],
  128. 'type'=>$type,
  129. 'title'=>$value['title']
  130. ];
  131. }
  132. }
  133. if(key_exists('recyclebin',$this->actionList) && empty($recyclebinField)){
  134. throw new \Exception(__('回收站的显示字段未定义'));
  135. }
  136. $this->relations=$relations;
  137. $this->recyclebinField=$recyclebinField;
  138. }
  139. public function getIndexUrl()
  140. {
  141. $pack=str_replace('\\','/',substr($this->controller,strpos($this->controller,'controller\\')+11));
  142. $pack=strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $pack));
  143. return request()->domain().build_url($pack.'/index','admin');
  144. }
  145. private function rollback()
  146. {
  147. Db::rollback();
  148. if($this->type=='file'){
  149. $filelist=$this->buildFile();
  150. foreach ($filelist as $key=>$file){
  151. if($key=='view'){
  152. foreach ($file as $f){
  153. if(file_exists($f)){
  154. unlink($f);
  155. }
  156. }
  157. }else if(file_exists($file)){
  158. unlink($file);
  159. }
  160. }
  161. }
  162. }
  163. private function buildFile(mixed $key=false)
  164. {
  165. $rootPath = root_path();
  166. $controller=$rootPath.str_replace('\\',DS,$this->controller).'.php';
  167. $model=$rootPath.str_replace('\\',DS,$this->model).'.php';
  168. $temp=strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $this->controller));
  169. $view=[
  170. 'index'=>$rootPath.str_replace(['\\','controller'],[DS,'view'],$temp).DS.'index.html',
  171. ];
  172. if(key_exists('add',$this->actionList)){
  173. $view['add']=$rootPath.str_replace(['\\','controller'],[DS,'view'],$temp).DS.'add.html';
  174. }
  175. if(key_exists('edit',$this->actionList)){
  176. $view['edit']=$rootPath.str_replace(['\\','controller'],[DS,'view'],$temp).DS.'edit.html';
  177. }
  178. $js=$rootPath.'public'.DS.'assets'.DS.'js'.DS.str_replace(['app\\','controller\\','\\'],['','',DS],$temp).'.js';
  179. $arr=compact('controller','model','view','js');
  180. if($key){
  181. return $arr[$key];
  182. }
  183. return $arr;
  184. }
  185. private function createView()
  186. {
  187. $keys=array_keys($this->actionList);
  188. $reduced=$this->reduced;
  189. $table=$this->actions['table'];
  190. $form=$this->actions['form'];
  191. $isTree=$this->isTree;
  192. $treeTitle=$this->treeTitle;
  193. $viewContent=[];
  194. foreach ($keys as $key){
  195. if(in_array($key,['del','multi','import','download','recyclebin'])){
  196. continue;
  197. }
  198. $action=$key;
  199. $title=$this->actionList[$key];
  200. $search=[];
  201. $commonSearch=false;
  202. $pagination=$this->pagination;
  203. $tabs=$this->tabs;
  204. $toolbar=['refresh'];
  205. if($key=='index'){
  206. foreach ($this->actionList as $xkey=>$value){
  207. if(in_array($xkey,['add','edit','del'])){
  208. $toolbar[]=$xkey;
  209. }
  210. }
  211. $slot='';
  212. $weigh=0;
  213. foreach ($this->fields as $value){
  214. if(!empty($value['search'])){
  215. $search[]=$value['field'];
  216. }
  217. if(trim($value['operate'])){
  218. $commonSearch=true;
  219. }
  220. if($value['field']=='weigh' && $value['type']=='int'){
  221. $weigh=1;
  222. }
  223. if($value['field']=='status' && $value['type']=='varchar'){
  224. $toolbar[]='more';
  225. }
  226. $slot.=getTableslot($value);
  227. }
  228. $slot=rtrim($slot);
  229. foreach ($this->actionList as $fkey=>$value){
  230. if(in_array($fkey,['import','download','recyclebin'])){
  231. $toolbar[]=$fkey;
  232. }
  233. }
  234. $search=empty($search)?'':implode(',',$search);
  235. $toolbarStr=implode(',',$toolbar);
  236. $controller=str_replace('\\','\\\\',$this->controller);
  237. $summary=$this->summary;
  238. $expand=$this->expand;
  239. $js=$this->getJsContent('js-index');
  240. $replace=compact('title','reduced','table','slot','expand','weigh','search','summary','controller','toolbar','toolbarStr','commonSearch','pagination','tabs','isTree','js');
  241. $content=$this->getContent('view-index',$replace);
  242. }else if($key=='add'){
  243. $slot='';
  244. foreach ($this->fields as $value){
  245. $slot.=getFormslot($value,$isTree,$treeTitle);
  246. }
  247. $slot=rtrim($slot);
  248. $js=$this->getJsContent('js-add');
  249. $replace=compact('title','reduced','slot','form','js');
  250. $content=$this->getContent('view-add',$replace);
  251. }else if($key=='edit'){
  252. $temp='';
  253. if($key=='edit'){
  254. $temp=str_replace('\\','/',substr($this->controller,strpos($this->controller,'controller\\')+11));
  255. $temp=strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $temp)).'/add';
  256. }
  257. $js=$this->getJsContent('js');
  258. $replace=compact('title','reduced','form','temp','js');
  259. $content=$this->getContent('view-edit',$replace);
  260. }else{
  261. $js=$this->getJsContent('js');
  262. $replace=compact('title','js');
  263. $content=$this->getContent('view-method',$replace);
  264. }
  265. if($this->type=='file'){
  266. $file=$this->buildFile('view')[$key];
  267. create_file($file,$content);
  268. }
  269. $viewContent[$key]=$content;
  270. }
  271. return $viewContent;
  272. }
  273. private function getJsContent(string $jsfile)
  274. {
  275. $reduced=$this->reduced;
  276. $table=$this->actions['table'];
  277. $form=$this->actions['form'];
  278. $expand=$this->expand;
  279. $actions=array_keys($this->actionList);
  280. $pack=str_replace('\\','/',substr($this->controller,strpos($this->controller,'controller\\')+11));
  281. $pack=strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $pack));
  282. $sort='';
  283. $fields='';
  284. $isTree=$this->isTree;
  285. $treeTitle=$this->treeTitle;
  286. foreach ($this->fields as $value){
  287. $istable=($jsfile=='js-index');
  288. $isform=($jsfile=='js-add');
  289. $fields.=getFields($value,$istable,$isform,$isTree,$treeTitle);
  290. if($value['field']=='weigh' && $value['type']=='int'){
  291. $sort=true;
  292. }
  293. }
  294. $fields=rtrim($fields);
  295. $isTree=$this->isTree;
  296. $replace=compact('pack','reduced','table','form','actions','expand','sort','isTree','fields');
  297. $content=$this->getContent($jsfile,$replace);
  298. return $content;
  299. }
  300. private function createController()
  301. {
  302. $reduced=$this->reduced;
  303. $controllerName=substr($this->controller,strrpos($this->controller,'\\')+1);
  304. $namespace=substr($this->controller,0,strrpos($this->controller,'\\'));
  305. $model=$this->model;
  306. $modelName=substr($this->model,strrpos($this->model,'\\')+1);
  307. $recyclebinField=rtrim(getRecyclebinField($this->recyclebinField));
  308. $recyclebinType=rtrim(getRecyclebinType($this->recyclebinField));
  309. $table=$this->actions['table'];
  310. $form=$this->actions['form'];
  311. $group=str_replace('\\','/',substr($this->controller,strpos($this->controller,'controller\\')+11));
  312. $group=strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $group));
  313. $methods='';
  314. foreach ($this->actionList as $key=>$value){
  315. if($table && in_array($key,['index','del','multi','import','download','recyclebin'])){
  316. continue;
  317. }
  318. if($form && in_array($key,['add','edit'])){
  319. continue;
  320. }
  321. $methods.=getAction($key,$value);
  322. }
  323. $methods=rtrim($methods);
  324. $relation=$this->getRelation();
  325. $actionList=$this->actionList;
  326. $isTree=$this->isTree;
  327. $summary=$this->summary;
  328. $treeTitle=$this->treeTitle;
  329. $replace=compact(
  330. 'namespace','controllerName','model','modelName',
  331. 'group','table','summary','form', 'relation',
  332. 'methods','actionList','isTree','treeTitle',
  333. 'recyclebinField','recyclebinType'
  334. );
  335. if($reduced){
  336. $content=$this->getContent('controller-reduced',$replace);
  337. }else{
  338. $content=$this->getContent('controller-normal',$replace);
  339. }
  340. if($this->type=='file'){
  341. $file=$this->buildFile('controller');
  342. create_file($file,$content);
  343. }
  344. return $content;
  345. }
  346. private function getRelation()
  347. {
  348. if(!$this->actions['table']){
  349. return '';
  350. }
  351. $relation=[];
  352. foreach ($this->relations as $value){
  353. $relation[]=str_replace(self::$prefix,'',$value['table']);
  354. }
  355. $relation=count($relation)>0?json_encode($relation):'';
  356. return $relation;
  357. }
  358. private function createModel()
  359. {
  360. //用户自定义模型名
  361. $modelName=substr($this->model,strrpos($this->model,'\\')+1);
  362. $namespace=substr($this->model,0,strrpos($this->model,'\\'));
  363. //根据表名生成模型名
  364. $table=str_replace(self::$prefix,'',$this->table);
  365. $tableModelName=str_replace(' ','',ucwords(str_replace('_',' ',$table)));
  366. $name='';
  367. if($tableModelName!=$modelName) {
  368. $name = $table;
  369. }
  370. $createtime=false;
  371. $updatetime=false;
  372. $deletetime=false;
  373. $weigh=false;
  374. foreach ($this->fields as $value){
  375. if($value['field']=='createtime'){
  376. $createtime=true;
  377. }
  378. if($value['field']=='updatetime'){
  379. $updatetime=true;
  380. }
  381. if($value['field']=='deletetime'){
  382. $deletetime=true;
  383. }
  384. if($value['field']=='weigh'){
  385. $weigh=true;
  386. }
  387. }
  388. $methods='';
  389. foreach($this->relations as $field=>$value){
  390. $methods.=getRelationMethods($field,$value);
  391. }
  392. $methods=rtrim($methods);
  393. if($createtime && $updatetime && $deletetime) {
  394. $content=$this->getContent('model-extend-base',compact('namespace','modelName','name','weigh','methods'));
  395. }else{
  396. $content=$this->getContent('model-normal',compact('namespace','modelName','name','createtime','updatetime','deletetime','weigh','methods'));
  397. }
  398. if($this->type=='file'){
  399. $file=$this->buildFile('model');
  400. create_file($file,$content);
  401. }
  402. return $content;
  403. }
  404. private function getContent(string $file,array $replace=[]):string
  405. {
  406. $filepath=__DIR__.DS.$file.'.txt';
  407. $myfile = fopen($filepath, "r");
  408. $content='';
  409. $if=[];
  410. $lastline='';
  411. while(!feof($myfile)) {
  412. $line_str = fgets($myfile);
  413. if($line_str){
  414. $ix=false;
  415. if($lastline==='' && trim($line_str)===''){
  416. $ix=true;
  417. }
  418. //条件判断
  419. if(strpos($line_str,'<#if')!==false){
  420. $continue=false;
  421. $if_str=substr($line_str,strpos($line_str,'<#if')+4);
  422. $if_str=substr($if_str,0,strpos($if_str,'#>'));
  423. $keys=array_keys($replace);
  424. $values=array_map(function ($res){
  425. return '$replace[\''.$res.'\']';
  426. },$keys);
  427. $if_str=str_replace(array_keys($replace),$values,$if_str);
  428. $phpstr="if(!({$if_str})){\$continue=true;}";
  429. eval($phpstr);
  430. $if[]=$continue;
  431. $ix=true;
  432. }
  433. if(strpos($line_str,'<#endif#>')!==false){
  434. array_pop($if);
  435. $ix=true;
  436. }
  437. foreach ($if as $value) {
  438. if ($value) {
  439. $ix = true;
  440. break;
  441. }
  442. }
  443. if($ix){
  444. continue;
  445. }
  446. //替换内容
  447. foreach ($replace as $key=>$value){
  448. if(is_string($value)){
  449. $line_str=str_replace('<#'.$key.'#>',$value,$line_str);
  450. }
  451. }
  452. $content.=$line_str;
  453. $lastline=trim($line_str);
  454. }
  455. }
  456. fclose($myfile);
  457. return $content;
  458. }
  459. }