AddonsController.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. <?php
  2. namespace backend\modules\common\controllers;
  3. use Yii;
  4. use yii\web\NotFoundHttpException;
  5. use common\models\base\SearchModel;
  6. use common\traits\Curd;
  7. use common\models\common\Addons;
  8. use common\enums\AppEnum;
  9. use common\helpers\AddonHelper;
  10. use common\helpers\ExecuteHelper;
  11. use common\helpers\FileHelper;
  12. use common\interfaces\AddonWidget;
  13. use common\helpers\ResultHelper;
  14. use common\helpers\ArrayHelper;
  15. use backend\forms\AddonsForm;
  16. use backend\controllers\BaseController;
  17. use yii\web\UnprocessableEntityHttpException;
  18. /**
  19. * Class AddonsController
  20. * @package backend\modules\common\controllers
  21. */
  22. class AddonsController extends BaseController
  23. {
  24. use Curd;
  25. /**
  26. * @var Addons
  27. */
  28. public $modelClass = Addons::class;
  29. /**
  30. * 首页
  31. *
  32. * @return mixed|string
  33. * @throws NotFoundHttpException
  34. * @throws \Throwable
  35. * @throws \yii\db\StaleObjectException
  36. */
  37. public function actionIndex()
  38. {
  39. $searchModel = new SearchModel([
  40. 'model' => Addons::class,
  41. 'scenario' => 'default',
  42. 'partialMatchAttributes' => ['title', 'name'], // 模糊查询
  43. 'defaultOrder' => [
  44. 'id' => SORT_DESC,
  45. ],
  46. 'pageSize' => $this->pageSize,
  47. ]);
  48. $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
  49. $models = $dataProvider->getModels();
  50. $addons = [];
  51. foreach ($models as $model) {
  52. $addons[] = [
  53. 'name' => $model->name,
  54. 'version' => $model->version,
  55. ];
  56. }
  57. try {
  58. $newestVersion = Yii::$app->services->rageFrame->queryNewestByNames($addons);
  59. } catch (\Exception $e) {
  60. $newestVersion = [];
  61. }
  62. return $this->render($this->action->id, [
  63. 'dataProvider' => $dataProvider,
  64. 'searchModel' => $searchModel,
  65. 'newestVersion' => ArrayHelper::map($newestVersion, 'version_name', 'version'),
  66. 'addonsGroup' => Yii::$app->params['addonsGroup'],
  67. ]);
  68. }
  69. /**
  70. * 卸载
  71. *
  72. * @return mixed
  73. * @throws NotFoundHttpException
  74. * @throws \Throwable
  75. * @throws \yii\db\StaleObjectException
  76. */
  77. public function actionUnInstall()
  78. {
  79. $name = Yii::$app->request->get('name');
  80. // 删除数据库
  81. if ($model = Addons::findOne(['name' => $name])) {
  82. $model->delete();
  83. }
  84. try {
  85. // 进行卸载数据库
  86. if ($class = AddonHelper::getAddonConfig($name)) {
  87. $uninstallClass = AddonHelper::getAddonRoot($name) . (new $class)->uninstall;
  88. ExecuteHelper::map($uninstallClass, 'run', $model);
  89. }
  90. } catch (\Exception $e) {
  91. }
  92. return $this->message('卸载成功', $this->redirect(['index']));
  93. }
  94. /**
  95. * 在线升级
  96. *
  97. * @param $name
  98. * @return array|mixed
  99. */
  100. public function actionOnLineUpgrade($name = 'Authority')
  101. {
  102. try {
  103. Yii::$app->services->rageFrame->update($name);
  104. return ResultHelper::json(200, '升级成功');
  105. } catch (\Exception $e) {
  106. return ResultHelper::json(422, $e->getMessage());
  107. }
  108. }
  109. /**
  110. * 安装列表
  111. *
  112. * @return mixed|string
  113. * @throws \Throwable
  114. * @throws \yii\db\Exception
  115. */
  116. public function actionLocal()
  117. {
  118. return $this->render($this->action->id, [
  119. 'list' => Yii::$app->services->addons->getLocalList(),
  120. ]);
  121. }
  122. /**
  123. * 安装
  124. *
  125. * @return mixed|string
  126. * @throws \Throwable
  127. * @throws \yii\db\Exception
  128. */
  129. public function actionInstall($installData = true)
  130. {
  131. $name = Yii::$app->request->get('name');
  132. if (!($class = AddonHelper::getAddonConfig($name))) {
  133. return $this->message('实例化失败,插件不存在或检查插件名称', $this->redirect(['index']), 'error');
  134. }
  135. ini_set("max_execution_time", 300);
  136. // 开启事务
  137. $transaction = Yii::$app->db->beginTransaction();
  138. try {
  139. $config = new $class;
  140. $rootPath = AddonHelper::getAddonRootPath($name);
  141. $allAuthItem = [];
  142. $defaultConfig = [];
  143. // 前置插件
  144. foreach ($config->preposition as $addon => $preposition) {
  145. if (empty(Yii::$app->services->addons->findByName($addon))) {
  146. throw new NotFoundHttpException('请先安装「' . $preposition . '」插件');
  147. }
  148. }
  149. foreach ($config->appsConfig as $appId => $item) {
  150. $file = $rootPath . $item;
  151. if (!in_array($appId, array_keys(AppEnum::getMap()))) {
  152. throw new NotFoundHttpException('找不到应用');
  153. }
  154. if (!file_exists($file)) {
  155. throw new NotFoundHttpException("找不到 $appId 应用文件");
  156. }
  157. $appConfig = require $file;
  158. // 权限
  159. if (isset($appConfig['authItem']) && !empty($appConfig['authItem'])) {
  160. $allAuthItem[$appId] = $appConfig['authItem'];
  161. }
  162. // 默认存储配置
  163. $defaultConfig[$appId] = $appConfig['config'] ?? [];
  164. // 菜单配置
  165. if (isset($defaultConfig[$appId]['menu'])) {
  166. $cate = Yii::$app->services->menuCate->createByAddon(
  167. $appId,
  168. $config->info,
  169. $defaultConfig[$appId]['menu']['icon'],
  170. $defaultConfig[$appId]['menu']['location'],
  171. $defaultConfig[$appId]['menu']['sort'] ?? 999,
  172. $defaultConfig[$appId]['menu']['pattern'] ?? []
  173. );
  174. Yii::$app->services->menu->delByCate($cate);
  175. Yii::$app->services->menu->createByAddon($appConfig['menu'], $cate);
  176. }
  177. }
  178. Yii::$app->services->rbacAuthItemChild->accreditByAddon($allAuthItem, $name, $installData);
  179. // 更新信息
  180. $model = Yii::$app->services->addons->updateByName($name, $config, $defaultConfig);
  181. // 进行安装数据库
  182. if ($installData == true) {
  183. $installClass = AddonHelper::getAddonRoot($name) . $config->install;
  184. ExecuteHelper::map($installClass, 'run', $model);
  185. }
  186. $transaction->commit();
  187. return ResultHelper::json(200, '操作成功');
  188. } catch (\Exception $e) {
  189. $transaction->rollBack();
  190. return ResultHelper::json(422, $e->getMessage());
  191. }
  192. }
  193. /**
  194. * 升级数据库
  195. *
  196. * @return mixed
  197. * @throws \yii\web\NotFoundHttpException
  198. */
  199. public function actionUpgrade()
  200. {
  201. $name = Yii::$app->request->get('name');
  202. if (
  203. !($class = AddonHelper::getConfigClass($name)) ||
  204. !($model = Yii::$app->services->addons->findByName($name))
  205. ) {
  206. return $this->message('实例化失败,插件不存在或检查插件名称', $this->redirect(['index']), 'error');
  207. }
  208. // 更新数据库
  209. $upgradeClass = AddonHelper::getAddonRoot($name) . (new $class)->upgrade;
  210. if (!class_exists($upgradeClass)) {
  211. throw new NotFoundHttpException($upgradeClass . '未找到');
  212. }
  213. /** @var AddonWidget $upgradeModel */
  214. $upgradeModel = new $upgradeClass;
  215. if (!method_exists($upgradeModel, 'run')) {
  216. throw new NotFoundHttpException($upgradeClass . '/run方法未找到');
  217. }
  218. if (!isset($upgradeModel->versions)) {
  219. throw new NotFoundHttpException($upgradeClass . '下 versions 属性未找到');
  220. }
  221. $versions = $upgradeModel->versions;
  222. $count = count($versions);
  223. for ($i = 0; $i < $count; $i++) {
  224. // 验证版本号和更新
  225. if ($model->version == $versions[$i] && isset($versions[$i + 1])) {
  226. // 开启事务
  227. $transaction = Yii::$app->db->beginTransaction();
  228. try {
  229. $model->version = $versions[$i + 1];
  230. $upgradeModel->run($model);
  231. $model->save();
  232. // 完成事务
  233. $transaction->commit();
  234. } catch (\Exception $e) {
  235. // 回滚事务
  236. $transaction->rollBack();
  237. if (YII_DEBUG) {
  238. throw new UnprocessableEntityHttpException($e->getMessage());
  239. }
  240. ob_clean();
  241. return ResultHelper::json(422, $e->getMessage());
  242. }
  243. }
  244. }
  245. ob_clean();
  246. return ResultHelper::json(200, '升级数据库成功');
  247. }
  248. /**
  249. * 创建模块
  250. *
  251. * @return mixed|string
  252. */
  253. public function actionCreate()
  254. {
  255. $model = new AddonsForm();
  256. $data = Yii::$app->request->post();
  257. if ($model->load($data) && $model->validate()) {
  258. if (!is_writable(Yii::getAlias('@addons'))) {
  259. return $this->message('您没有创建目录写入权限,无法使用此功能', $this->redirect(['create']), 'error');
  260. }
  261. $addonDir = Yii::getAlias('@addons') . '/' . trim($model->name) . '/';
  262. if (is_dir($addonDir)) {
  263. return $this->message('插件已经存在,请删除后在试', $this->redirect(['create']), 'error');
  264. }
  265. // 创建目录结构
  266. $files = [];
  267. $files[] = $addonDir;
  268. $files[] = "{$addonDir}AddonConfig.php";
  269. $app = [
  270. AppEnum::BACKEND,
  271. AppEnum::FRONTEND,
  272. AppEnum::HTML5,
  273. AppEnum::OAUTH2,
  274. AppEnum::MERCHANT,
  275. AppEnum::API,
  276. ];
  277. $files[] = "{$addonDir}console/";
  278. $files[] = "{$addonDir}console/controllers/";
  279. $files[] = "{$addonDir}console/migrations/";
  280. $files[] = "{$addonDir}common/";
  281. $files[] = "{$addonDir}common/config/";
  282. $files[] = "{$addonDir}common/components/";
  283. $files[] = "{$addonDir}common/components/Bootstrap.php";
  284. $files[] = "{$addonDir}common/models/";
  285. $files[] = "{$addonDir}common/forms/";
  286. $files[] = "{$addonDir}common/models/DefaultModel.php";
  287. // 生成目录和空文件
  288. foreach ($app as $item) {
  289. $files[] = "{$addonDir}common/config/{$item}.php";
  290. $files[] = "{$addonDir}{$item}/";
  291. $files[] = "{$addonDir}{$item}/controllers/";
  292. if (!in_array($item, AppEnum::api())) {
  293. $files[] = "{$addonDir}{$item}/controllers/DefaultController.php";
  294. $files[] = "{$addonDir}{$item}/views/";
  295. $files[] = "{$addonDir}{$item}/views/layouts/";
  296. $files[] = "{$addonDir}{$item}/views/layouts/main.php";
  297. $files[] = "{$addonDir}{$item}/views/default/";
  298. $files[] = "{$addonDir}{$item}/views/default/index.php";
  299. $files[] = "{$addonDir}{$item}/assets/";
  300. $files[] = "{$addonDir}{$item}/assets/AppAsset.php";
  301. $files[] = "{$addonDir}{$item}/resources/";
  302. } else {
  303. // api特殊目录
  304. $files[] = "{$addonDir}{$item}/controllers/DefaultController.php";
  305. $files[] = "{$addonDir}{$item}/modules/";
  306. $files[] = "{$addonDir}{$item}/modules/v1/";
  307. $files[] = "{$addonDir}{$item}/modules/v1/Module.php";
  308. $files[] = "{$addonDir}{$item}/modules/v1/controllers/";
  309. $files[] = "{$addonDir}{$item}/modules/v1/controllers/DefaultController.php";
  310. $files[] = "{$addonDir}{$item}/modules/v2/";
  311. $files[] = "{$addonDir}{$item}/modules/v2/Module.php";
  312. $files[] = "{$addonDir}{$item}/modules/v2/controllers/";
  313. $files[] = "{$addonDir}{$item}/modules/v2/controllers/DefaultController.php";
  314. }
  315. }
  316. // 参数设置支持
  317. $files[] = "{$addonDir}common/forms/SettingForm.php";
  318. $files[] = "{$addonDir}merchant/controllers/SettingController.php";
  319. $files[] = "{$addonDir}merchant/views/setting/";
  320. // 服务层支持
  321. $files[] = "{$addonDir}services/";
  322. $files[] = "{$addonDir}services/Application.php";
  323. $files[] = "{$addonDir}services/ConfigService.php";
  324. $model['install'] && $files[] = "{$addonDir}{$model['install']}.php";
  325. $model['uninstall'] && $files[] = "{$addonDir}{$model['uninstall']}.php";
  326. $model['upgrade'] && $files[] = "{$addonDir}{$model['upgrade']}.php";
  327. FileHelper::createDirOrFiles($files);
  328. // 写入文件
  329. foreach ($app as $item) {
  330. // 配置文件
  331. file_put_contents("{$addonDir}common/config/{$item}.php", $this->renderPartial('template/config/app', [
  332. 'menu' => $data['menu'] ?? [],
  333. 'appID' => $item,
  334. 'model' => $model
  335. ]));
  336. if (in_array($item, AppEnum::api())) {
  337. // 默认控制器
  338. file_put_contents("{$addonDir}{$item}/controllers/DefaultController.php", $this->renderPartial(
  339. 'template/controllers/ApiDefaultController',
  340. ['model' => $model, 'appID' => $item]
  341. ));
  342. file_put_contents("{$addonDir}{$item}/modules/v1/controllers/DefaultController.php", $this->renderPartial(
  343. 'template/controllers/ApiModulesDefaultController',
  344. ['appID' => $item, 'model' => $model, 'versions' => 'v1']
  345. ));
  346. file_put_contents("{$addonDir}{$item}/modules/v2/controllers/DefaultController.php", $this->renderPartial(
  347. 'template/controllers/ApiModulesDefaultController',
  348. ['appID' => $item, 'model' => $model, 'versions' => 'v2']
  349. ));
  350. file_put_contents("{$addonDir}{$item}/modules/v1/Module.php", $this->renderPartial(
  351. 'template/ApiModules',
  352. ['appID' => $item, 'model' => $model, 'versions' => 'v1']
  353. ));
  354. file_put_contents("{$addonDir}{$item}/modules/v2/Module.php", $this->renderPartial(
  355. 'template/ApiModules',
  356. ['appID' => $item, 'model' => $model, 'versions' => 'v2']
  357. ));
  358. continue;
  359. }
  360. // 默认控制器
  361. file_put_contents("{$addonDir}{$item}/controllers/DefaultController.php", $this->renderPartial(
  362. 'template/controllers/DefaultController',
  363. ['model' => $model, 'appID' => $item]
  364. ));
  365. // 基础控制器
  366. file_put_contents("{$addonDir}{$item}/controllers/BaseController.php", $this->renderPartial('template/controllers/BaseController', ['model' => $model, 'appID' => $item]));
  367. // 资源目录
  368. file_put_contents("{$addonDir}{$item}/resources/.gitkeep", '*');
  369. // 写入默认视图
  370. file_put_contents("{$addonDir}{$item}/views/default/index.php", $this->renderPartial('template/view/index', ['model' => $model, 'appID' => $item]));
  371. // 写入视图自动载入
  372. file_put_contents("{$addonDir}{$item}/views/layouts/main.php", $this->renderPartial('template/view/main', ['model' => $model, 'appID' => $item]));
  373. // 写入前台/后台/微信资源
  374. file_put_contents("{$addonDir}{$item}/assets/AppAsset.php", $this->renderPartial('template/AppAsset', ['model' => $model, 'appID' => $item]));
  375. }
  376. // 控制台控制器
  377. file_put_contents("{$addonDir}console/controllers/.gitkeep", '*');
  378. // 控制台数据迁移
  379. file_put_contents("{$addonDir}console/migrations/.gitkeep", '*');
  380. // 写入引导
  381. file_put_contents("{$addonDir}common/components/Bootstrap.php", $this->renderPartial('template/Bootstrap', ['model' => $model]));
  382. // 写入服务
  383. file_put_contents("{$addonDir}services/Application.php", $this->renderPartial('template/Application', ['model' => $model]));
  384. file_put_contents("{$addonDir}services/ConfigService.php", $this->renderPartial('template/ConfigService', ['model' => $model]));
  385. // 写入默认model
  386. file_put_contents("{$addonDir}common/models/DefaultModel.php", $this->renderPartial('template/models/DefaultModel', ['model' => $model, 'appID' => 'merchant']));
  387. // 参数设置支持
  388. file_put_contents("{$addonDir}merchant/controllers/SettingController.php", $this->renderPartial(
  389. 'template/controllers/SettingController',
  390. ['model' => $model, 'appID' => 'merchant']
  391. ));
  392. file_put_contents("{$addonDir}common/forms/SettingForm.php", $this->renderPartial('template/forms/SettingFormModel', ['model' => $model, 'appID' => 'common']));
  393. file_put_contents("{$addonDir}merchant/views/setting/display.php", $this->renderPartial('template/view/display', ['model' => $model]));
  394. // 写入配置
  395. file_put_contents("{$addonDir}AddonConfig.php", $this->renderPartial('template/AddonConfig', [
  396. 'model' => $model
  397. ]));
  398. // 写入文件
  399. $model['install'] && file_put_contents("{$addonDir}/{$model['install']}.php", $this->renderPartial('template/Install', ['model' => $model]));
  400. $model['uninstall'] && file_put_contents("{$addonDir}/{$model['uninstall']}.php", $this->renderPartial('template/UnInstall', ['model' => $model]));
  401. $model['upgrade'] && file_put_contents("{$addonDir}/{$model['upgrade']}.php", $this->renderPartial('template/Upgrade', ['model' => $model]));
  402. return $this->message('模块创建成功', $this->redirect(['local']));
  403. }
  404. return $this->render($this->action->id, [
  405. 'model' => $model,
  406. 'addonsGroup' => Yii::$app->params['addonsGroup'],
  407. ]);
  408. }
  409. }
粤ICP备19079148号