You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
428 lines
15 KiB
428 lines
15 KiB
<?php
|
|
// +----------------------------------------------------------------------
|
|
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
|
|
// +----------------------------------------------------------------------
|
|
// | Copyright (c) 2016~2020 https://www.crmeb.com All rights reserved.
|
|
// +----------------------------------------------------------------------
|
|
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
|
|
// +----------------------------------------------------------------------
|
|
// | Author: CRMEB Team <admin@crmeb.com>
|
|
// +----------------------------------------------------------------------
|
|
|
|
namespace crmeb\command;
|
|
|
|
use app\services\system\admin\SystemAdminServices;
|
|
use app\services\system\config\SystemConfigServices;
|
|
use crmeb\services\MysqlBackupService;
|
|
use PhpOffice\PhpSpreadsheet\Cell\Cell;
|
|
use think\console\Command;
|
|
use think\console\Input;
|
|
use think\console\input\Argument;
|
|
use think\console\input\Option;
|
|
use think\console\Output;
|
|
use think\console\Table;
|
|
|
|
class Install extends Command
|
|
{
|
|
|
|
protected $host;
|
|
|
|
protected function configure()
|
|
{
|
|
$this->setName('install')
|
|
->addArgument('status', Argument::OPTIONAL, 'start/remove')
|
|
->addOption('h', null, Option::VALUE_REQUIRED, '网站域名')
|
|
->addOption('db', null, Option::VALUE_NONE, '是否卸载数据库')
|
|
->setDescription('命令行一键安装/卸载');
|
|
}
|
|
|
|
protected function execute(Input $input, Output $output)
|
|
{
|
|
$status = $input->getArgument('status') ?: 'start';
|
|
|
|
$database = env('database.database');
|
|
if (in_array($status, ['start', 'remove'])) {
|
|
if (!env('database.hostname') ||
|
|
!$database ||
|
|
!env('database.username') ||
|
|
!env('database.password') ||
|
|
!env('database.hostport')
|
|
) {
|
|
$output->error('请先配置数据库,确保数据库能正常连接!');
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
if ($input->hasOption('h')) {
|
|
$this->host = $input->getOption('h');
|
|
}
|
|
if (!$status || !in_array($status, ['start', 'remove'])) {
|
|
$question = $output->choice($input, ' 请选择执行指令数字 ', ['start' => '1', 'remove' => '-1', 'exit' => 0]);
|
|
if ($question === 'exit') {
|
|
$output->info('您已退出命令');
|
|
return;
|
|
} else {
|
|
$status = $question;
|
|
}
|
|
}
|
|
if ($status === 'remove') {
|
|
$this->remove($input, $output);
|
|
} elseif ($status === 'start') {
|
|
$this->start($input, $output);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* 开始安装
|
|
* @param Input $input
|
|
* @param Output $output
|
|
* @throws \think\db\exception\BindParamException
|
|
*/
|
|
protected function start(Input $input, Output $output)
|
|
{
|
|
$installLockDir = root_path('public') . 'install/install.lock';
|
|
if (file_exists($installLockDir)) {
|
|
$crmeb = get_crmeb_version();
|
|
$question = $output->confirm($input, '您已经安装' . $crmeb . '版本是否重新安装,重新安装会清除掉之前的数据请谨慎操作?', false);
|
|
if ($question) {
|
|
$res = $this->authBackups();
|
|
if (!$res) {
|
|
$output->info('已退出安装程序');
|
|
}
|
|
$database = env('database.database');
|
|
$this->dropTable($database);
|
|
|
|
unlink($installLockDir);
|
|
} else {
|
|
$output->info('已退出安装程序');
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
$installSql = file_get_contents(root_path('public' . DIRECTORY_SEPARATOR . 'install') . 'crmeb.sql');
|
|
if (!$installSql) {
|
|
$output->error('读取安装sql失败,请检查安装sql文件权限,再次尝试安装!');
|
|
return;
|
|
}
|
|
|
|
$this->query($installSql);
|
|
|
|
$this->cleanTable();
|
|
|
|
$this->output->writeln('+---------------------------- [创建管理员] ---------------------------------+');
|
|
$this->output->newLine();
|
|
[$account, $password] = $this->createAdmin();
|
|
|
|
file_put_contents($installLockDir, time());
|
|
|
|
$output->info('安装完成!!请妥善保管您的账号密码!');
|
|
$output->info('账号:' . $account);
|
|
$output->info('密码:' . $password);
|
|
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* 创建账号
|
|
*/
|
|
protected function createAdmin()
|
|
{
|
|
$account = $this->adminAccount();
|
|
$password = $this->adminPassword();
|
|
/** @var SystemAdminServices $service */
|
|
$service = app()->make(SystemAdminServices::class);
|
|
$tablepre = env('database.prefix');
|
|
$this->app->db->query('truncate table ' . $tablepre . 'system_admin');
|
|
try {
|
|
$service->create(['conf_pwd' => $password, 'roles' => [1], 'pwd' => $password, 'account' => $account, 'level' => 0, 'status' => 1]);
|
|
} catch (\Throwable $e) {
|
|
$this->output->writeln($e->getMessage());
|
|
$this->output->newLine();
|
|
[$account, $password] = $this->createAdmin();
|
|
}
|
|
|
|
if ($this->host) {
|
|
/** @var SystemConfigServices $configService */
|
|
$configService = app()->make(SystemConfigServices::class);
|
|
$configService->update('site_url', ['value' => json_encode($this->host)], 'menu_name');
|
|
}
|
|
|
|
return [$account, $password];
|
|
}
|
|
|
|
/**
|
|
* 卸载程序
|
|
* @param Input $input
|
|
* @param Output $output
|
|
* @throws \think\db\exception\BindParamException
|
|
*/
|
|
protected function remove(Input $input, Output $output)
|
|
{
|
|
$installLockDir = root_path('public') . 'install/install.lock';
|
|
if (!file_exists($installLockDir)) {
|
|
$this->output->info('你尚未安装本程序');
|
|
return;
|
|
}
|
|
$database = env('database.database');
|
|
$output->info(' 正在进行卸载中...');
|
|
|
|
$this->authBackups();
|
|
|
|
if ($input->hasOption('db')) {
|
|
$question = $output->confirm($input, '您确定要清除掉[' . $database . ']数据吗?', false);
|
|
if ($question) {
|
|
$this->dropTable($database);
|
|
}
|
|
}
|
|
|
|
unlink($installLockDir);
|
|
|
|
$this->output->info('卸载完成');
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* 清除所有表数据
|
|
* @param string $database
|
|
*/
|
|
protected function dropTable(string $database)
|
|
{
|
|
$this->output->writeln('+---------------------------- [清理表数据] ---------------------------------+');
|
|
$this->output->newLine();
|
|
$this->output->write("\r 正在清理表数据");
|
|
|
|
/** @var MysqlBackupService $service */
|
|
$service = app()->make(MysqlBackupService::class, [[
|
|
//数据库备份卷大小
|
|
'compress' => 1,
|
|
//数据库备份文件是否启用压缩 0不压缩 1 压缩
|
|
'level' => 5,
|
|
]]);
|
|
$dataList = $service->dataList();
|
|
$tableName = array_column($dataList, 'name');
|
|
$count = count($tableName);
|
|
if ($count) {
|
|
$res = $this->app->db->transaction(function () use ($database, $tableName) {
|
|
foreach ($tableName as $name) {
|
|
$this->app->db->query('DROP TABLE ' . $name);
|
|
}
|
|
});
|
|
}
|
|
|
|
$this->output->write("\r 已清理完毕");
|
|
$this->output->newLine(2);
|
|
|
|
return $res;
|
|
}
|
|
|
|
/**
|
|
* 执行安装sql
|
|
* @param string $installSql
|
|
*/
|
|
protected function query(string $installSql)
|
|
{
|
|
$tablepre = env('database.prefix');
|
|
$sqlArray = $this->sqlSplit($installSql, $tablepre);
|
|
$table = new Table();
|
|
$this->output->writeln('+----------------------------- [SQL安装] -----------------------------------+');
|
|
$this->output->newLine();
|
|
$header = ['表名', '执行结果', '错误原因', '时间'];
|
|
$table->setHeader($header);
|
|
foreach ($sqlArray as $sql) {
|
|
$sql = trim($sql);
|
|
if (strstr($sql, 'CREATE TABLE')) {
|
|
preg_match('/CREATE TABLE (IF NOT EXISTS)? `eb_([^ ]*)`/is', $sql, $matches);
|
|
$tableName = $tablepre . ($matches[2] ?? '');
|
|
} else {
|
|
$tableName = '';
|
|
}
|
|
try {
|
|
$this->app->db->transaction(function () use ($tablepre, $sql, $tableName) {
|
|
|
|
$sql = str_replace('`eb_', '`' . $tablepre, $sql);//替换表前缀
|
|
$this->app->db->query($sql);
|
|
|
|
});
|
|
|
|
$tableName && $table->addRow([$tableName, 'ok', '无错误', date('Y-m-d H:i:s')]);
|
|
|
|
} catch (\Throwable $e) {
|
|
$tableName && $table->addRow([$tableName, 'x', $e->getMessage(), date('Y-m-d H:i:s')]);
|
|
}
|
|
}
|
|
$this->output->writeln($table->render());
|
|
$this->output->newLine(2);
|
|
}
|
|
|
|
/**
|
|
* 账号
|
|
* @return bool|mixed|string
|
|
*/
|
|
protected function adminAccount()
|
|
{
|
|
$account = $this->output->ask($this->input, '请输入后台登陆账号,最少4个字符', null, function ($value) {
|
|
if (strlen($value) < 4) {
|
|
return false;
|
|
}
|
|
return $value;
|
|
});
|
|
if (!$account) {
|
|
$this->output->error('账号至少4个字符');
|
|
$this->output->newLine();
|
|
$account = $this->adminAccount();
|
|
}
|
|
return $account;
|
|
}
|
|
|
|
/**
|
|
* 密码
|
|
* @return bool|mixed|string|null
|
|
*/
|
|
protected function adminPassword()
|
|
{
|
|
$password = $this->output->ask($this->input, '请输入登陆密码,密码为数字加字母/字母加符号/数字加字符的组合不能少于6位');
|
|
if (!preg_match('/^(?![a-zA-z]+$)(?!\d+$)(?![!@#$%^<>&*]+$)[a-zA-Z\d!@#$%^&<>*]{6,16}$/', $password)) {
|
|
$this->output->error('请输入符合要求的密码');
|
|
$this->output->newLine();
|
|
$password = $this->adminPassword();
|
|
}
|
|
return $password;
|
|
}
|
|
|
|
/**
|
|
* 清楚多余数据
|
|
*/
|
|
protected function cleanTable()
|
|
{
|
|
$tablepre = env('database.prefix');
|
|
$database = env('database.database');
|
|
$blTable = ['eb_system_admin', 'eb_system_role', 'eb_system_config', 'eb_system_config_tab',
|
|
'eb_system_menus', 'eb_system_file', 'eb_express', 'eb_system_group', 'eb_system_group_data',
|
|
'eb_template_message', 'eb_shipping_templates', "eb_shipping_templates_region",
|
|
"eb_shipping_templates_free", 'eb_system_city', 'eb_diy', 'eb_member_ship', 'eb_member_right',
|
|
'eb_agreement', 'eb_store_service_speechcraft', 'eb_system_user_level', 'eb_cache'];
|
|
if ($tablepre !== 'eb_') {
|
|
$blTable = array_map(function ($name) use ($tablepre) {
|
|
return str_replace('eb_', $tablepre, $name);
|
|
}, $blTable);
|
|
}
|
|
$tableList = $this->app->db->query("select table_name from information_schema.tables where table_schema='$database'");
|
|
$tableList = array_column($tableList, 'table_name');
|
|
foreach ($tableList as $table) {
|
|
if (!in_array($table, $blTable)) {
|
|
$this->app->db->query('truncate table ' . $table);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 切割sql
|
|
* @param $sql
|
|
* @return array
|
|
*/
|
|
protected function sqlSplit(string $sql, string $tablepre)
|
|
{
|
|
if ($tablepre != "tp_")
|
|
$sql = str_replace("tp_", $tablepre, $sql);
|
|
|
|
$sql = preg_replace("/TYPE=(InnoDB|MyISAM|MEMORY)( DEFAULT CHARSET=[^; ]+)?/", "ENGINE=\\1 DEFAULT CHARSET=utf8", $sql);
|
|
|
|
$sql = str_replace("\r", "\n", $sql);
|
|
$ret = [];
|
|
$num = 0;
|
|
$queriesarray = explode(";\n", trim($sql));
|
|
unset($sql);
|
|
foreach ($queriesarray as $query) {
|
|
$ret[$num] = '';
|
|
$queries = explode("\n", trim($query));
|
|
$queries = array_filter($queries);
|
|
foreach ($queries as $query) {
|
|
$str1 = substr($query, 0, 1);
|
|
if ($str1 != '#' && $str1 != '-')
|
|
$ret[$num] .= $query;
|
|
}
|
|
$num++;
|
|
}
|
|
return $ret;
|
|
}
|
|
|
|
/**
|
|
* 自动备份表
|
|
* @return bool|mixed|string|null
|
|
* @throws \think\db\exception\BindParamException
|
|
*/
|
|
protected function authBackups(bool $g = false)
|
|
{
|
|
/** @var MysqlBackupService $service */
|
|
$service = app()->make(MysqlBackupService::class, [[
|
|
//数据库备份卷大小
|
|
'compress' => 1,
|
|
//数据库备份文件是否启用压缩 0不压缩 1 压缩
|
|
'level' => 5,
|
|
]]);
|
|
$dataList = $service->dataList();
|
|
$tableName = array_column($dataList, 'name');
|
|
$count = count($tableName);
|
|
if ($count) {
|
|
$this->output->writeln('+----------------------------- [自动备份] ----------------------------------+');
|
|
$this->output->newLine();
|
|
$this->output->newLine();
|
|
$this->output->writeln(' 正在自动备份[start]');
|
|
|
|
$data = [];
|
|
foreach ($tableName as $i => $t) {
|
|
|
|
// $equalStr = str_repeat("=", $i);
|
|
// $space = str_repeat(" ", $count - $i);
|
|
// $this->output->write("\r [$equalStr>$space]($i/$count%)");
|
|
$this->output->writeln(' 已备份:' . $t . ' 完成:(' . ($i + 1) . '/' . $count . ')');
|
|
|
|
$res = $service->backup($t, 0);
|
|
if ($res == false && $res != 0) {
|
|
$data [] = $t;
|
|
}
|
|
}
|
|
|
|
$this->output->writeln("\r\n 备份结束[end]");
|
|
|
|
if ($data && $g) {
|
|
return $this->output->confirm($this->input, '自动备份表失败,失败数据库表:' . implode('|', $data) . ';是否继续执行?');
|
|
}
|
|
$this->output->newLine();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 创建进度条
|
|
* @param $percent
|
|
* @return string
|
|
*/
|
|
protected function buildLine($percent)
|
|
{
|
|
$repeatTimes = 100;
|
|
if ($percent > 0) {
|
|
$hasColor = str_repeat('■', $percent);
|
|
} else {
|
|
$hasColor = '';
|
|
}
|
|
|
|
if ($repeatTimes - $percent > 0) {
|
|
$noColor = str_repeat(' ', $repeatTimes - $percent);
|
|
} else {
|
|
$noColor = '';
|
|
}
|
|
|
|
$buffer = sprintf("[{$hasColor}{$noColor}]");
|
|
if ($percent !== 100) {
|
|
$percentString = sprintf("[ %-6s]", $percent . '%');
|
|
} else {
|
|
$percentString = sprintf("[ %-5s]", 'OK');;
|
|
}
|
|
|
|
return $percentString . $buffer . "\r";
|
|
}
|
|
}
|
|
|