diff --git a/extend/catcher/CatchAdmin.php b/extend/catcher/CatchAdmin.php
new file mode 100644
index 0000000..78e5ade
--- /dev/null
+++ b/extend/catcher/CatchAdmin.php
@@ -0,0 +1,325 @@
+getRootPath() . self::NAME . DIRECTORY_SEPARATOR;
+ }
+
+ /**
+ *
+ * @time 2019年12月04日
+ * @param $module
+ * @return string
+ */
+ public static function moduleDirectory($module): string
+ {
+ $directory = self::directory() . $module . DIRECTORY_SEPARATOR;
+
+ if (!is_dir($directory) && !mkdir($directory, 0777, true) && !is_dir($directory)) {
+ throw new \RuntimeException(sprintf('Directory "%s" was not created', $directory));
+ }
+
+ return $directory;
+ }
+
+ /**
+ *
+ * @time 2019年11月30日
+ * @return string
+ */
+ public static function cacheDirectory(): string
+ {
+ $directory = app()->getRuntimePath() . self::NAME . DIRECTORY_SEPARATOR;
+
+ if (!is_dir($directory) && !mkdir($directory, 0777, true) && !is_dir($directory)) {
+ throw new \RuntimeException(sprintf('Directory "%s" was not created', $directory));
+ }
+
+ return $directory;
+ }
+
+ /**
+ *
+ * @time 2019年12月03日
+ * @param $module
+ * @return string
+ */
+ public static function moduleMigrationsDirectory($module): string
+ {
+ return self::directory() . $module . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR. 'migrations' . DIRECTORY_SEPARATOR;
+ }
+
+ /**
+ *
+ * @time 2019年12月03日
+ * @param $module
+ * @return string
+ */
+ public static function moduleSeedsDirectory($module): string
+ {
+ return self::directory() . $module . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR. 'seeds' . DIRECTORY_SEPARATOR;
+ }
+
+ /**
+ * 获取模块 view path
+ *
+ * @time 2019年12月03日
+ * @param $module
+ * @return string
+ */
+ public static function getModuleViewPath($module): string
+ {
+ $directory = self::directory() . $module . DIRECTORY_SEPARATOR . 'view' . DIRECTORY_SEPARATOR;
+
+ if (!is_dir($directory) && !mkdir($directory, 0777, true) && !is_dir($directory)) {
+ throw new \RuntimeException(sprintf('Directory "%s" was not created', $directory));
+ }
+
+ return $directory;
+ }
+
+ /**
+ *
+ * @time 2019年12月03日
+ * @param $module
+ * @return string
+ */
+ public static function getModuleModelDirectory($module): string
+ {
+ $directory = self::directory() . $module . DIRECTORY_SEPARATOR . 'model' . DIRECTORY_SEPARATOR;
+
+ if (!is_dir($directory) && !mkdir($directory, 0777, true) && !is_dir($directory)) {
+ throw new \RuntimeException(sprintf('Directory "%s" was not created', $directory));
+ }
+
+ return $directory;
+ }
+ /**
+ *
+ * @time 2019年11月30日
+ * @return array
+ */
+ public static function getModulesDirectory(): array
+ {
+ $modules = glob(self::directory() . '*');
+
+ foreach ($modules as $key => &$module) {
+ if (!is_dir($module)) {
+ unset($modules[$key]);
+ }
+
+ $module .= DIRECTORY_SEPARATOR;
+ }
+
+ return $modules;
+ }
+
+ /**
+ *
+ * @time 2019年11月30日
+ * @return array
+ */
+ protected static function getModuleServices(): array
+ {
+ $services = [];
+
+ foreach (self::getModulesDirectory() as $module) {
+ if (is_dir($module)) {
+ $moduleInfo = self::getModuleInfo($module);
+ if (isset($moduleInfo['services']) && !empty($moduleInfo['services'])) {
+ $services = array_merge($services, $moduleInfo['services']);
+ }
+ }
+ }
+
+ return $services;
+ }
+
+ /**
+ *
+ * @time 2019年11月30日
+ * @return array
+ */
+ protected static function getModuleViews(): array
+ {
+ $views = [];
+
+ foreach (self::getModulesDirectory() as $module) {
+ if (is_dir($module . 'view')) {
+ $moduleInfo = self::getModuleInfo($module);
+ $moduleName = $moduleInfo['alias'] ?? Arr::last(explode('/', $module));
+ $views[$moduleName] = $module . 'view' . DIRECTORY_SEPARATOR;
+ }
+ }
+
+ return $views;
+ }
+
+ /**
+ * 获取模块信息
+ *
+ * @time 2019年11月30日
+ * @param $module
+ * @return mixed
+ */
+ public static function getModuleInfo($module)
+ {
+ if (file_exists($module . DIRECTORY_SEPARATOR . 'module.json')) {
+ return \json_decode(file_get_contents($module . DIRECTORY_SEPARATOR . 'module.json'), true);
+ }
+
+ return [];
+ }
+
+ /**
+ * 获取服务
+ *
+ * @time 2019年11月30日
+ * @return array
+ */
+ public static function getServices(): array
+ {
+ if (file_exists(self::getCacheServicesFile())) {
+ return self::getCacheServices();
+ }
+
+ return self::getModuleServices();
+ }
+
+ /**
+ *
+ * @time 2019年11月30日
+ * @return string
+ */
+ public static function getRoutes(): string
+ {
+ if (file_exists(self::getCacheViewsFile())) {
+ return self::getCacheRoutesFile();
+ }
+
+ self::cacheRoutes();
+
+ return self::getCacheRoutesFile();
+ }
+
+ /**
+ *
+ * @time 2019年11月30日
+ * @return array|mixed
+ */
+ public static function getViews()
+ {
+ if (file_exists(self::getCacheViewsFile())) {
+ return self::getCacheViews();
+ }
+
+ return self::getModuleViews();
+ }
+
+ /**
+ *
+ * @time 2019年11月30日
+ * @return false|int
+ */
+ public static function cacheRoutes()
+ {
+ $routeFiles = [];
+ foreach (self::getModulesDirectory() as $module) {
+ if (file_exists($module . 'route.php')) {
+ $routeFiles[] = $module . 'route.php';
+ }
+ }
+ $routes = '';
+ foreach ($routeFiles as $route) {
+ $routes .= trim(str_replace('fields[$this->name] = array_merge($this->fields[$this->name], [
+ 'id' => $id,
+ ]);
+ return $this;
+ }
+
+
+ public function class($class='', $labelClass = '', $inlineClass = '')
+ {
+ $this->fields[$this->name] = array_merge($this->fields[$this->name], [
+ 'class' => $class,
+ 'labelClass' => $labelClass,
+ 'inlineClass' => $inlineClass,
+ ]);
+
+ return $this;
+ }
+
+ public function options(array $options)
+ {
+ $this->fields[$this->name] = array_merge($this->fields[$this->name], [
+ 'options' => $options,
+ ]);
+
+ return $this;
+ }
+
+ public function default($value)
+ {
+ $this->fields[$this->name] = array_merge($this->fields[$this->name], [
+ 'default' => $value,
+ ]);
+
+ return $this;
+ }
+
+ public function disabled()
+ {
+ $this->fields[$this->name] = array_merge($this->fields[$this->name], [
+ 'disabled' => '',
+
+ ]);
+
+ return $this;
+ }
+ public function placeholder($content)
+ {
+ $this->fields[$this->name] = array_merge($this->fields[$this->name], [
+ 'placeholder' => 'placeholder='.$content,
+ ]);
+
+ return $this;
+ }
+
+ public function readonly()
+ {
+ $this->fields[$this->name] = array_merge($this->fields[$this->name], [
+ 'readonly' => 'readonly',
+ ]);
+
+ return $this;
+ }
+
+
+ public function render()
+ {
+ $form = '';
+ foreach ($this->fields as $field) {
+ $form .= sprintf($this->baseField(),
+ $field['labelClass'] ?? '',
+ $field['label'],
+ $field['inlineClass'] ?? '',
+ $this->{$field['type'].'Field'}($field));
+
+ }
+
+ return $form;
+ }
+
+
+ public function __call($method, $arguments)
+ {
+ // TODO: Implement __call() method.
+ $this->name = $arguments[0] ?? '';
+ $label = $arguments[1] ?? '';
+
+ $this->fields[$this->name] = [
+ 'name' => $this->name,
+ 'type' => $method,
+ 'label' => $label,
+ ];
+
+ return $this;
+ }
+
+ protected function inline()
+ {
+ $this->fields[] = array_merge($this->fields, [
+ 'inline' => true,
+ ]);
+
+ return $this;
+ }
+
+ private function baseField()
+ {
+ return
+ '
';
+ }
+ private function textField($field)
+ {
+ return
+ sprintf('',
+ $field['name'],
+ $field['class'],
+ $field['default'] ?? '',
+ $field['readonly'] ?? '',
+ $field['placeholder'] ?? '',
+ $field['disabled'] ?? ''
+ );
+
+ }
+
+ private function selectField($field)
+ {
+ $select = sprintf('';
+ }
+
+ private function radioField()
+ {}
+
+ private function textareaField()
+ {}
+
+ private function imageField()
+ {}
+
+}
diff --git a/extend/catcher/CatchResponse.php b/extend/catcher/CatchResponse.php
new file mode 100644
index 0000000..2323a15
--- /dev/null
+++ b/extend/catcher/CatchResponse.php
@@ -0,0 +1,39 @@
+ $code,
+ 'msg' => $msg,
+ 'data' => $data,
+ ]);
+ }
+
+ /**
+ * 错误的响应
+ *
+ * @time 2019年12月02日
+ * @param string $msg
+ * @param int $code
+ * @return \think\response\Json
+ */
+ public static function fail($msg = '', $code = 10001): \think\response\Json
+ {
+ return json([
+ 'code' => $code,
+ 'msg' => $msg,
+ ]);
+ }
+}
diff --git a/extend/catcher/Form.php b/extend/catcher/Form.php
new file mode 100644
index 0000000..383c8eb
--- /dev/null
+++ b/extend/catcher/Form.php
@@ -0,0 +1,7 @@
+ CatchAdmin::getViews()[$this->getModule($end['class'])]
+ ]);
+
+ return View::fetch($template ? : $this->getTemp($end['class'], $end['function']), $data);
+
+ }
+
+ /**
+ *
+ * @time 2019年12月03日
+ * @param $class
+ * @param $func
+ * @return string
+ */
+ protected function getTemp($class, $func)
+ {
+ $viewPath = CatchAdmin::getModuleViewPath($this->getModule($class));
+
+ $class = explode('\\', $class);
+
+ $className = strtolower(end($class));
+
+ if (is_dir($viewPath . $className)) {
+ return sprintf('%s/%s', $className, $func);
+ }
+
+ return $func;
+ }
+
+ /**
+ *
+ * @time 2019年12月03日
+ * @param $class
+ * @return mixed
+ */
+ protected function getModule($class)
+ {
+ return explode('\\', $class)[1];
+ }
+}
diff --git a/extend/catcher/base/BaseModel.php b/extend/catcher/base/BaseModel.php
new file mode 100644
index 0000000..ad4e984
--- /dev/null
+++ b/extend/catcher/base/BaseModel.php
@@ -0,0 +1,20 @@
+validate();
+ }
+
+ /**
+ * 初始化验证
+ *
+ * @time 2019年11月27日
+ * @throws \Exception
+ * @return mixed
+ */
+ protected function validate()
+ {
+ $validate = new Validate();
+
+ if (!$validate->check(request()->param(), $this->rules())) {
+ throw new ValidateFailedException($validate->getError());
+ }
+
+ return true;
+ }
+
+ abstract protected function rules(): array;
+
+ abstract protected function message(): array;
+}
diff --git a/extend/catcher/base/BaseValidate.php b/extend/catcher/base/BaseValidate.php
new file mode 100644
index 0000000..db644e2
--- /dev/null
+++ b/extend/catcher/base/BaseValidate.php
@@ -0,0 +1,37 @@
+register();
+
+ $this->rule = $this->getRules();
+ }
+
+
+ abstract protected function getRules(): array ;
+
+
+ private function register()
+ {
+ if (!empty($this->newValidates())) {
+ foreach ($this->newValidates() as $validate) {
+ $this->extend($validate->type(), [$validate, 'verify'], $validate->message());
+ }
+ }
+ }
+
+
+ private function newValidates()
+ {
+ return [
+ ];
+ }
+}
diff --git a/extend/catcher/command/InstallCommand.php b/extend/catcher/command/InstallCommand.php
new file mode 100644
index 0000000..8fb5f5c
--- /dev/null
+++ b/extend/catcher/command/InstallCommand.php
@@ -0,0 +1,329 @@
+setName('install:project')
+ // ->addArgument('module', Argument::REQUIRED, 'module name')
+ ->setDescription('install project');
+ }
+
+ /**
+ *
+ * @time 2019年11月29日
+ * @param Input $input
+ * @param Output $output
+ * @return int|void|null
+ */
+ protected function execute(Input $input, Output $output)
+ {
+ $this->detectionEnvironment();
+
+ $this->firstStep();
+
+ $this->secondStep();
+
+ $this->thirdStep();
+
+ $this->finished();
+
+ $this->project();
+ }
+
+ /**
+ * 环境检测
+ *
+ * @time 2019年11月29日
+ * @return void
+ */
+ protected function detectionEnvironment(): void
+ {
+ $this->output->info('environment begin to check...');
+
+ if (version_compare(PHP_VERSION, '7.1.0', '<')) {
+ $this->output->error('php version should >= 7.1.0');
+ exit();
+ }
+
+ $this->output->info('php version ' . PHP_VERSION);
+
+ if (!extension_loaded('mbstring')) {
+ $this->output->error('mbstring extension not install');exit();
+ }
+ $this->output->info('mbstring extension is installed');
+
+ if (!extension_loaded('json')) {
+ $this->output->error('json extension not install');
+ exit();
+ }
+ $this->output->info('json extension is installed');
+
+ if (!extension_loaded('openssl')) {
+ $this->output->error('openssl extension not install');
+ exit();
+ }
+ $this->output->info('openssl extension is installed');
+
+ if (!extension_loaded('pdo')) {
+ $this->output->error('pdo extension not install');
+ exit();
+ }
+ $this->output->info('pdo extension is installed');
+
+ if (!extension_loaded('xml')) {
+ $this->output->error('xml extension not install');
+ exit();
+ }
+
+ $this->output->info('xml extension is installed');
+
+ $this->output->info('🎉 environment checking finished');
+ }
+
+ /**
+ * 安装第一步
+ *
+ * @time 2019年11月29日
+ * @return bool
+ */
+ protected function firstStep(): bool
+ {
+ if (file_exists($this->app->getRootPath() . '.env')) {
+ return false;
+ }
+
+ $answer = strtolower($this->output->ask($this->input, '🤔️ Did You Need to Set Database information? (Y/N): '));
+
+ if ($answer === 'y' || $answer === 'yes') {
+ $charset = $this->output->ask($this->input, '👉 please input database charset, default (utf8mb4):') ? : 'utf8mb4';
+ $database = '';
+ while (!$database) {
+ $database = $this->output->ask($this->input, '👉 please input database name: ');
+ if ($database) {
+ break;
+ }
+ }
+ $host = $this->output->ask($this->input, '👉 please input database host, default (127.0.0.1):') ? : '127.0.0.1';
+ $port = $this->output->ask($this->input, '👉 please input database host port, default (3306):') ? : '3306';
+ $prefix = $this->output->ask($this->input, '👉 please input table prefix, default (null):') ? : '';
+ $username = $this->output->ask($this->input, '👉 please input database username default (root): ') ? : 'root';
+ $password = '';
+ while (!$password) {
+ $password = $this->output->ask($this->input, '👉 please input database password: ');
+ if ($password) {
+ break;
+ }
+ }
+
+ $this->generateEnvFile($host, $database, $username, $password, $port, $charset, $prefix);
+ }
+ }
+
+ /**
+ * 安装第二部
+ *
+ * @time 2019年11月29日
+ * @return void
+ */
+ protected function secondStep(): void
+ {
+ $modulePaths = glob(root_path('module') . '*');
+
+ $this->checkRootDatabase();
+
+ foreach ($modulePaths as $path) {
+ if (is_dir($path)) {
+ $moduleDatabasePath = $path . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR;
+ if (is_dir($moduleDatabasePath)) {
+ if (is_dir($moduleDatabasePath . 'migrations' . DIRECTORY_SEPARATOR)) {
+ $migrationFiles = glob($moduleDatabasePath . 'migrations' . DIRECTORY_SEPARATOR . '*.php');
+ foreach ($migrationFiles as $file) {
+ copy($file,
+ root_path('database') . 'migrations'. DIRECTORY_SEPARATOR .
+ pathinfo($file, PATHINFO_BASENAME));
+ }
+ }
+
+ if (is_dir($moduleDatabasePath . 'seeds' . DIRECTORY_SEPARATOR)) {
+ $seedFiles = glob($moduleDatabasePath . 'seeds' . DIRECTORY_SEPARATOR . '*.php');
+ foreach ($seedFiles as $file) {
+ copy($file,
+ root_path('database') . 'seeds' . DIRECTORY_SEPARATOR .
+ pathinfo($file, PATHINFO_BASENAME));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * 安装第四步
+ *
+ * @time 2019年11月29日
+ * @return void
+ */
+ protected function thirdStep(): void
+ {
+ Console::call('catch:cache');
+ }
+
+ /**
+ * finally
+ *
+ * @time 2019年11月30日
+ * @return void
+ */
+ protected function finished(): void
+ {
+ // todo something
+ if ($this->dataInstall) {
+ rmdir($this->app->getRootPath() . 'database');
+ }
+ }
+
+ /**
+ * generate env file
+ *
+ * @time 2019年11月29日
+ * @param $host
+ * @param $database
+ * @param $username
+ * @param $password
+ * @param $port
+ * @param $charset
+ * @param $prefix
+ * @return void
+ */
+ protected function generateEnvFile($host, $database, $username, $password, $port, $charset, $prefix): void
+ {
+ $env = \parse_ini_file(root_path() . '.example.env', true);
+
+ $env['DATABASE']['HOSTNAME'] = $host;
+ $env['DATABASE']['DATABASE'] = $database;
+ $env['DATABASE']['USERNAME'] = $username;
+ $env['DATABASE']['PASSWORD'] = $password;
+ $env['DATABASE']['HOSTPORT'] = $port;
+ $env['DATABASE']['CHARSET'] = $charset;
+ if ($prefix) {
+ $env['DATABASE']['PREFIX'] = $prefix;
+ }
+ $dotEnv = '';
+ foreach ($env as $key => $e) {
+ if (is_string($e)) {
+ $dotEnv .= sprintf('%s = %s', $key, $e === '1' ? 'true' : ($e === '' ? 'false' : $e)) . PHP_EOL;
+ $dotEnv .= PHP_EOL;
+ } else {
+ $dotEnv .= sprintf('[%s]', $key) . PHP_EOL;
+ foreach ($e as $k => $v) {
+ $dotEnv .= sprintf('%s = %s', $k, $v === '1' ? 'true' : ($v === '' ? 'false' : $v)) . PHP_EOL;
+ }
+
+ $dotEnv .= PHP_EOL;
+ }
+ }
+
+ file_put_contents(root_path() . '.env', $dotEnv);
+
+ if ($this->getEnvFile()) {
+ $this->output->info('env file has been generated');
+ }
+
+ if ((new \mysqli($host, $username, $password, null, $port))->query(sprintf('CREATE DATABASE IF NOT EXISTS %s DEFAULT CHARSET %s COLLATE %s_general_ci;',
+ $database, $charset, $charset))) {
+ $this->output->info(sprintf('🎉 create database %s successfully', $database));
+
+ exec(sprintf('%s %s migrate:run', getenv('_'), root_path() . DIRECTORY_SEPARATOR . 'think'));
+
+ $this->output->info('🎉 database table install successfully');
+
+ exec(sprintf('%s %s seed:run', getenv('_'),root_path() . DIRECTORY_SEPARATOR . 'think'));
+
+ $this->output->info('🎉 Fill database table successfully ');
+ } else {
+ $this->dataInstall = false;
+ $this->output->warning(sprintf('create database %s failed, you should create it by yourself', $database));
+ $this->output->warning('you should use `php think migrate:run` to create tables');
+ $this->output->warning('you should use `php think seed:run` to fill tables data');
+ }
+ }
+
+ /**
+ *
+ * @time 2019年11月29日
+ * @return string
+ */
+ protected function getEnvFile(): string
+ {
+ return file_exists(root_path() . '.env') ? root_path() . '.env' : '';
+ }
+
+ /**
+ * 检测根目录
+ *
+ * @time 2019年11月28日
+ * @return bool
+ */
+ protected function checkRootDatabase(): bool
+ {
+ $databasePath = root_path('database');
+
+ if (!is_dir($databasePath)) {
+ if (!mkdir($databasePath, 0777, true) && !is_dir($databasePath)) {
+ throw new \RuntimeException(sprintf('Directory "%s" was not created', $databasePath));
+ }
+ }
+
+ $migrationPath = $databasePath . DIRECTORY_SEPARATOR . 'migrations' . DIRECTORY_SEPARATOR;
+
+ $seedPath = $databasePath . DIRECTORY_SEPARATOR . 'seeds' . DIRECTORY_SEPARATOR;
+
+ if (!is_dir($migrationPath)) {
+ if (!mkdir($migrationPath, 0777, true) && !is_dir($migrationPath)) {
+ throw new \RuntimeException(sprintf('Directory "%s" was not created', $migrationPath));
+ }
+ }
+
+ if (!is_dir($seedPath)) {
+ if (!mkdir($seedPath, 0777, true) && !is_dir($seedPath)) {
+ throw new \RuntimeException(sprintf('Directory "%s" was not created', $seedPath));
+ }
+ }
+
+ return true;
+ }
+
+
+ protected function project()
+ {
+ $year = date('Y');
+
+ $this->output->info('🎉 project is installed, welcome!');
+
+ $this->output->info(sprintf('
+ /-------------------- welcome to use -------------------------\
+| __ __ ___ __ _ |
+| _________ _/ /______/ /_ / | ____/ /___ ___ (_)___ |
+| / ___/ __ `/ __/ ___/ __ \ / /| |/ __ / __ `__ \/ / __ \ |
+| / /__/ /_/ / /_/ /__/ / / / / ___ / /_/ / / / / / / / / / / |
+| \___/\__,_/\__/\___/_/ /_/ /_/ |_\__,_/_/ /_/ /_/_/_/ /_/ |
+| |
+ \ __ __ __ __ _ __ _ __ enjoy it ! _ __ __ __ __ __ __ ___ _ @ 2017 ~ %s
+
+', $year));
+
+ }
+}
diff --git a/extend/catcher/command/MigrateRunCommand.php b/extend/catcher/command/MigrateRunCommand.php
new file mode 100644
index 0000000..ba808b8
--- /dev/null
+++ b/extend/catcher/command/MigrateRunCommand.php
@@ -0,0 +1,64 @@
+setName('catch-migrate:run')
+ ->setDescription('Migrate the database')
+ ->addArgument('module', Argument::REQUIRED, 'migrate the module database')
+ ->addOption('--target', '-t', InputOption::VALUE_REQUIRED, 'The version number to migrate to')
+ ->addOption('--date', '-d', InputOption::VALUE_REQUIRED, 'The date to migrate to')
+ ->setHelp(<<migrate:run command runs all available migrations, optionally up to a specific version
+
+php think catch-migrate:run module
+php think catch-migrate:run -t 20110103081132
+php think catch-migrate:run -d 20110103
+php think catch-migrate:run -v
+
+EOT
+ );
+ }
+
+ protected function execute(Input $input, Output $output)
+ {
+ $this->module = strtolower($input->getArgument('module'));
+ $version = $input->getOption('target');
+ $date = $input->getOption('date');
+
+ // run the migrations
+ $start = microtime(true);
+ if (null !== $date) {
+ $this->migrateToDateTime(new \DateTime($date));
+ } else {
+ $this->migrate($version);
+ }
+ $end = microtime(true);
+
+ $output->writeln('');
+ $output->writeln('All Done. Took ' . sprintf('%.4fs', $end - $start) . '');
+ }
+
+ /**
+ * 获取 migration path
+ *
+ * @time 2019年12月03日
+ * @param $module
+ * @return string
+ */
+ protected function getPath()
+ {
+ return CatchAdmin::moduleMigrationsDirectory($this->module);
+ }
+}
\ No newline at end of file
diff --git a/extend/catcher/command/ModelGeneratorCommand.php b/extend/catcher/command/ModelGeneratorCommand.php
new file mode 100644
index 0000000..66e68e1
--- /dev/null
+++ b/extend/catcher/command/ModelGeneratorCommand.php
@@ -0,0 +1,95 @@
+setName('create:model')
+ ->addArgument('model', Argument::REQUIRED, 'model name')
+ ->addArgument('module', Argument::REQUIRED, 'module name')
+ ->setDescription('create model');
+ }
+
+ protected function execute(Input $input, Output $output)
+ {
+ $model = ucfirst($input->getArgument('model'));
+ $module = strtolower($input->getArgument('module'));
+
+ $table = Str::snake($model);
+
+ $modelFile= CatchAdmin::getModuleModelDirectory($module) . $model . '.php';
+
+ file_put_contents($modelFile, $this->replaceContent([
+ $module, $model, $table, $this->generateFields($this->getTableFields($table))
+ ]));
+
+ if (file_exists($modelFile)) {
+ $output->info(sprintf('%s Create Successfully!', $modelFile));
+ } else {
+ $output->error(sprintf('%s Create Failed!', $modelFile));
+ }
+ }
+
+
+
+ private function getTableFields($table): array
+ {
+ $fields = Db::query('show full columns from ' .
+ config('database.connections.mysql.prefix') . $table);
+
+ $new = [];
+
+ foreach ($fields as $field) {
+ $new[$field['Field']] = $field['Comment'];
+ }
+
+ return $new;
+ }
+
+ private function generateFields($fields)
+ {
+ $f = '';
+ foreach ($fields as $field => $comment) {
+ $f .= sprintf("'%s', // %s" . "\r\n\t\t\t", $field, $comment);
+ }
+
+ return $f;
+ }
+
+ private function replaceContent(array $replace)
+ {
+ return str_replace([
+ '{Module}', '{Class}', '{Name}', '{Field}'
+ ], $replace, $this->content());
+ }
+
+ private function content()
+ {
+ return <<setName('catch:cache')
+ ->setDescription('cache routes, views, services of module');
+ }
+
+ protected function execute(Input $input, Output $output)
+ {
+ CatchAdmin::cacheRoutes();
+ CatchAdmin::cacheServices();
+ CatchAdmin::cacheViews();
+ // 指令输出
+ $output->info('succeed!');
+ }
+}
diff --git a/extend/catcher/event/LoadModuleRoutes.php b/extend/catcher/event/LoadModuleRoutes.php
new file mode 100644
index 0000000..864ab7b
--- /dev/null
+++ b/extend/catcher/event/LoadModuleRoutes.php
@@ -0,0 +1,25 @@
+group(function () use ($router) {
+ include CatchAdmin::getRoutes();
+ });
+ }
+}
diff --git a/extend/catcher/exceptions/LoginFailedException.php b/extend/catcher/exceptions/LoginFailedException.php
new file mode 100644
index 0000000..1470ca7
--- /dev/null
+++ b/extend/catcher/exceptions/LoginFailedException.php
@@ -0,0 +1,7 @@
+ $value) {
+ if (in_array($field, $this->field)) {
+ $this->{$field} = $value;
+ }
+ }
+
+ if ($this->save()) {
+ return $this->id;
+ }
+
+ return false;
+ }
+
+ /**
+ *
+ * @time 2019年12月03日
+ * @param $id
+ * @param $data
+ * @return bool
+ */
+ public function updateBy($id, $data)
+ {
+ $model = $this->findBy($id);
+ foreach ($data as $field => $value) {
+ if (in_array($field, $this->field)) {
+ $model->{$field} = $value;
+ }
+ }
+
+ if ($model->save()) {
+ $model->id;
+ }
+
+ return false;
+ }
+
+ /**
+ *
+ * @time 2019年12月03日
+ * @param $id
+ * @param array $field
+ * @return mixed
+ */
+ public function findBy($id, array $field = ['*'])
+ {
+ return static::where($this->getPk(), $id)->select($field)->find();
+ }
+
+ /**
+ *
+ * @time 2019年12月03日
+ * @param $id
+ * @return mixed
+ */
+ public function deleteBy($id)
+ {
+ return static::destory($id);
+ }
+}
diff --git a/extend/catcher/traits/db/TransTrait.php b/extend/catcher/traits/db/TransTrait.php
new file mode 100644
index 0000000..ec51f5f
--- /dev/null
+++ b/extend/catcher/traits/db/TransTrait.php
@@ -0,0 +1,48 @@
+