feat: 添加统一数据库初始化命令,简化数据库迁移和种子填充流程;新增基础和演示数据填充器
This commit is contained in:
41
README.md
41
README.md
@@ -34,6 +34,45 @@ php artisan schema:dump --database=pgsql
|
|||||||
|
|
||||||
如果只是日常开发中的普通字段变更,仍然按正常方式新增 migration 即可;等累积到一段时间后,再统一刷新一次 schema dump。
|
如果只是日常开发中的普通字段变更,仍然按正常方式新增 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
|
## 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:
|
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」。
|
在 **本仓库根目录**(即含 `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**
|
**终端 1 — HTTP API**
|
||||||
|
|
||||||
|
|||||||
90
app/Console/Commands/LotteryDatabaseInitCommand.php
Normal file
90
app/Console/Commands/LotteryDatabaseInitCommand.php
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
final class LotteryDatabaseInitCommand extends Command
|
||||||
|
{
|
||||||
|
protected $signature = 'lottery:db-init
|
||||||
|
{--fresh : 先执行 migrate:fresh,再初始化数据库}
|
||||||
|
{--demo : 额外写入演示数据(生产环境默认关闭)}
|
||||||
|
{--no-demo : 禁止写入演示数据}
|
||||||
|
{--skip-auth-sync : 跳过后台权限注册表同步}';
|
||||||
|
|
||||||
|
protected $description = '统一初始化数据库:迁移/基线、基础种子、后台权限同步,以及非生产演示数据';
|
||||||
|
|
||||||
|
public function handle(): int
|
||||||
|
{
|
||||||
|
$isProduction = app()->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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -44,10 +44,16 @@
|
|||||||
"composer install",
|
"composer install",
|
||||||
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\"",
|
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\"",
|
||||||
"@php artisan key:generate",
|
"@php artisan key:generate",
|
||||||
"@php artisan migrate --force",
|
"@php artisan lottery:db-init",
|
||||||
"npm install --ignore-scripts",
|
"npm install --ignore-scripts",
|
||||||
"npm run build"
|
"npm run build"
|
||||||
],
|
],
|
||||||
|
"db:init": [
|
||||||
|
"@php artisan lottery:db-init"
|
||||||
|
],
|
||||||
|
"db:fresh": [
|
||||||
|
"@php artisan lottery:db-init --fresh"
|
||||||
|
],
|
||||||
"dev": [
|
"dev": [
|
||||||
"Composer\\Config::disableProcessTimeout",
|
"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"
|
"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"
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
- 最终版 PostgreSQL 结构:[`../schema/pgsql-schema.sql`](../schema/pgsql-schema.sql)
|
- 最终版 PostgreSQL 结构:[`../schema/pgsql-schema.sql`](../schema/pgsql-schema.sql)
|
||||||
- 新环境初始化:优先加载 schema dump,再执行后续新增 migration
|
- 新环境初始化:优先加载 schema dump,再执行后续新增 migration
|
||||||
- 旧的历史 migration 已清理,不再作为基线结构来源
|
- 旧的历史 migration 已清理,不再作为基线结构来源
|
||||||
|
- 统一初始化入口:`php artisan lottery:db-init`
|
||||||
|
|
||||||
后续规则:
|
后续规则:
|
||||||
|
|
||||||
|
|||||||
@@ -11,27 +11,10 @@ final class DatabaseSeeder extends Seeder
|
|||||||
|
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
// 全环境可用的基础枚举数据
|
$this->call([FoundationSeeder::class]);
|
||||||
$this->call([
|
|
||||||
CurrencySeeder::class,
|
|
||||||
PlayTypeSeeder::class,
|
|
||||||
OperationalConfigV1Seeder::class,
|
|
||||||
OddsPrizeScopesBackfillSeeder::class,
|
|
||||||
/** 对齐玩法目录与 active/draft 配置行(修复历史子集种子) */
|
|
||||||
PlayOperationalAlignmentSeeder::class,
|
|
||||||
LotterySettingsSeeder::class,
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 演示管理员 + 演示玩家 + 演示期号:**勿在生产库执行**(或确保 APP_ENV≠production)
|
|
||||||
if (! app()->environment('production')) {
|
if (! app()->environment('production')) {
|
||||||
$this->call([
|
$this->call([LocalDemoSeeder::class]);
|
||||||
AdminRbacAndUserSeeder::class,
|
|
||||||
DevPlayerAndWalletSeeder::class,
|
|
||||||
DrawDemoSeeder::class,
|
|
||||||
// 仪表盘:对齐「大厅 resolve 当期」+ 多场景演示期(-996 settled / -995 pending)
|
|
||||||
DashboardHallFixtureSeeder::class,
|
|
||||||
DashboardSecondaryScenariosSeeder::class,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
30
database/seeders/FoundationSeeder.php
Normal file
30
database/seeders/FoundationSeeder.php
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全环境通用的基础数据。
|
||||||
|
*
|
||||||
|
* 约束:
|
||||||
|
* - 只能放可重复执行、幂等、安全用于生产的枚举/配置/权限同步类种子
|
||||||
|
* - 不放演示账号、演示玩家、演示期号等开发数据
|
||||||
|
*/
|
||||||
|
final class FoundationSeeder extends Seeder
|
||||||
|
{
|
||||||
|
use WithoutModelEvents;
|
||||||
|
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$this->call([
|
||||||
|
CurrencySeeder::class,
|
||||||
|
PlayTypeSeeder::class,
|
||||||
|
OperationalConfigV1Seeder::class,
|
||||||
|
OddsPrizeScopesBackfillSeeder::class,
|
||||||
|
PlayOperationalAlignmentSeeder::class,
|
||||||
|
LotterySettingsSeeder::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
29
database/seeders/LocalDemoSeeder.php
Normal file
29
database/seeders/LocalDemoSeeder.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本地/测试联调用的演示数据。
|
||||||
|
*
|
||||||
|
* 约束:
|
||||||
|
* - 绝不在 production 环境默认执行
|
||||||
|
* - 仅包含便于联调的账号、玩家、期号、仪表盘样例等
|
||||||
|
*/
|
||||||
|
final class LocalDemoSeeder extends Seeder
|
||||||
|
{
|
||||||
|
use WithoutModelEvents;
|
||||||
|
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$this->call([
|
||||||
|
AdminRbacAndUserSeeder::class,
|
||||||
|
DevPlayerAndWalletSeeder::class,
|
||||||
|
DrawDemoSeeder::class,
|
||||||
|
DashboardHallFixtureSeeder::class,
|
||||||
|
DashboardSecondaryScenariosSeeder::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user