Files
mpay/app/controller/InstallController.php
技术老胡 a05a5710af 1. 优化数据库结构,增强兼容性
2. 添加手机浏览器跳转支付宝收银
3. 添加手机浏览器跳转微信支付提示信息
2025-03-18 11:49:00 +08:00

364 lines
12 KiB
PHP

<?php
declare(strict_types=1);
namespace app\controller;
use think\facade\Db;
use think\Request;
use think\facade\View;
use think\facade\Log;
use think\exception\ValidateException;
use think\Validate;
class InstallController
{
private const INSTALL_LOCK_FILE = 'install.lock';
/**
* 连接数据库
* @return \think\db\Connection
*/
private function connectDatabase()
{
return Db::connect();
}
/**
* 首页,检查是否已安装,若已安装则跳转到登录页,否则显示安装页面
* @return \think\response\Redirect|\think\response\View
*/
public function index()
{
if ($this->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 <<<EOT
APP_DEBUG = false
DB_TYPE = mysql
DB_HOST = {$dbConfig['host']}
DB_NAME = {$dbConfig['name']}
DB_USER = {$dbConfig['user']}
DB_PASS = {$dbConfig['pass']}
DB_PORT = {$dbConfig['port']}
DB_CHARSET = {$dbConfig['charset']}
DB_PREFIX = mpay_
DEFAULT_LANG = zh-cn
EOT;
}
/**
* 创建数据库表
* @throws \Exception
*/
private function createTables()
{
$db = $this->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, '数据库结构更新失败'));
}
}
}