diff --git a/.env-example b/.env-example index 4db777e..24fc733 100644 --- a/.env-example +++ b/.env-example @@ -8,7 +8,7 @@ APP_DEFAULT_TIMEZONE = Asia/Shanghai # 语言 LANG_DEFAULT_LANG = zh-cn -# 数据库(config/thinkorm.php) +# 数据库(config/thinkorm.php/database.php) DATABASE_DRIVER = mysql DATABASE_TYPE = mysql DATABASE_HOSTNAME = 127.0.0.1 diff --git a/README.md b/README.md index 98aa6cb..f06a1ca 100644 --- a/README.md +++ b/README.md @@ -159,24 +159,32 @@ php webman migrate ### 5.5 启动 -**Linux / Mac:** +**开发环境需同时启动后端与前端:** -```bash -php start.php start -``` +1. **启动后端(API 服务,端口 8787):** -**Windows:** + **Linux / Mac:** + ```bash + php start.php start + ``` -```bash -php windows.php -``` + **Windows:** + ```bash + php windows.php + ``` -### 5.6 前端开发 +2. **启动前端(Vue 开发服务,端口 1818):** + ```bash + cd web + pnpm dev + ``` -```bash -cd web -pnpm dev -``` +3. **访问地址:** + - 安装向导:http://localhost:1818/install/ + - 前台地址:http://localhost:1818/index.html/#/ + - 后台地址:http://localhost:1818/index.html/#/admin + +> 注意:前端通过 Vite 代理将 `/api`、`/admin`、`/install` 转发到后端 8787 端口,请勿直接访问 8787 端口的前端页面,否则可能出现 404。 --- diff --git a/app/admin/model/Admin.php b/app/admin/model/Admin.php index 4f2399e..b368554 100644 --- a/app/admin/model/Admin.php +++ b/app/admin/model/Admin.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace app\admin\model; +use app\common\model\traits\TimestampInteger; use support\think\Model; use support\think\Db; @@ -23,6 +24,8 @@ use support\think\Db; */ class Admin extends Model { + use TimestampInteger; + protected string $table = 'admin'; protected string $pk = 'id'; protected bool $autoWriteTimestamp = true; diff --git a/app/admin/model/AdminGroup.php b/app/admin/model/AdminGroup.php index 9b3dcf3..de6f842 100644 --- a/app/admin/model/AdminGroup.php +++ b/app/admin/model/AdminGroup.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace app\admin\model; +use app\common\model\traits\TimestampInteger; use support\think\Model; /** @@ -11,6 +12,8 @@ use support\think\Model; */ class AdminGroup extends Model { + use TimestampInteger; + protected string $table = 'admin_group'; protected string $pk = 'id'; protected bool $autoWriteTimestamp = true; diff --git a/app/admin/model/AdminLog.php b/app/admin/model/AdminLog.php index 74ed0d6..ecfac3e 100644 --- a/app/admin/model/AdminLog.php +++ b/app/admin/model/AdminLog.php @@ -6,6 +6,7 @@ namespace app\admin\model; use Throwable; use app\admin\library\Auth; +use app\common\model\traits\TimestampInteger; use support\think\Model; use Webman\Http\Request; @@ -14,6 +15,8 @@ use Webman\Http\Request; */ class AdminLog extends Model { + use TimestampInteger; + protected string $table = 'admin_log'; protected string $pk = 'id'; protected bool $autoWriteTimestamp = true; diff --git a/app/admin/model/AdminRule.php b/app/admin/model/AdminRule.php index aba24a1..c4ef0ae 100644 --- a/app/admin/model/AdminRule.php +++ b/app/admin/model/AdminRule.php @@ -4,10 +4,13 @@ declare(strict_types=1); namespace app\admin\model; +use app\common\model\traits\TimestampInteger; use support\think\Model; class AdminRule extends Model { + use TimestampInteger; + protected string $table = 'admin_rule'; protected string $pk = 'id'; protected bool $autoWriteTimestamp = true; diff --git a/app/admin/model/Config.php b/app/admin/model/Config.php index 564d07f..f5afd8b 100644 --- a/app/admin/model/Config.php +++ b/app/admin/model/Config.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace app\admin\model; +use app\common\model\traits\TimestampInteger; use support\think\Model; /** @@ -11,6 +12,8 @@ use support\think\Model; */ class Config extends Model { + use TimestampInteger; + public static string $cacheTag = 'sys_config'; protected string $table = 'config'; diff --git a/app/admin/model/CrudLog.php b/app/admin/model/CrudLog.php index 5c199e6..26c0af2 100644 --- a/app/admin/model/CrudLog.php +++ b/app/admin/model/CrudLog.php @@ -14,6 +14,8 @@ class CrudLog extends Model protected bool $updateTime = false; protected array $type = [ + 'create_time' => 'integer', + 'update_time' => 'integer', 'table' => 'array', 'fields' => 'array', ]; diff --git a/app/admin/model/DataRecycle.php b/app/admin/model/DataRecycle.php index 2ae544f..80ba3ee 100644 --- a/app/admin/model/DataRecycle.php +++ b/app/admin/model/DataRecycle.php @@ -4,10 +4,13 @@ declare(strict_types=1); namespace app\admin\model; +use app\common\model\traits\TimestampInteger; use support\think\Model; class DataRecycle extends Model { + use TimestampInteger; + protected string $table = 'security_data_recycle'; protected string $pk = 'id'; protected bool $autoWriteTimestamp = true; diff --git a/app/admin/model/DataRecycleLog.php b/app/admin/model/DataRecycleLog.php index fe51f05..63cdc17 100644 --- a/app/admin/model/DataRecycleLog.php +++ b/app/admin/model/DataRecycleLog.php @@ -4,11 +4,14 @@ declare(strict_types=1); namespace app\admin\model; +use app\common\model\traits\TimestampInteger; use support\think\Model; use think\model\relation\BelongsTo; class DataRecycleLog extends Model { + use TimestampInteger; + protected string $table = 'security_data_recycle_log'; protected string $pk = 'id'; protected bool $autoWriteTimestamp = true; diff --git a/app/admin/model/SensitiveData.php b/app/admin/model/SensitiveData.php index 28c7761..19bc86a 100644 --- a/app/admin/model/SensitiveData.php +++ b/app/admin/model/SensitiveData.php @@ -13,6 +13,8 @@ class SensitiveData extends Model protected bool $autoWriteTimestamp = true; protected array $type = [ + 'create_time' => 'integer', + 'update_time' => 'integer', 'data_fields' => 'array', ]; } diff --git a/app/admin/model/SensitiveDataLog.php b/app/admin/model/SensitiveDataLog.php index c379380..924601f 100644 --- a/app/admin/model/SensitiveDataLog.php +++ b/app/admin/model/SensitiveDataLog.php @@ -4,11 +4,14 @@ declare(strict_types=1); namespace app\admin\model; +use app\common\model\traits\TimestampInteger; use support\think\Model; use think\model\relation\BelongsTo; class SensitiveDataLog extends Model { + use TimestampInteger; + protected string $table = 'security_sensitive_data_log'; protected string $pk = 'id'; protected bool $autoWriteTimestamp = true; diff --git a/app/admin/model/User.php b/app/admin/model/User.php index bf17d3d..dd623e6 100644 --- a/app/admin/model/User.php +++ b/app/admin/model/User.php @@ -4,11 +4,14 @@ declare(strict_types=1); namespace app\admin\model; +use app\common\model\traits\TimestampInteger; use support\think\Model; use think\model\relation\BelongsTo; class User extends Model { + use TimestampInteger; + protected string $table = 'user'; protected string $pk = 'id'; protected bool $autoWriteTimestamp = true; diff --git a/app/admin/model/UserGroup.php b/app/admin/model/UserGroup.php index 3a10f0a..ebaf7a2 100644 --- a/app/admin/model/UserGroup.php +++ b/app/admin/model/UserGroup.php @@ -4,10 +4,13 @@ declare(strict_types=1); namespace app\admin\model; +use app\common\model\traits\TimestampInteger; use support\think\Model; class UserGroup extends Model { + use TimestampInteger; + protected string $table = 'user_group'; protected string $pk = 'id'; protected bool $autoWriteTimestamp = true; diff --git a/app/admin/model/UserMoneyLog.php b/app/admin/model/UserMoneyLog.php index 0efbea9..08b2c47 100644 --- a/app/admin/model/UserMoneyLog.php +++ b/app/admin/model/UserMoneyLog.php @@ -2,11 +2,14 @@ namespace app\admin\model; +use app\common\model\traits\TimestampInteger; use support\think\Model; use think\model\relation\BelongsTo; class UserMoneyLog extends Model { + use TimestampInteger; + protected string $table = 'user_money_log'; protected string $pk = 'id'; protected bool $autoWriteTimestamp = true; diff --git a/app/admin/model/UserRule.php b/app/admin/model/UserRule.php index 64835d1..6c16941 100644 --- a/app/admin/model/UserRule.php +++ b/app/admin/model/UserRule.php @@ -4,10 +4,13 @@ declare(strict_types=1); namespace app\admin\model; +use app\common\model\traits\TimestampInteger; use support\think\Model; class UserRule extends Model { + use TimestampInteger; + protected string $table = 'user_rule'; protected string $pk = 'id'; protected bool $autoWriteTimestamp = true; diff --git a/app/admin/model/UserScoreLog.php b/app/admin/model/UserScoreLog.php index a9f6438..64c4490 100644 --- a/app/admin/model/UserScoreLog.php +++ b/app/admin/model/UserScoreLog.php @@ -2,11 +2,14 @@ namespace app\admin\model; +use app\common\model\traits\TimestampInteger; use support\think\Model; use think\model\relation\BelongsTo; class UserScoreLog extends Model { + use TimestampInteger; + protected string $table = 'user_score_log'; protected string $pk = 'id'; protected bool $autoWriteTimestamp = true; diff --git a/app/admin/model/mall/Player.php b/app/admin/model/mall/Player.php index 2a6b6d6..5c47204 100644 --- a/app/admin/model/mall/Player.php +++ b/app/admin/model/mall/Player.php @@ -2,6 +2,7 @@ namespace app\admin\model\mall; +use app\common\model\traits\TimestampInteger; use support\think\Model; /** @@ -9,6 +10,8 @@ use support\think\Model; */ class Player extends Model { + use TimestampInteger; + // 表名 protected $name = 'mall_player'; diff --git a/app/api/controller/Install.php b/app/api/controller/Install.php index f8ff422..6a9031d 100644 --- a/app/api/controller/Install.php +++ b/app/api/controller/Install.php @@ -14,6 +14,10 @@ use app\admin\model\Admin as AdminModel; use app\admin\model\User as UserModel; use support\Response; use Webman\Http\Request; +use Phinx\Config\Config as PhinxConfig; +use Phinx\Migration\Manager as PhinxManager; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\NullOutput; /** * 安装控制器 @@ -74,7 +78,7 @@ class Install extends Api { $this->setRequest($request); if ($this->isInstallComplete()) { - return $this->error(__('The system has completed installation. If you need to reinstall, please delete the %s file first', ['public/' . self::$lockFileName])); + return $this->error(__('The system has completed installation. If you need to reinstall, please delete the %file% file first', ['%file%' => 'public/' . self::$lockFileName])); } (new Terminal())->exec(false); @@ -85,7 +89,7 @@ class Install extends Api { $this->setRequest($request); if ($this->isInstallComplete()) { - return $this->error(__('The system has completed installation. If you need to reinstall, please delete the %s file first', ['public/' . self::$lockFileName])); + return $this->error(__('The system has completed installation. If you need to reinstall, please delete the %file% file first', ['%file%' => 'public/' . self::$lockFileName])); } $newPackageManager = $request->post('manager', config('terminal.npm_package_manager')); @@ -104,7 +108,7 @@ class Install extends Api { $this->setRequest($request); if ($this->isInstallComplete()) { - return $this->error(__('The system has completed installation. If you need to reinstall, please delete the %s file first', ['public/' . self::$lockFileName]), []); + return $this->error(__('The system has completed installation. If you need to reinstall, please delete the %file% file first', ['%file%' => 'public/' . self::$lockFileName]), []); } if (($_ENV['DATABASE_TYPE'] ?? getenv('DATABASE_TYPE'))) { return $this->error(__('The .env file with database configuration was detected. Please clean up and try again!')); @@ -410,7 +414,7 @@ class Install extends Api { $this->setRequest($request); if ($this->isInstallComplete()) { - return $this->error(__('The system has completed installation. If you need to reinstall, please delete the %s file first', ['public/' . self::$lockFileName])); + return $this->error(__('The system has completed installation. If you need to reinstall, please delete the %file% file first', ['%file%' => 'public/' . self::$lockFileName])); } $envOk = $this->commandExecutionCheck(); @@ -456,27 +460,32 @@ class Install extends Api return $this->error(__('File has no write permission:%s', ['config/' . self::$dbConfigFileName])); } - // 写入 dafuweng-webman/.env-example - $envFile = root_path() . '.env-example'; - $envFileContent = @file_get_contents($envFile); - if ($envFileContent) { - $databasePos = stripos($envFileContent, '[DATABASE]'); - if ($databasePos !== false) { - $envFileContent = substr($envFileContent, 0, $databasePos); - } - $envFileContent .= "\n" . '[DATABASE]' . "\n"; - $envFileContent .= 'TYPE = mysql' . "\n"; - $envFileContent .= 'HOSTNAME = ' . $databaseParam['hostname'] . "\n"; - $envFileContent .= 'DATABASE = ' . $databaseParam['database'] . "\n"; - $envFileContent .= 'USERNAME = ' . $databaseParam['username'] . "\n"; - $envFileContent .= 'PASSWORD = ' . $databaseParam['password'] . "\n"; - $envFileContent .= 'HOSTPORT = ' . $databaseParam['hostport'] . "\n"; - $envFileContent .= 'PREFIX = ' . ($databaseParam['prefix'] ?? '') . "\n"; - $envFileContent .= 'CHARSET = utf8mb4' . "\n"; - $envFileContent .= 'DEBUG = true' . "\n"; - $result = @file_put_contents($envFile, $envFileContent); - if (!$result) { - return $this->error(__('File has no write permission:%s', ['/' . $envFile])); + // 写入 .env 和 .env-example(仅使用 Dotenv 可解析的 DATABASE_XXX 格式,避免 [DATABASE] 导致解析失败) + $databaseBlock = "\n# Database\n" + . 'DATABASE_TYPE = mysql' . "\n" + . 'DATABASE_HOSTNAME = ' . $databaseParam['hostname'] . "\n" + . 'DATABASE_DATABASE = ' . $databaseParam['database'] . "\n" + . 'DATABASE_USERNAME = ' . $databaseParam['username'] . "\n" + . 'DATABASE_PASSWORD = ' . $databaseParam['password'] . "\n" + . 'DATABASE_HOSTPORT = ' . $databaseParam['hostport'] . "\n" + . 'DATABASE_CHARSET = utf8mb4' . "\n" + . 'DATABASE_PREFIX = ' . ($databaseParam['prefix'] ?? '') . "\n"; + foreach (['.env', '.env-example'] as $envName) { + $envFile = root_path() . $envName; + $envFileContent = is_file($envFile) ? @file_get_contents($envFile) : ''; + if ($envFileContent !== false) { + $cutPos = strlen($envFileContent); + foreach (['[DATABASE]', "\n# Database\n", "\n# 数据库", "\nDATABASE_DRIVER", "\nDATABASE_TYPE"] as $marker) { + $pos = stripos($envFileContent, $marker); + if ($pos !== false && $pos < $cutPos) { + $cutPos = $pos; + } + } + $envFileContent = rtrim(substr($envFileContent, 0, $cutPos)) . $databaseBlock; + $result = @file_put_contents($envFile, $envFileContent); + if (!$result && is_file($envFile)) { + return $this->error(__('File has no write permission:%s', ['%s' => $envName])); + } } } @@ -500,13 +509,66 @@ class Install extends Api return $this->error(__('File has no write permission:%s', ['public/' . self::$lockFileName])); } + // 自动执行数据库迁移(无需手动运行 phinx 命令) + $migrateResult = $this->runPhinxMigrate($databaseParam); + if ($migrateResult !== true) { + return $this->error($migrateResult); + } + return $this->success('', [ - 'rootPath' => $rootPath, - 'executionWebCommand' => $envOk, - 'migrateCommand' => $migrateCommand, + 'rootPath' => $rootPath, + 'executionWebCommand' => $envOk, + 'migrateCommand' => $migrateCommand, + 'migrationCompleted' => true, ]); } + /** + * 程序化执行 Phinx 数据库迁移 + * @param array $databaseParam 数据库连接参数 + * @return true|string 成功返回 true,失败返回错误信息 + */ + private function runPhinxMigrate(array $databaseParam): true|string + { + try { + $baseDir = root_path(); + $phinxConfigPath = $baseDir . 'phinx.php'; + + if (!is_file($phinxConfigPath)) { + return __('Failed to install SQL execution:%msg%', ['%msg%' => 'phinx.php not found']); + } + + // 临时设置环境变量,供 phinx 读取数据库配置 + $_ENV['DATABASE_HOSTNAME'] = $databaseParam['hostname'] ?? '127.0.0.1'; + $_ENV['DATABASE_DATABASE'] = $databaseParam['database'] ?? ''; + $_ENV['DATABASE_USERNAME'] = $databaseParam['username'] ?? 'root'; + $_ENV['DATABASE_PASSWORD'] = $databaseParam['password'] ?? ''; + $_ENV['DATABASE_HOSTPORT'] = $databaseParam['hostport'] ?? '3306'; + $_ENV['DATABASE_PREFIX'] = $databaseParam['prefix'] ?? ''; + putenv('DATABASE_HOSTNAME=' . $_ENV['DATABASE_HOSTNAME']); + putenv('DATABASE_DATABASE=' . $_ENV['DATABASE_DATABASE']); + putenv('DATABASE_USERNAME=' . $_ENV['DATABASE_USERNAME']); + putenv('DATABASE_PASSWORD=' . $_ENV['DATABASE_PASSWORD']); + putenv('DATABASE_HOSTPORT=' . $_ENV['DATABASE_HOSTPORT']); + putenv('DATABASE_PREFIX=' . $_ENV['DATABASE_PREFIX']); + + $config = PhinxConfig::fromPhp($phinxConfigPath); + $input = new ArrayInput([]); + $output = new NullOutput(); + $manager = new PhinxManager($config, $input, $output); + + $environment = $config->getDefaultEnvironment(); + $manager->migrate($environment); + return true; + } catch (Throwable $e) { + $msg = $e->getMessage(); + if ($e->getPrevious()) { + $msg .= ' | ' . $e->getPrevious()->getMessage(); + } + return __('Failed to install SQL execution:%msg%', ['%msg%' => $msg]); + } + } + protected function isInstallComplete(): bool { if (is_file(public_path(self::$lockFileName))) { @@ -526,7 +588,7 @@ class Install extends Api { $this->setRequest($request); if ($this->isInstallComplete()) { - return $this->error(__('The system has completed installation. If you need to reinstall, please delete the %s file first', ['public/' . self::$lockFileName])); + return $this->error(__('The system has completed installation. If you need to reinstall, please delete the %file% file first', ['%file%' => 'public/' . self::$lockFileName])); } $param = $request->only(['type', 'adminname', 'adminpassword', 'sitename']); @@ -586,6 +648,36 @@ class Install extends Api return $envOk; } + /** + * 获取安装完成后的访问地址(根据请求来源区分 API 与前端开发模式) + * - 通过 API 访问(8787):index.html#/admin、index.html#/ + * - 通过前端开发服务访问(1818):/#/admin、/#/ + */ + public function accessUrls(Request $request): Response + { + $this->setRequest($request); + $host = $request->header('host', '127.0.0.1:8787'); + $port = '8787'; + if (str_contains($host, ':')) { + $port = substr($host, strrpos($host, ':') + 1); + } + $scheme = $request->header('x-forwarded-proto', 'http'); + $base = rtrim($scheme . '://' . $host, '/'); + + if ($port === '1818') { + $adminUrl = $base . '/#/admin'; + $frontUrl = $base . '/#/'; + } else { + $adminUrl = $base . '/index.html#/admin'; + $frontUrl = $base . '/index.html#/'; + } + + return $this->success('', [ + 'adminUrl' => $adminUrl, + 'frontUrl' => $frontUrl, + ]); + } + /** * 安装指引 */ diff --git a/app/api/lang/en/install.php b/app/api/lang/en/install.php index 0175147..6c1180c 100644 --- a/app/api/lang/en/install.php +++ b/app/api/lang/en/install.php @@ -18,9 +18,9 @@ return [ 'already installed' => 'Installed', 'Not installed' => 'Not installed', 'File has no write permission:%s' => 'File has no write permission:%s', - 'The system has completed installation. If you need to reinstall, please delete the %s file first' => 'The system has been installed, if you need to reinstall, please delete the %s file first.', + 'The system has completed installation. If you need to reinstall, please delete the %file% file first' => 'The system has been installed. If you need to reinstall, please delete the %file% file first.', 'Database connection failed:%s' => 'Database connection failure:%s', - 'Failed to install SQL execution:%s' => 'Installation SQL execution failed:%s', + 'Failed to install SQL execution:%msg%' => 'Installation SQL execution failed: %msg%', 'unknown' => 'Unknown', 'Database does not exist' => 'Database does not exist!', 'No built front-end file found, please rebuild manually!' => 'No built front-end file found, please rebuild manually.', diff --git a/app/api/lang/zh-cn/install.php b/app/api/lang/zh-cn/install.php index 18117ea..472200c 100644 --- a/app/api/lang/zh-cn/install.php +++ b/app/api/lang/zh-cn/install.php @@ -18,9 +18,9 @@ return [ 'already installed' => '已安装', 'Not installed' => '未安装', 'File has no write permission:%s' => '文件无写入权限:%s', - 'The system has completed installation. If you need to reinstall, please delete the %s file first' => '系统已完成安装。如果需要重新安装,请先删除 %s 文件', + 'The system has completed installation. If you need to reinstall, please delete the %file% file first' => '系统已完成安装。如果需要重新安装,请先删除 %file% 文件', 'Database connection failed:%s' => '数据库连接失败:%s', - 'Failed to install SQL execution:%s' => '安装SQL执行失败:%s', + 'Failed to install SQL execution:%msg%' => '安装SQL执行失败:%msg%', 'unknown' => '未知', 'Database does not exist' => '数据库不存在!', 'No built front-end file found, please rebuild manually!' => '没有找到构建好的前端文件,请手动重新构建!', diff --git a/app/common/middleware/LoadLangPack.php b/app/common/middleware/LoadLangPack.php index a51a14f..8a3d29f 100644 --- a/app/common/middleware/LoadLangPack.php +++ b/app/common/middleware/LoadLangPack.php @@ -26,12 +26,20 @@ class LoadLangPack implements MiddlewareInterface protected function loadLang(Request $request): void { // 优先从请求头 think-lang 获取前端选择的语言(与前端 axios 发送的 header 对应) + // 安装页等未发送 think-lang 时,回退到 Accept-Language 或配置默认值 $headerLang = $request->header('think-lang'); $allowLangList = config('lang.allow_lang_list', ['zh-cn', 'en']); if ($headerLang && in_array(str_replace('_', '-', strtolower($headerLang)), $allowLangList)) { $langSet = str_replace('_', '-', strtolower($headerLang)); } else { - $langSet = config('lang.default_lang', config('translation.locale', 'zh-cn')); + $acceptLang = $request->header('accept-language', ''); + if (preg_match('/^zh[-_]?cn|^zh/i', $acceptLang)) { + $langSet = 'zh-cn'; + } elseif (preg_match('/^en/i', $acceptLang)) { + $langSet = 'en'; + } else { + $langSet = config('lang.default_lang', config('translation.locale', 'zh-cn')); + } $langSet = str_replace('_', '-', strtolower($langSet)); } @@ -59,6 +67,7 @@ class LoadLangPack implements MiddlewareInterface } // 2. 加载控制器专用语言包(如 zh-cn/auth/group.php),供 get_route_remark 等使用 + // 同时加载到 messages 域,使 __() 能正确翻译控制器内的文案(如安装页错误提示) $controllerPath = get_controller_path($request); if ($controllerPath) { $controllerPathForFile = str_replace('.', '/', $controllerPath); @@ -68,6 +77,7 @@ class LoadLangPack implements MiddlewareInterface $controllerLangFile = $appLangDir . $langSet . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $controllerPathForFile) . '.php'; if (is_file($controllerLangFile)) { $translator->addResource('phpfile', $controllerLangFile, $langSet, $controllerPath); + $translator->addResource('phpfile', $controllerLangFile, $langSet, 'messages'); } } } diff --git a/app/common/model/Attachment.php b/app/common/model/Attachment.php index ee61446..be2786f 100644 --- a/app/common/model/Attachment.php +++ b/app/common/model/Attachment.php @@ -4,11 +4,14 @@ declare(strict_types=1); namespace app\common\model; +use app\common\model\traits\TimestampInteger; use support\think\Model; use think\model\relation\BelongsTo; class Attachment extends Model { + use TimestampInteger; + protected string $table = 'attachment'; protected string $pk = 'id'; protected bool $autoWriteTimestamp = true; diff --git a/app/common/model/User.php b/app/common/model/User.php index f452eca..8debfbb 100644 --- a/app/common/model/User.php +++ b/app/common/model/User.php @@ -2,6 +2,7 @@ namespace app\common\model; +use app\common\model\traits\TimestampInteger; use support\think\Model; /** @@ -9,6 +10,8 @@ use support\think\Model; */ class User extends Model { + use TimestampInteger; + protected string $table = 'user'; protected string $pk = 'id'; protected bool $autoWriteTimestamp = true; diff --git a/app/common/model/UserMoneyLog.php b/app/common/model/UserMoneyLog.php index da8febf..02b774e 100644 --- a/app/common/model/UserMoneyLog.php +++ b/app/common/model/UserMoneyLog.php @@ -2,10 +2,13 @@ namespace app\common\model; +use app\common\model\traits\TimestampInteger; use support\think\Model; class UserMoneyLog extends Model { + use TimestampInteger; + protected string $table = 'user_money_log'; protected string $pk = 'id'; protected bool $autoWriteTimestamp = true; diff --git a/app/common/model/UserScoreLog.php b/app/common/model/UserScoreLog.php index 68b18fa..e23f3ec 100644 --- a/app/common/model/UserScoreLog.php +++ b/app/common/model/UserScoreLog.php @@ -2,10 +2,13 @@ namespace app\common\model; +use app\common\model\traits\TimestampInteger; use support\think\Model; class UserScoreLog extends Model { + use TimestampInteger; + protected string $table = 'user_score_log'; protected string $pk = 'id'; protected bool $autoWriteTimestamp = true; diff --git a/app/common/model/traits/TimestampInteger.php b/app/common/model/traits/TimestampInteger.php new file mode 100644 index 0000000..c35002b --- /dev/null +++ b/app/common/model/traits/TimestampInteger.php @@ -0,0 +1,25 @@ + 'integer', + 'update_time' => 'integer', + 'last_login_time' => 'integer', + ]; +} diff --git a/config/buildadmin.php b/config/buildadmin.php index ccb970e..5253a9e 100644 --- a/config/buildadmin.php +++ b/config/buildadmin.php @@ -44,7 +44,7 @@ return [ // 默认驱动方式 'default' => 'mysql', // 加密key - 'key' => '5u9HTYBPXId3i6K4S2Q08wWRVFxCENLU', + 'key' => 'L1iYVS0PChKA9pjcFdmOGb4zfDIHo5xw', // 加密方式 'algo' => 'ripemd160', // 驱动 diff --git a/config/route.php b/config/route.php index ea1a5f3..6ee7b62 100644 --- a/config/route.php +++ b/config/route.php @@ -35,16 +35,30 @@ Route::get('/index.html', function () use ($installLockFile, $installCompleteMar }); // ==================== 安装向导(静态页) ==================== -// /install、/install/、/install/index 均返回 public/install/index.html -Route::get('/install', function () { +// 已安装时访问 /install 重定向到应用,访问提示仅在终端显示 +$installLockFileForInstall = public_path('install.lock'); +$installCompleteMarkForInstall = 'install-end'; +Route::get('/install', function () use ($installLockFileForInstall, $installCompleteMarkForInstall) { + $installed = is_file($installLockFileForInstall) && @file_get_contents($installLockFileForInstall) === $installCompleteMarkForInstall; + if ($installed && is_file(public_path('index.html'))) { + return new Response(302, ['Location' => '/index.html']); + } $file = public_path('install/index.html'); return is_file($file) ? (new Response())->file($file) : new Response(404, [], 'Install page not found'); }); -Route::get('/install/', function () { +Route::get('/install/', function () use ($installLockFileForInstall, $installCompleteMarkForInstall) { + $installed = is_file($installLockFileForInstall) && @file_get_contents($installLockFileForInstall) === $installCompleteMarkForInstall; + if ($installed && is_file(public_path('index.html'))) { + return new Response(302, ['Location' => '/index.html']); + } $file = public_path('install/index.html'); return is_file($file) ? (new Response())->file($file) : new Response(404, [], 'Install page not found'); }); -Route::get('/install/index', function () { +Route::get('/install/index', function () use ($installLockFileForInstall, $installCompleteMarkForInstall) { + $installed = is_file($installLockFileForInstall) && @file_get_contents($installLockFileForInstall) === $installCompleteMarkForInstall; + if ($installed && is_file(public_path('index.html'))) { + return new Response(302, ['Location' => '/index.html']); + } $file = public_path('install/index.html'); return is_file($file) ? (new Response())->file($file) : new Response(404, [], 'Install page not found'); }); @@ -65,6 +79,7 @@ Route::get('/api/install/envBaseCheck', [\app\api\controller\Install::class, 'en Route::add(['GET', 'POST'], '/api/install/envNpmCheck', [\app\api\controller\Install::class, 'envNpmCheck']); Route::post('/api/install/testDatabase', [\app\api\controller\Install::class, 'testDatabase']); Route::add(['GET', 'POST'], '/api/install/baseConfig', [\app\api\controller\Install::class, 'baseConfig']); +Route::get('/api/install/accessUrls', [\app\api\controller\Install::class, 'accessUrls']); Route::post('/api/install/commandExecComplete', [\app\api\controller\Install::class, 'commandExecComplete']); Route::post('/api/install/manualInstall', [\app\api\controller\Install::class, 'manualInstall']); Route::post('/api/install/mvDist', [\app\api\controller\Install::class, 'mvDist']); diff --git a/public/install/index.html b/public/install/index.html index 373529a..5164917 100644 --- a/public/install/index.html +++ b/public/install/index.html @@ -1,14 +1,54 @@ - - - - - - - BuildAdmin-安装 + + + + + + + BuildAdmin-安装 + - - -
- - + + +
+ + diff --git a/web/src/router/index.ts b/web/src/router/index.ts index adbfbc0..694dbde 100644 --- a/web/src/router/index.ts +++ b/web/src/router/index.ts @@ -7,6 +7,7 @@ import { loading } from '/@/utils/loading' import langAutoLoadMap from '/@/lang/autoload' import { mergeMessage } from '/@/lang/index' import { useConfig } from '/@/stores/config' +import { useAdminInfo } from '/@/stores/adminInfo' import { isAdminApp } from '/@/utils/common' import { uniq } from 'lodash-es' @@ -16,6 +17,17 @@ const router = createRouter({ }) router.beforeEach((to, from, next) => { + // 后台路由鉴权:未登录时访问 /admin 或 /admin/*(除 /admin/login)则跳转登录页 + const isAdminRoute = to.path === adminBaseRoutePath || to.path.startsWith(adminBaseRoutePath + '/') + const isAdminLogin = to.path === adminBaseRoutePath + '/login' + if (isAdminRoute && !isAdminLogin) { + const adminInfo = useAdminInfo() + if (!adminInfo.token) { + next({ name: 'adminLogin' }) + return + } + } + NProgress.configure({ showSpinner: false }) NProgress.start() if (!window.existLoading) { diff --git a/web/vite.config.ts b/web/vite.config.ts index cc763fe..a881451 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -27,10 +27,11 @@ const viteConfig = ({ mode }: ConfigEnv): UserConfig => { server: { port: parseInt(VITE_PORT), open: VITE_OPEN != 'false', - // 开发时把 /api、/admin 代理到 webman,避免跨域 + // 开发时把 /api、/admin、/install 代理到 webman,避免跨域 proxy: { '/api': { target: 'http://localhost:8787', changeOrigin: true }, '/admin': { target: 'http://localhost:8787', changeOrigin: true }, + '/install': { target: 'http://localhost:8787', changeOrigin: true }, }, }, build: {