System.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  1. <?php
  2. /**
  3. * Niushop商城系统 - 团队十年电商经验汇集巨献!
  4. * =========================================================
  5. * Copy right 2019-2029 杭州牛之云科技有限公司, 保留所有权利。
  6. * ----------------------------------------------
  7. * 官方网址: https://www.niushop.com
  8. * =========================================================
  9. */
  10. namespace app\shop\controller;
  11. use app\model\system\Addon;
  12. use app\model\system\Database;
  13. use app\model\system\Menu;
  14. use app\model\web\Config as ConfigModel;
  15. use app\model\web\DiyView as DiyViewModel;
  16. use extend\database\Database as dbdatabase;
  17. use think\facade\Cache;
  18. class System extends BaseShop
  19. {
  20. /*********************************************************系统缓存与数据库管理***************************************************/
  21. /**
  22. * 缓存设置
  23. */
  24. public function cache()
  25. {
  26. if (request()->isAjax()) {
  27. $type = input("key", '');
  28. $type_list = explode(',', $type);
  29. $res = [];
  30. $msg = '';
  31. foreach ($type_list as $k => $v) {
  32. switch ( $v ) {
  33. case 'content':
  34. Cache::clear();
  35. $msg = '数据缓存清除成功';
  36. break;
  37. case 'data_table_cache':
  38. // 数据表缓存清除
  39. if (is_dir('runtime/schema')) {
  40. rmdirs("schema");
  41. }
  42. $msg = '数据表缓存清除成功';
  43. break;
  44. case 'template_cache':
  45. // 模板缓存清除
  46. if (is_dir('runtime/temp')) {
  47. rmdirs("temp");
  48. }
  49. $msg = '模板缓存清除成功';
  50. break;
  51. case 'menu_cache':
  52. $addon_model = new Addon();
  53. $menu = new Menu();
  54. $menu->truncateMenu();
  55. $menu->truncateCashierAuth();
  56. $menu->refreshMenu('shop', '');
  57. $addon_model->cacheAddonMenu();
  58. $addon_model->cacheAddon();
  59. Cache::clear();
  60. $msg = '刷新菜单成功';
  61. break;
  62. case 'diy_view':
  63. $res = $this->refreshDiy();
  64. Cache::clear();
  65. $msg = '刷新自定义模板成功';
  66. break;
  67. case 'all':
  68. // 清除缓存
  69. $msg = '一键刷新成功';
  70. break;
  71. }
  72. }
  73. return success(0, $msg, $res);
  74. } else {
  75. $config_model = new ConfigModel();
  76. $cache_list = $config_model->getCacheList();
  77. $this->assign("cache_list", $cache_list);
  78. return $this->fetch('system/cache');
  79. }
  80. }
  81. public function cach1e()
  82. {
  83. if (request()->isAjax()) {
  84. $type = input("key", '');
  85. $type_list = explode(',', $type);
  86. $msg = '';
  87. foreach ($type_list as $k => $v) {
  88. switch ( $v ) {
  89. case 'all':
  90. $msg = '一键刷新成功';
  91. break;
  92. }
  93. }
  94. return success(0, $msg, '');
  95. } else {
  96. $config_model = new ConfigModel();
  97. $cache_list = $config_model->getCacheList();
  98. $this->assign("cache_list", $cache_list);
  99. return $this->fetch('system/cache');
  100. }
  101. }
  102. /**
  103. * 插件管理
  104. */
  105. public function addon()
  106. {
  107. $addon = new Addon();
  108. if (request()->isAjax()) {
  109. $addon_name = input("addon_name");
  110. $tag = input("tag", "install");
  111. if ($tag == 'install') {
  112. $res = $addon->install($addon_name);
  113. return $res;
  114. } else {
  115. $res = $addon->uninstall($addon_name);
  116. return $res;
  117. }
  118. }
  119. $addon = $addon->getAddonAllList();
  120. $this->assign("addons", $addon[ 'data' ][ 'install' ]);
  121. $this->assign("uninstall", $addon[ 'data' ][ 'uninstall' ]);
  122. return $this->fetch('system/addon');
  123. }
  124. /**
  125. * 数据库管理
  126. */
  127. public function database()
  128. {
  129. $database = new Database();
  130. $table = $database->getDatabaseList();
  131. $this->assign('list', $table);
  132. $this->forthMenu();
  133. return $this->fetch('system/database');
  134. }
  135. /**
  136. * 数据库还原页面展示
  137. */
  138. public function importlist()
  139. {
  140. $database = new Database();
  141. $path = $database->backup_path;
  142. if (!is_dir($path)) {
  143. $mode = intval('0777', 8);
  144. mkdir($path, $mode, true);
  145. }
  146. $flag = \FilesystemIterator::KEY_AS_FILENAME;
  147. $glob = new \FilesystemIterator($path, $flag);
  148. $list = array ();
  149. foreach ($glob as $name => $file) {
  150. if (preg_match('/^\d{8,8}-\d{6,6}-\d+\.sql(?:\.gz)?$/', $name)) {
  151. $name = sscanf($name, '%4s%2s%2s-%2s%2s%2s-%d');
  152. $date = "{$name[0]}-{$name[1]}-{$name[2]}";
  153. $time = "{$name[3]}:{$name[4]}:{$name[5]}";
  154. $part = $name[ 6 ];
  155. if (isset($list[ "{$date} {$time}" ])) {
  156. $info = $list[ "{$date} {$time}" ];
  157. $info[ 'part' ] = max($info[ 'part' ], $part);
  158. $info[ 'size' ] = $info[ 'size' ] + $file->getSize();
  159. $info[ 'size' ] = $database->format_bytes($info[ 'size' ]);
  160. } else {
  161. $info[ 'part' ] = $part;
  162. $info[ 'size' ] = $file->getSize();
  163. $info[ 'size' ] = $database->format_bytes($info[ 'size' ]);
  164. }
  165. $info[ 'name' ] = date('Ymd-His', strtotime("{$date} {$time}"));;
  166. $extension = strtoupper(pathinfo($file->getFilename(), PATHINFO_EXTENSION));
  167. $info[ 'compress' ] = ( $extension === 'SQL' ) ? '-' : $extension;
  168. $info[ 'time' ] = strtotime("{$date} {$time}");
  169. $list[] = $info;
  170. }
  171. }
  172. if (!empty($list)) {
  173. $list = $database->my_array_multisort($list, "time");
  174. }
  175. $this->assign('list', $list);
  176. $this->forthMenu();
  177. return $this->fetch('system/importlist');
  178. }
  179. /**
  180. * 还原数据库
  181. */
  182. public function importData()
  183. {
  184. $time = request()->post('time', '');
  185. $part = request()->post('part', 0);
  186. $start = request()->post('start', 0);
  187. $database = new Database();
  188. if (is_numeric($time) && ( is_null($part) || empty($part) ) && ( is_null($start) || empty($start) )) { // 初始化
  189. // 获取备份文件信息
  190. $name = date('Ymd-His', $time) . '-*.sql*';
  191. $path = realpath($database->backup_path) . DIRECTORY_SEPARATOR . $name;
  192. $files = glob($path);
  193. $list = array ();
  194. foreach ($files as $name) {
  195. $basename = basename($name);
  196. $match = sscanf($basename, '%4s%2s%2s-%2s%2s%2s-%d');
  197. $gz = preg_match('/^\d{8,8}-\d{6,6}-\d+\.sql.gz$/', $basename);
  198. $list[ $match[ 6 ] ] = array (
  199. $match[ 6 ],
  200. $name,
  201. $gz
  202. );
  203. }
  204. ksort($list);
  205. // 检测文件正确性
  206. $last = end($list);
  207. if (count($list) === $last[ 0 ]) {
  208. session('backup_list', $list); // 缓存备份列表
  209. $return_data = [
  210. 'code' => 1,
  211. 'message' => '初始化完成',
  212. 'data' => [ 'part' => 1, 'start' => 0 ]
  213. ];
  214. return $return_data;
  215. } else {
  216. $return_data = [
  217. 'code' => -1,
  218. 'message' => '备份文件可能已经损坏,请检查!',
  219. ];
  220. return $return_data;
  221. }
  222. } elseif (is_numeric($part) && is_numeric($start)) {
  223. $list = session('backup_list');
  224. $db = new dbdatabase($list[ $part ], array (
  225. 'path' => realpath($database->backup_path) . DIRECTORY_SEPARATOR,
  226. 'compress' => $list[ $part ][ 2 ]
  227. ));
  228. $start = $db->import($start);
  229. if ($start === false) {
  230. $return_data = [
  231. 'code' => -1,
  232. 'message' => '还原数据出错!',
  233. ];
  234. return $return_data;
  235. } elseif ($start === 0) { // 下一卷
  236. if (isset($list[ ++$part ])) {
  237. $data = array (
  238. 'part' => $part,
  239. 'start' => 0
  240. );
  241. $return_data = [
  242. 'code' => -1,
  243. 'message' => "正在还原...#{$part}",
  244. 'data' => $data
  245. ];
  246. return $return_data;
  247. } else {
  248. session('backup_list', null);
  249. $return_data = [
  250. 'code' => -1,
  251. 'message' => "还原完成!",
  252. ];
  253. return $return_data;
  254. }
  255. } else {
  256. $data = array (
  257. 'part' => $part,
  258. 'start' => $start[ 0 ]
  259. );
  260. if ($start[ 1 ]) {
  261. $rate = floor(100 * ( $start[ 0 ] / $start[ 1 ] ));
  262. $return_data = [
  263. 'code' => 1,
  264. 'message' => "正在还原...#{$part} ({$rate}%)",
  265. ];
  266. return $return_data;
  267. } else {
  268. $data[ 'gz' ] = 1;
  269. $return_data = [
  270. 'code' => 1,
  271. 'message' => "正在还原...#{$part}",
  272. 'data' => $data
  273. ];
  274. return $return_data;
  275. }
  276. }
  277. } else {
  278. $return_data = [
  279. 'code' => -1,
  280. 'message' => "参数有误",
  281. ];
  282. return $return_data;
  283. }
  284. }
  285. /**
  286. * 数据表修复
  287. */
  288. public function tablerepair()
  289. {
  290. if (request()->isAjax()) {
  291. $table_str = input('tables', '');
  292. $database = new Database();
  293. $res = $database->repair($table_str);
  294. return $res;
  295. }
  296. }
  297. /**
  298. * 数据表备份
  299. */
  300. public function backup()
  301. {
  302. $database = new Database();
  303. $tables = input('tables', []);
  304. $id = input('id', '');
  305. $start = input('start', '');
  306. if (!empty($tables) && is_array($tables)) { // 初始化
  307. // 读取备份配置
  308. $config = array (
  309. 'path' => $database->backup_path . DIRECTORY_SEPARATOR,
  310. 'part' => 20971520,
  311. 'compress' => 1,
  312. 'level' => 9
  313. );
  314. // 检查是否有正在执行的任务
  315. $lock = "{$config['path']}backup.lock";
  316. if (is_file($lock)) {
  317. return error(-1, '检测到有一个备份任务正在执行,请稍后再试!');
  318. } else {
  319. $mode = intval('0777', 8);
  320. if (!file_exists($config[ 'path' ]) || !is_dir($config[ 'path' ]))
  321. mkdir($config[ 'path' ], $mode, true); // 创建锁文件
  322. file_put_contents($lock, date('Ymd-His', time()));
  323. }
  324. // 自动创建备份文件夹
  325. // 检查备份目录是否可写
  326. is_writeable($config[ 'path' ]) || exit('backup_not_exist_success');
  327. session('backup_config', $config);
  328. // 生成备份文件信息
  329. $file = array (
  330. 'name' => date('Ymd-His', time()),
  331. 'part' => 1
  332. );
  333. session('backup_file', $file);
  334. // 缓存要备份的表
  335. session('backup_tables', $tables);
  336. $dbdatabase = new dbdatabase($file, $config);
  337. if (false !== $dbdatabase->create()) {
  338. $data = array ();
  339. $data[ 'status' ] = 1;
  340. $data[ 'message' ] = '初始化成功';
  341. $data[ 'tables' ] = $tables;
  342. $data[ 'tab' ] = array (
  343. 'id' => 0,
  344. 'start' => 0
  345. );
  346. return $data;
  347. } else {
  348. return error(-1, '初始化失败,备份文件创建失败!');
  349. }
  350. } elseif (is_numeric($id) && is_numeric($start)) { // 备份数据
  351. $tables = session('backup_tables');
  352. // 备份指定表
  353. $dbdatabase = new dbdatabase(session('backup_file'), session('backup_config'));
  354. $start = $dbdatabase->backup($tables[ $id ], $start);
  355. if (false === $start) { // 出错
  356. return error(-1, '备份出错!');
  357. } elseif (0 === $start) { // 下一表
  358. if (isset($tables[ ++$id ])) {
  359. $tab = array (
  360. 'id' => $id,
  361. 'table' => $tables[ $id ],
  362. 'start' => 0
  363. );
  364. $data = array ();
  365. $data[ 'rate' ] = 100;
  366. $data[ 'status' ] = 1;
  367. $data[ 'message' ] = '备份完成!';
  368. $data[ 'tab' ] = $tab;
  369. return $data;
  370. } else { // 备份完成,清空缓存
  371. unlink($database->backup_path . DIRECTORY_SEPARATOR . 'backup.lock');
  372. session('backup_tables', null);
  373. session('backup_file', null);
  374. session('backup_config', null);
  375. return success(1);
  376. }
  377. } else {
  378. $tab = array (
  379. 'id' => $id,
  380. 'table' => $tables[ $id ],
  381. 'start' => $start[ 0 ]
  382. );
  383. $rate = floor(100 * ( $start[ 0 ] / $start[ 1 ] ));
  384. $data = array ();
  385. $data[ 'status' ] = 1;
  386. $data[ 'rate' ] = $rate;
  387. $data[ 'message' ] = "正在备份...({$rate}%)";
  388. $data[ 'tab' ] = $tab;
  389. return $data;
  390. }
  391. } else { // 出错
  392. return error(-1, '参数有误!');
  393. }
  394. }
  395. /**
  396. * 删除备份文件
  397. */
  398. public function deleteData()
  399. {
  400. $name_time = input('time', '');
  401. if ($name_time) {
  402. $database = new Database();
  403. $name = date('Ymd-His', $name_time) . '-*.sql*';
  404. $path = realpath($database->backup_path) . DIRECTORY_SEPARATOR . $name;
  405. array_map("unlink", glob($path));
  406. if (count(glob($path))) {
  407. return error(-1, "备份文件删除失败,请检查权限!");
  408. } else {
  409. return success(1, "备份文件删除成功!");
  410. }
  411. } else {
  412. return error(-1, "参数有误!");
  413. }
  414. }
  415. /**
  416. * 刷新菜单 测试
  417. */
  418. public function refresh()
  419. {
  420. $menu = new Menu();
  421. $res = $menu->refreshAllMenu();
  422. var_dump($res);
  423. }
  424. /**
  425. * 刷新自定义组件
  426. */
  427. public function refreshDiy()
  428. {
  429. $menu = new Menu();
  430. $menu->truncateDiyView();
  431. $addon = new Addon();
  432. $addon_list = $addon->getAddonList([], 'name')[ 'data' ];
  433. $res = [];
  434. $res[] = $addon->refreshDiyView('');
  435. foreach ($addon_list as $k => $v) {
  436. $res[] = $addon->refreshDiyView($v[ 'name' ]);
  437. }
  438. // 处理升级版本数据遇到的数据问题
  439. $this->handleVersionData();
  440. return $res;
  441. }
  442. /**
  443. * 处理升级版本数据遇到的数据问题
  444. */
  445. public function handleVersionData()
  446. {
  447. $msg = '处理成功';
  448. try {
  449. model('site_diy_view')->startTrans();
  450. // 处理微页面数据、图标显示问题
  451. $page = model('site_diy_view')->getList([], 'id,value');
  452. foreach ($page as $k => $v) {
  453. if (!empty($v[ 'value' ])) {
  454. $value = json_decode($v[ 'value' ], true);
  455. foreach ($value[ 'value' ] as $ck => $cv) {
  456. if ($cv[ 'componentName' ] == 'Text') {
  457. // 标题组件
  458. } elseif ($cv[ 'componentName' ] == 'Search') {
  459. // 搜索框组件,v5.1.7新增
  460. if (!isset($cv[ 'searchLink' ])) {
  461. $value[ 'value' ][ $ck ][ 'searchLink' ] = [
  462. "name" => ""
  463. ];
  464. }
  465. } elseif ($cv[ 'componentName' ] == 'GraphicNav') {
  466. // 图文导航组件
  467. } elseif ($cv[ 'componentName' ] == 'GoodsList') {
  468. // 商品列表组件
  469. } elseif ($cv[ 'componentName' ] == 'ManyGoodsList') {
  470. // 多商品组组件,v5.1.9新增
  471. if (!isset($value[ 'value' ][ $ck ][ 'headStyle' ])) {
  472. $value[ 'value' ][ $ck ][ 'headStyle' ] = [
  473. 'titleColor' => '#303133'
  474. ];
  475. }
  476. } elseif ($cv[ 'componentName' ] == 'GoodsRecommend') {
  477. // 商品推荐组件
  478. } elseif ($cv[ 'componentName' ] == 'Seckill') {
  479. // 秒杀组件
  480. } elseif ($cv[ 'componentName' ] == 'Notice') {
  481. // 公告组件,v5.1.7新增
  482. if (!empty($cv[ 'list' ])) {
  483. foreach ($cv[ 'list' ] as $notice_k => $notice_v) {
  484. $cv[ 'list' ][ $notice_k ][ 'id' ] = unique_random(12) . $notice_k;
  485. }
  486. $value[ 'value' ][ $ck ][ 'list' ] = $cv[ 'list' ];
  487. }
  488. } elseif ($cv[ 'componentName' ] == 'ImageAds') {
  489. // 图片广告组件,v5.1.6新增
  490. if (!isset($cv[ 'indicatorIsShow' ])) {
  491. $value[ 'value' ][ $ck ][ 'indicatorIsShow' ] = true;
  492. }
  493. } elseif ($cv[ 'componentName' ] == 'MemberMyOrder') {
  494. // 会员中心 我的订单组件,v5.1.7新增
  495. if ($cv[ 'style' ] == 4) {
  496. $value[ 'value' ][ $ck ][ 'icon' ] = [
  497. "waitPay" => [
  498. "title" => "待支付",
  499. "icon" => "icondiy icon-system-daizhifu",
  500. "style" => [
  501. "bgRadius" => 0,
  502. "fontSize" => 90,
  503. "iconBgColor" => [],
  504. "iconBgColorDeg" => 0,
  505. "iconBgImg" => "",
  506. "iconColor" => [ "#20DA86", "#03B352" ],
  507. "iconColorDeg" => 0
  508. ]
  509. ],
  510. "waitSend" => [
  511. "title" => "备货中",
  512. "icon" => "icondiy icon-system-beihuozhong",
  513. "style" => [
  514. "bgRadius" => 0,
  515. "fontSize" => 90,
  516. "iconBgColor" => [],
  517. "iconBgColorDeg" => 0,
  518. "iconBgImg" => "",
  519. "iconColor" => [ "#20DA86", "#03B352" ],
  520. "iconColorDeg" => 0
  521. ]
  522. ],
  523. "waitConfirm" => [
  524. "title" => "配送中",
  525. "icon" => "icondiy icon-system-paisongzhong",
  526. "style" => [
  527. "bgRadius" => 0,
  528. "fontSize" => 90,
  529. "iconBgColor" => [],
  530. "iconBgColorDeg" => 0,
  531. "iconBgImg" => "",
  532. "iconColor" => [ "#20DA86", "#03B352" ],
  533. "iconColorDeg" => 0
  534. ]
  535. ],
  536. "waitUse" => [
  537. "title" => "待评价",
  538. "icon" => "icondiy icon-system-daishiyong2",
  539. "style" => [
  540. "bgRadius" => 0,
  541. "fontSize" => 90,
  542. "iconBgColor" => [],
  543. "iconBgColorDeg" => 0,
  544. "iconBgImg" => "",
  545. "iconColor" => [ "#20DA86", "#03B352" ],
  546. "iconColorDeg" => 0
  547. ]
  548. ],
  549. "refunding" => [
  550. "title" => "退换货",
  551. "icon" => "icondiy icon-system-tuihuoguanli",
  552. "style" => [
  553. "bgRadius" => 0,
  554. "fontSize" => 90,
  555. "iconBgColor" => [],
  556. "iconBgColorDeg" => 0,
  557. "iconBgImg" => "",
  558. "iconColor" => [ "#20DA86", "#03B352" ],
  559. "iconColorDeg" => 0
  560. ]
  561. ]
  562. ];
  563. }
  564. } elseif ($cv[ 'componentName' ] == 'FloatBtn') {
  565. // 浮动按钮 组件,v5.1.7新增
  566. if (!isset($cv[ 'imageSize' ])) {
  567. $value[ 'value' ][ $ck ][ 'imageSize' ] = 40;
  568. }
  569. } elseif ($cv[ 'componentName' ] == 'TopCategory') {
  570. // 分类导航 组件,v5.1.7新增
  571. if (!isset($cv[ 'moreColor' ])) {
  572. $value[ 'value' ][ $ck ][ 'moreColor' ] = '#333333';
  573. }
  574. }
  575. }
  576. model('site_diy_view')->update([ 'value' => json_encode($value) ], [ [ 'id', '=', $v[ 'id' ] ] ]);
  577. }
  578. }
  579. model('site_diy_view')->commit();
  580. Cache::clear();
  581. } catch (\Exception $e) {
  582. model('site_diy_view')->rollback();
  583. $msg = 'File:' . $e->getFile() . ',Line:' . $e->getLine() . ',Message:' . $e->getMessage() . ',Code:' . $e->getCode();
  584. }
  585. return $msg;
  586. }
  587. }