diff --git a/.gitignore b/.gitignore index e108b2f..abdd957 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ app/test/ vendor runtime ul.db -/app/tools/controller/Install.php \ No newline at end of file +/app/tools/controller/Install.php +/app/common/command/curd/migrate_output.php \ No newline at end of file diff --git a/app/common/command/curd/Migrate.php b/app/common/command/curd/Migrate.php new file mode 100644 index 0000000..9c8a312 --- /dev/null +++ b/app/common/command/curd/Migrate.php @@ -0,0 +1,202 @@ +setName('curd:migrate') + ->addOption('table', 't', Option::VALUE_REQUIRED, '主表名') + ->addOption('tableName', '', Option::VALUE_OPTIONAL, '要生成的表名') + ->addOption('fileName', '', Option::VALUE_OPTIONAL, '要生成的文件名') + ->addOption('force', 'f', Option::VALUE_NONE, '强制生成') + ->setDescription('the curd:migrate command'); + } + + protected function execute(Input $input, Output $output) + { + // 指令输出 + $output->writeln('curd:migrate'); + + $table = $input->getOption('table'); + $file_name = $input->getOption('fileName'); + $table_name = $input->getOption('tableName'); + $force = $input->getOption('force'); + + if (empty($table)) { + $output->error('请输入表名'); + return; + } + + if (empty($table_name)) { + $table_name = $table; + } + + if (empty($file_name)) { + $file_name = $table_name; + } + $this->table = $table; + + + $this->database = config('database.connections.mysql.database'); + $this->tablePrefix = config('database.connections.mysql.prefix'); + + $table_info_sql = "select * from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA='{$this->database}' and TABLE_NAME='{$this->tablePrefix}{$this->table}'"; + + $table_info = Db::query($table_info_sql); + + if (empty($table_info)) { + $output->error('表不存在'); + return; + } + + $dist_file_path = App::getRootPath() . '/database/migrations/' . date('YmdHis') . '_' . $file_name . '.php'; + + $patt_path = App::getRootPath() . '/database/migrations/*' . $file_name . '.php'; + + $patt_files = glob($patt_path); + + + $is_extis = false; + foreach ($patt_files as $patt_file) { + + + $patt = '/.*database\/migrations\/\d+_' . $file_name . '.php$/'; + + $preg_result = preg_match($patt, $patt_file); + + + if ($preg_result) { + $is_extis = true; + } + } + + if ($is_extis) { + $output->error('文件已存在:' . $patt_files[0]); + if (!$force) { + $confirm_force = $output->confirm($input, '确定要覆盖文件吗?', false); + + if (!$confirm_force) { + return; + } + } + $output->highlight('执行覆盖操作'); + + $dist_file_path = $patt_files[0]; + } + + + $columns = Db::query("SHOW FULL COLUMNS FROM {$this->tablePrefix}{$this->table}"); + + $data['class_name'] = \think\helper\Str::studly($file_name); + + + $table_columns = []; + + $table_keys = []; + $table_keys_text = []; + $table_keys_uni = []; + + foreach ($columns as $column) { + + if ($column['Field'] == 'id') { + continue; + } + $column_item = []; + $column_item['options'] = []; + + $column_item['field'] = $column['Field']; + + $column_item['type'] = ''; + + $type = $column['Type']; + + if (strpos($type, '(') !== false) { + // 带有长度 + + $type_info = explode('(', $type); + $column_item['type'] = $type_info[0]; + + $length = substr($type_info[1], 0, strpos($type_info[1], ')')); + $column_item['options']['limit'] = $length; + + if (strpos($type, 'unsigned') !== false) { + // 无符号 + $column_item['options']['signed'] = 0; + } + } else { + $column_item['type'] = $type; + } + + $column_item['options']['null'] = $column['Null'] == 'YES' ? 1 : 0; + if ($column['Default'] !== null) { + $column_item['options']['default'] = $column['Default']; + } + $column_item['options']['comment'] = $column['Comment']; + + $key = $column['Key']; + + if (!empty($key)) { + if ($key == 'MUL') { + if ($type == 'text' || $type == 'longtext') { + + $table_keys_text[] = $column['Field']; + } else { + $table_keys[] = $column['Field']; + } + } else if ($key == 'UNI') { + $table_keys_uni[] = $column['Field']; + } + } + + $table_columns[] = $column_item; + } + + $type_map = [ + 'bigint' => 'biginteger', + 'int' => 'integer', + 'varchar' => 'string', + 'smallint' => 'integer', + 'tinyint' => 'integer', + 'longtext' => 'text', + ]; + + foreach ($table_columns as &$column_item_set) { + + if (isset($type_map[$column_item_set['type']])) { + $column_item_set['type'] = $type_map[$column_item_set['type']]; + } + } + + + $data['table_info'] = $table_info[0]; + $data['table'] = $table_name; + $data['table_columns'] = $table_columns; + $data['table_keys'] = $table_keys; + $data['table_keys_uni'] = $table_keys_uni; + $data['table_keys_text'] = $table_keys_text; + + + $migrate_content = View::fetch(__DIR__ . '/migrate.tpl', $data); + + file_put_contents(__DIR__ . '/migrate_output.php', "table('{$table}') + ->setComment('{$table_info.TABLE_COMMENT}') + {volist name="table_columns" id="column"}->addColumn('{$column.field}', '{$column.type}', [{volist name="column.options" id="option"}'{$key}' => '{$option}', {/volist}]) + {/volist} + {volist name="table_keys_uni" id="vo"}->addIndex('{$vo}',['unique'=>true]) + {/volist} + {volist name="table_keys_text" id="vo"}->addIndex('{$vo}',['type'=>'fulltext']) + {/volist} + {volist name="table_keys" id="vo"}->addIndex('{$vo}') + {/volist}->create(); + } +} diff --git a/config/console.php b/config/console.php index 47d19fd..1acfddc 100644 --- a/config/console.php +++ b/config/console.php @@ -5,6 +5,7 @@ use app\common\command\admin\Version; use app\common\command\admin\ResetPassword; +use app\common\command\curd\Migrate; use app\common\command\Timer; return [ @@ -15,6 +16,7 @@ return [ 'OssStatic' => 'app\common\command\OssStatic', ResetPassword::class, Timer::class, - Version::class + Version::class, + Migrate::class ], ]; diff --git a/database/migrations/20220905222557_test_goods.php b/database/migrations/20220905222557_test_goods.php new file mode 100644 index 0000000..9dfd8dc --- /dev/null +++ b/database/migrations/20220905222557_test_goods.php @@ -0,0 +1,66 @@ +table('test_goods') + ->setComment('商品列表') + ->addColumn('cate_id', 'biginteger', ['limit' => '20', 'signed' => '0', 'null' => '0', 'default' => '0', 'comment' => '分类ID {relation} (table:mall_cate,relationBindSelect:title)',]) + ->addColumn('title', 'char', ['limit' => '20', 'null' => '0', 'default' => '', 'comment' => '商品名称',]) + ->addColumn('logo', 'char', ['limit' => '255', 'null' => '0', 'comment' => '商品logo {image}',]) + ->addColumn('images', 'text', ['null' => '0', 'comment' => '商品图片 {images}',]) + ->addColumn('describe', 'text', ['null' => '0', 'comment' => '商品描述 {editor}',]) + ->addColumn('total_stock', 'integer', ['limit' => '11', 'signed' => '0', 'null' => '0', 'default' => '0', 'comment' => '总库存',]) + ->addColumn('sort', 'integer', ['limit' => '11', 'signed' => '0', 'null' => '0', 'default' => '0', 'comment' => '排序',]) + ->addColumn('status', 'integer', ['limit' => '1', 'signed' => '0', 'null' => '0', 'default' => '0', 'comment' => '状态 {radio} (0:正常,1:禁用)',]) + ->addColumn('cert_file', 'string', ['limit' => '100', 'null' => '0', 'comment' => '合格证 {file}',]) + ->addColumn('verfiy_file', 'text', ['null' => '0', 'comment' => '检测报告 {files}',]) + ->addColumn('remark', 'char', ['limit' => '255', 'null' => '0', 'default' => '', 'comment' => '备注说明',]) + ->addColumn('create_time', 'integer', ['limit' => '11', 'signed' => '0', 'null' => '0', 'default' => '0', 'comment' => '',]) + ->addColumn('update_time', 'integer', ['limit' => '11', 'signed' => '0', 'null' => '0', 'default' => '0', 'comment' => '',]) + ->addColumn('delete_time', 'integer', ['limit' => '11', 'signed' => '0', 'null' => '0', 'default' => '0', 'comment' => '',]) + ->addColumn('publish_time', 'integer', ['limit' => '10', 'signed' => '0', 'null' => '0', 'comment' => '发布日期 {date} (date)',]) + ->addColumn('sale_time', 'biginteger', ['limit' => '20', 'signed' => '0', 'null' => '0', 'comment' => '售卖日期 {date} (datetime)',]) + ->addColumn('intro', 'string', ['limit' => '100', 'null' => '0', 'comment' => '简介 {textarea}',]) + ->addColumn('time_status', 'integer', ['limit' => '5', 'signed' => '0', 'null' => '0', 'comment' => '秒杀状态 {select} (0:未参加,1:已开始,3:已结束)',]) + ->addColumn('is_recommend', 'integer', ['limit' => '4', 'null' => '0', 'comment' => '是否推荐 {switch} (0:不推荐,1:推荐)',]) + ->addColumn('shop_type', 'string', ['limit' => '100', 'null' => '0', 'comment' => '商品类型 {checkbox} (taobao:淘宝,jd:京东)',]) + ->addColumn('tag', 'string', ['limit' => '100', 'null' => '0', 'comment' => '商品标签 {table} (table:mall_tag,type:checkbox,valueField:id,fieldName:title)',]) + ->addColumn('tag_backup', 'string', ['limit' => '100', 'null' => '1', 'comment' => '商品标签(单选) {table} (table:mall_tag,type:radio,valueField:id,fieldName:title)',]) + ->addColumn('from_area', 'string', ['limit' => '100', 'null' => '0', 'comment' => '产地 {city} (name-province:0,code:0)',]) + ->addColumn('store_city', 'string', ['limit' => '100', 'null' => '0', 'default' => '山东省/临沂市', 'comment' => '仓库 {city} (level:city)',]) + ->addColumn('tag_input', 'string', ['limit' => '100', 'null' => '0', 'comment' => '商品标签 (输入) {tag}',]) + ->addColumn('uid', 'string', ['limit' => '100', 'null' => '0', 'comment' => '唯一id',]) + ->addColumn('price', 'decimal', ['limit' => '10,2', 'null' => '1', 'comment' => '价格',]) + ->addColumn('detail', 'text', ['null' => '1', 'comment' => '详情',]) + ->addIndex('uid', ['unique' => true]) + ->addIndex('detail', ['type' => 'fulltext']) + ->addIndex('cate_id') + ->create(); + } +}