From bbdb69dabb7b67abb60e3519da3b75270c61e4f5 Mon Sep 17 00:00:00 2001 From: kang Date: Tue, 9 Jun 2026 17:41:37 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=BB=9F=E4=B8=80?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=BA=93=E5=88=9D=E5=A7=8B=E5=8C=96=E5=91=BD?= =?UTF-8?q?=E4=BB=A4=EF=BC=8C=E7=AE=80=E5=8C=96=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=E8=BF=81=E7=A7=BB=E5=92=8C=E7=A7=8D=E5=AD=90=E5=A1=AB=E5=85=85?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=EF=BC=9B=E6=96=B0=E5=A2=9E=E5=9F=BA=E7=A1=80?= =?UTF-8?q?=E5=92=8C=E6=BC=94=E7=A4=BA=E6=95=B0=E6=8D=AE=E5=A1=AB=E5=85=85?= =?UTF-8?q?=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 41 ++++++++- .../Commands/LotteryDatabaseInitCommand.php | 90 +++++++++++++++++++ composer.json | 8 +- database/migrations/README.md | 1 + database/seeders/DatabaseSeeder.php | 21 +---- database/seeders/FoundationSeeder.php | 30 +++++++ database/seeders/LocalDemoSeeder.php | 29 ++++++ 7 files changed, 199 insertions(+), 21 deletions(-) create mode 100644 app/Console/Commands/LotteryDatabaseInitCommand.php create mode 100644 database/seeders/FoundationSeeder.php create mode 100644 database/seeders/LocalDemoSeeder.php diff --git a/README.md b/README.md index e7a2410..d181e94 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,45 @@ php artisan schema:dump --database=pgsql 如果只是日常开发中的普通字段变更,仍然按正常方式新增 migration 即可;等累积到一段时间后,再统一刷新一次 schema dump。 +## 统一数据库初始化 + +现在统一使用一条命令初始化或更新数据库: + +```bash +php artisan lottery:db-init +``` + +这条命令会自动完成: + +- 执行 `migrate`,让 Laravel 在空库时优先加载 `database/schema/pgsql-schema.sql` +- 执行生产安全的基础种子 `FoundationSeeder` +- 执行后台权限同步与体检 `lottery:admin-auth-sync --audit` +- 在非 `production` 环境默认补充联调用演示数据 `LocalDemoSeeder` + +常用场景: + +```bash +# 新同事拉代码 / 日常本地初始化 +php artisan lottery:db-init + +# 本地需要清空重建再灌演示数据 +php artisan lottery:db-init --fresh + +# 线上发布 / 已有数据环境增量更新 +php artisan lottery:db-init + +# 线上强制不写任何演示数据 +php artisan lottery:db-init --no-demo +``` + +种子职责约定: + +- `FoundationSeeder`:全环境通用、可幂等、允许在线上执行的基础数据 +- `LocalDemoSeeder`:仅本地/测试联调使用的账号、玩家、期号、仪表盘样例 +- `DatabaseSeeder`:兼容 Laravel 默认入口,内部按环境组合以上两层 + +以后给别人开发或线上部署时,统一记这一条命令即可,不再要求手工区分“先 migrate 还是先 seed”。 + ## About Laravel Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as: @@ -72,7 +111,7 @@ Boost provides your agent 15+ tools and skills that help agents build Laravel ap 在 **本仓库根目录**(即含 `artisan` 的 `lotterLaravel` 目录)开 **3 个终端**,每段整段复制即可。若已用 Herd / Valet / Sail / 自有 Web 服务器指到 `public`,可不跑「终端 1」。 -**前置(首次)**:`cp .env.example .env`,`composer install`,`php artisan key:generate`,`php artisan migrate`(及你的库表/种子)。本地重置演示数据可用 `php artisan migrate:fresh --seed`(会清空当前库)。若只改了 `AdminAuthorizationRegistry` 而未新增迁移,还需 `php artisan lottery:admin-auth-sync --audit`。 +**前置(首次)**:`cp .env.example .env`,`composer install`,`php artisan key:generate`,`php artisan lottery:db-init`。本地重置演示数据可用 `php artisan lottery:db-init --fresh`(会清空当前库)。若只改了 `AdminAuthorizationRegistry` 而未新增迁移,还可单独执行 `php artisan lottery:admin-auth-sync --audit`。 **终端 1 — HTTP API** diff --git a/app/Console/Commands/LotteryDatabaseInitCommand.php b/app/Console/Commands/LotteryDatabaseInitCommand.php new file mode 100644 index 0000000..d8726fe --- /dev/null +++ b/app/Console/Commands/LotteryDatabaseInitCommand.php @@ -0,0 +1,90 @@ +environment('production'); + $seedDemo = $this->shouldSeedDemo($isProduction); + $migrationCommand = (bool) $this->option('fresh') ? 'migrate:fresh' : 'migrate'; + + $this->components->info(sprintf( + '开始数据库初始化:%s%s', + $migrationCommand, + $seedDemo ? ' + foundation seed + demo seed' : ' + foundation seed', + )); + + $migrateExit = $this->call($migrationCommand, [ + '--force' => true, + ]); + if ($migrateExit !== self::SUCCESS) { + return $migrateExit; + } + + $foundationExit = $this->call('db:seed', [ + '--class' => 'Database\\Seeders\\FoundationSeeder', + '--force' => true, + ]); + if ($foundationExit !== self::SUCCESS) { + return $foundationExit; + } + + if (! (bool) $this->option('skip-auth-sync')) { + $authExit = $this->call('lottery:admin-auth-sync', [ + '--audit' => true, + ]); + if ($authExit !== self::SUCCESS) { + return $authExit; + } + } + + if ($seedDemo) { + if ($isProduction) { + $this->components->error('production 环境禁止写入演示数据。'); + + return self::FAILURE; + } + + $demoExit = $this->call('db:seed', [ + '--class' => 'Database\\Seeders\\LocalDemoSeeder', + '--force' => true, + ]); + if ($demoExit !== self::SUCCESS) { + return $demoExit; + } + } + + $this->newLine(); + $this->components->info('数据库初始化完成。'); + $this->line(sprintf('环境:%s', app()->environment())); + $this->line(sprintf('演示数据:%s', $seedDemo ? '已写入' : '未写入')); + $this->line('统一入口命令:php artisan lottery:db-init'); + + return self::SUCCESS; + } + + private function shouldSeedDemo(bool $isProduction): bool + { + if ((bool) $this->option('no-demo')) { + return false; + } + + if ((bool) $this->option('demo')) { + return true; + } + + return ! $isProduction; + } +} diff --git a/composer.json b/composer.json index 08a31c8..55ffb8f 100644 --- a/composer.json +++ b/composer.json @@ -44,10 +44,16 @@ "composer install", "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"", "@php artisan key:generate", - "@php artisan migrate --force", + "@php artisan lottery:db-init", "npm install --ignore-scripts", "npm run build" ], + "db:init": [ + "@php artisan lottery:db-init" + ], + "db:fresh": [ + "@php artisan lottery:db-init --fresh" + ], "dev": [ "Composer\\Config::disableProcessTimeout", "npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#f472b6\" \"php artisan serve --host=\\\"${APP_BIND_HOST:-127.0.0.1}\\\"\" \"php artisan queue:listen --tries=1 --timeout=0\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite --kill-others" diff --git a/database/migrations/README.md b/database/migrations/README.md index d042f75..71850ce 100644 --- a/database/migrations/README.md +++ b/database/migrations/README.md @@ -5,6 +5,7 @@ - 最终版 PostgreSQL 结构:[`../schema/pgsql-schema.sql`](../schema/pgsql-schema.sql) - 新环境初始化:优先加载 schema dump,再执行后续新增 migration - 旧的历史 migration 已清理,不再作为基线结构来源 +- 统一初始化入口:`php artisan lottery:db-init` 后续规则: diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index f551d99..d68bc29 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -11,27 +11,10 @@ final class DatabaseSeeder extends Seeder public function run(): void { - // 全环境可用的基础枚举数据 - $this->call([ - CurrencySeeder::class, - PlayTypeSeeder::class, - OperationalConfigV1Seeder::class, - OddsPrizeScopesBackfillSeeder::class, - /** 对齐玩法目录与 active/draft 配置行(修复历史子集种子) */ - PlayOperationalAlignmentSeeder::class, - LotterySettingsSeeder::class, - ]); + $this->call([FoundationSeeder::class]); - // 演示管理员 + 演示玩家 + 演示期号:**勿在生产库执行**(或确保 APP_ENV≠production) if (! app()->environment('production')) { - $this->call([ - AdminRbacAndUserSeeder::class, - DevPlayerAndWalletSeeder::class, - DrawDemoSeeder::class, - // 仪表盘:对齐「大厅 resolve 当期」+ 多场景演示期(-996 settled / -995 pending) - DashboardHallFixtureSeeder::class, - DashboardSecondaryScenariosSeeder::class, - ]); + $this->call([LocalDemoSeeder::class]); } } } diff --git a/database/seeders/FoundationSeeder.php b/database/seeders/FoundationSeeder.php new file mode 100644 index 0000000..930ef7f --- /dev/null +++ b/database/seeders/FoundationSeeder.php @@ -0,0 +1,30 @@ +call([ + CurrencySeeder::class, + PlayTypeSeeder::class, + OperationalConfigV1Seeder::class, + OddsPrizeScopesBackfillSeeder::class, + PlayOperationalAlignmentSeeder::class, + LotterySettingsSeeder::class, + ]); + } +} diff --git a/database/seeders/LocalDemoSeeder.php b/database/seeders/LocalDemoSeeder.php new file mode 100644 index 0000000..ffa8ada --- /dev/null +++ b/database/seeders/LocalDemoSeeder.php @@ -0,0 +1,29 @@ +call([ + AdminRbacAndUserSeeder::class, + DevPlayerAndWalletSeeder::class, + DrawDemoSeeder::class, + DashboardHallFixtureSeeder::class, + DashboardSecondaryScenariosSeeder::class, + ]); + } +}