checkLock()) { return redirect('User/login'); } return View::fetch(); } /** * 安装操作,检查环境、保存数据库配置信息 * @param Request $request * @return \think\response\Json */ public function install(Request $request) { if ($this->checkLock()) { return json(backMsg(1, '已经安装')); } $envCheck = $this->checkEnvironment(); if ($envCheck !== true) { return json(backMsg(1, $envCheck)); } $dbConfig = $request->post(); try { $this->validateDbConfig($dbConfig); $this->saveDbConfig($dbConfig); return json(backMsg(0, '配置保存成功')); } catch (ValidateException $e) { return json(backMsg(1, $e->getMessage())); } catch (\Exception $e) { Log::error("保存数据库配置失败: " . $e->getMessage()); return json(backMsg(1, '配置保存失败')); } } /** * 初始化数据库,创建表并初始化数据 * @param Request $request * @return \think\response\Json */ public function init(Request $request) { if ($this->checkLock()) { return json(backMsg(1, '已经安装')); } $dbConfig = $request->post(); $startTime = microtime(true); try { $this->validateInitData($dbConfig); $this->connectDatabase()->transaction(function () use ($dbConfig) { $this->createTables(); $this->initData($dbConfig); }); $this->setLock(); $endTime = microtime(true); Log::info("数据库初始化完成,耗时: " . ($endTime - $startTime) . " 秒"); return json(backMsg(0, '安装成功')); } catch (ValidateException $e) { return json(backMsg(1, $e->getMessage())); } catch (\Exception $e) { Log::error("数据库初始化失败: " . $e->getMessage()); return json(backMsg(1, '数据库初始化失败')); } } /** * 检查环境,包括 PHP 版本、文件上传写入权限、Fileinfo 扩展 * @return bool|string */ private function checkEnvironment() { if (version_compare(PHP_VERSION, '8.0', '<')) { return 'PHP 版本必须大于等于 8.0'; } if (!is_writable(sys_get_temp_dir())) { return '文件上传目录没有写入权限'; } if (!extension_loaded('fileinfo')) { return 'Fileinfo 扩展未安装'; } return true; } /** * 验证数据库配置信息 * @param array $dbConfig * @throws ValidateException */ private function validateDbConfig(array $dbConfig) { $validate = new Validate(); $rule = [ 'host' => 'require', 'name' => 'require', 'user' => 'require', 'pass' => 'require', 'port' => 'require|integer', 'charset' => 'require' ]; if (!$validate->rule($rule)->check($dbConfig)) { throw new ValidateException($validate->getError()); } } /** * 验证初始化数据信息 * @param array $dbConfig * @throws ValidateException */ private function validateInitData(array $dbConfig) { $validate = new Validate(); $rule = [ 'nickname' => 'require', 'username' => 'require', 'password' => 'require' ]; if (!$validate->rule($rule)->check($dbConfig)) { throw new ValidateException($validate->getError()); } } /** * 保存数据库配置信息到 .env 文件 * @param array $dbConfig * @throws \Exception */ private function saveDbConfig(array $dbConfig) { $envPath = app()->getRootPath() . '.env'; $envContent = $this->generateEnvContent($dbConfig); if (file_put_contents($envPath, $envContent) === false) { throw new \Exception("无法写入 .env 文件"); } } /** * 生成 .env 文件内容 * @param array $dbConfig * @return string */ private function generateEnvContent(array $dbConfig): string { return <<connectDatabase(); $tables = $this->getTableCreationSqls(); foreach ($tables as $tableName => $sql) { try { $db->execute("DROP TABLE IF EXISTS `$tableName`;"); $db->execute($sql); Log::info("$tableName 表创建成功"); } catch (\Exception $e) { throw new \Exception("创建 $tableName 表失败: " . $e->getMessage()); } } } /** * 获取表创建的 SQL 语句 * @return array */ private function getTableCreationSqls(): array { return [ 'mpay_order' => "CREATE TABLE `mpay_order` ( `id` int(11) NOT NULL AUTO_INCREMENT, `pid` int(11) NOT NULL DEFAULT 0, `order_id` varchar(255) NOT NULL DEFAULT '', `type` varchar(255) NOT NULL DEFAULT '', `out_trade_no` varchar(255) NOT NULL DEFAULT '', `notify_url` varchar(255) NOT NULL DEFAULT '', `return_url` varchar(255) NOT NULL DEFAULT '', `name` varchar(255) NOT NULL DEFAULT '', `really_price` decimal(10, 2) NOT NULL DEFAULT 0.00, `money` decimal(10, 2) NOT NULL DEFAULT 0.00, `clientip` varchar(255) NOT NULL DEFAULT '', `device` varchar(255) NOT NULL DEFAULT '', `param` varchar(720) NOT NULL DEFAULT '', `state` tinyint(4) NOT NULL DEFAULT 0, `patt` tinyint(4) NOT NULL DEFAULT 0, `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `close_time` datetime DEFAULT NULL, `pay_time` datetime DEFAULT NULL, `platform_order` varchar(255) NOT NULL DEFAULT '', `aid` int(11) NOT NULL DEFAULT 0, `cid` int(11) NOT NULL DEFAULT 0, `delete_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), INDEX `idx_order_id` (`order_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC;", 'mpay_pay_account' => "CREATE TABLE `mpay_pay_account` ( `id` int(11) NOT NULL AUTO_INCREMENT, `pid` int(11) NOT NULL DEFAULT 0, `platform` varchar(255) NOT NULL DEFAULT '', `account` varchar(255) NOT NULL DEFAULT '', `password` varchar(255) NOT NULL DEFAULT '', `state` tinyint(4) NOT NULL DEFAULT 1, `pattern` tinyint(4) NOT NULL DEFAULT 1, `params` text NOT NULL, `delete_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC;", 'mpay_pay_channel' => "CREATE TABLE `mpay_pay_channel` ( `id` int(11) NOT NULL AUTO_INCREMENT, `account_id` int(11) NOT NULL DEFAULT 0, `channel` varchar(255) NOT NULL DEFAULT '', `type` tinyint(4) NOT NULL DEFAULT 0, `qrcode` varchar(255) NOT NULL DEFAULT '', `last_time` datetime DEFAULT CURRENT_TIMESTAMP, `state` tinyint(4) NOT NULL DEFAULT 1, `delete_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC;", 'mpay_user' => "CREATE TABLE `mpay_user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `pid` int(11) NOT NULL DEFAULT 0, `secret_key` varchar(255) NOT NULL DEFAULT '', `nickname` varchar(255) NOT NULL DEFAULT '', `username` varchar(255) NOT NULL DEFAULT '', `password` varchar(255) NOT NULL DEFAULT '', `state` tinyint(4) NOT NULL DEFAULT 1, `role` tinyint(4) NOT NULL DEFAULT 0, `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `delete_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC;" ]; } /** * 初始化数据 * @param array $dbConfig * @throws \Exception */ private function initData(array $dbConfig) { $db = $this->connectDatabase(); $info = [ 'secret_key' => md5(1000 . time() . mt_rand()), 'nickname' => $dbConfig['nickname'], 'username' => $dbConfig['username'], 'password' => password_hash($dbConfig['password'], PASSWORD_DEFAULT), 'create_time' => date('Y-m-d H:i:s'), ]; $sql = "INSERT INTO `mpay_user` (`id`, `pid`, `secret_key`, `nickname`, `username`, `password`, `state`, `role`, `create_time`) VALUES (1, 1000, :secret_key, :nickname, :username, :password, 1, 1, :create_time);"; try { $db->execute($sql, $info); Log::info("mpay_user 表数据初始化成功"); } catch (\Exception $e) { throw new \Exception("初始化 mpay_user 表数据失败: " . $e->getMessage()); } } /** * 检查是否已安装 * @return bool */ private function checkLock() { $path = runtime_path() . self::INSTALL_LOCK_FILE; return file_exists($path); } /** * 设置安装锁 * @throws \Exception */ private function setLock() { $path = runtime_path() . self::INSTALL_LOCK_FILE; if (file_put_contents($path, time()) === false) { throw new \Exception("无法写入安装锁文件"); } } /** * 更新数据库结构 * @return \think\response\Json */ public function update() { if ($this->checkLock()) { return json(backMsg(1, '已经安装')); } $db = $this->connectDatabase(); try { $result = $db->query("SHOW COLUMNS FROM `mpay_pay_account` WHERE Field = 'params'"); if ($result && $result[0]['Type'] != 'text') { $sql = "ALTER TABLE `mpay_pay_account` MODIFY COLUMN `params` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '自定义查询' AFTER `pattern`;"; $is_succ = $db->execute($sql); if (!$is_succ) { throw new \Exception("修改 mpay_pay_account 表 params 列类型失败"); } } return json(backMsg(0, '数据库结构检查并更新完成')); } catch (\Exception $e) { Log::error("更新数据库结构失败: " . $e->getMessage()); return json(backMsg(1, '数据库结构更新失败')); } } }