feat(config): autoload system_configs into config() for governance
This commit is contained in:
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use App\Models\SystemConfig;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
@@ -19,6 +21,52 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
*/
|
*/
|
||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
|
// 从数据库 system_configs 自动注入可配置项到 config(),用于“总台可治理/可配置”的运营闭环。
|
||||||
//
|
//
|
||||||
|
// 安全阀:
|
||||||
|
// - 在测试/首次安装阶段,可能尚未执行迁移;此时 system_configs 表不存在,必须跳过。
|
||||||
|
// - 任意配置项解析失败(如 JSON 格式错误)也不应阻断应用启动。
|
||||||
|
if (! Schema::hasTable('system_configs')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$configs = SystemConfig::query()
|
||||||
|
->where('autoload', true)
|
||||||
|
->orderBy('id')
|
||||||
|
->get(['config_key', 'config_value', 'value_type']);
|
||||||
|
|
||||||
|
foreach ($configs as $row) {
|
||||||
|
$key = (string) ($row->config_key ?? '');
|
||||||
|
if ($key === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$type = (string) ($row->value_type ?? 'string');
|
||||||
|
$raw = $row->config_value;
|
||||||
|
|
||||||
|
$value = match ($type) {
|
||||||
|
'boolean' => in_array(strtolower((string) $raw), ['1', 'true', 'yes', 'on'], true),
|
||||||
|
'number' => is_numeric($raw) ? (float) $raw : 0.0,
|
||||||
|
'json' => (function () use ($raw) {
|
||||||
|
$decoded = json_decode((string) ($raw ?? ''), true);
|
||||||
|
|
||||||
|
return json_last_error() === JSON_ERROR_NONE ? $decoded : null;
|
||||||
|
})(),
|
||||||
|
default => $raw,
|
||||||
|
};
|
||||||
|
|
||||||
|
// JSON 解析失败时直接跳过(不影响系统启动)
|
||||||
|
if ($type === 'json' && $value === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注意:使用 dot key 注入到 config(),例如:saasshop.amounts.tolerance
|
||||||
|
config([$key => $value]);
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
// 不阻断启动:治理配置加载失败时,回退到 config/*.php 默认值
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
33
tests/Feature/ConfigAutoloadFromSystemConfigsTest.php
Normal file
33
tests/Feature/ConfigAutoloadFromSystemConfigsTest.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use App\Models\SystemConfig;
|
||||||
|
use App\Providers\AppServiceProvider;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class ConfigAutoloadFromSystemConfigsTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
public function test_system_configs_autoload_injected_into_config(): void
|
||||||
|
{
|
||||||
|
// 写入 DB 后,手动触发一次 AppServiceProvider::boot(),
|
||||||
|
// 以模拟“请求启动时自动注入 config()”的行为。
|
||||||
|
SystemConfig::query()->create([
|
||||||
|
'config_key' => 'saasshop.amounts.tolerance',
|
||||||
|
'config_name' => '金额容差(元)',
|
||||||
|
'config_value' => '0.05',
|
||||||
|
'value_type' => 'number',
|
||||||
|
'autoload' => true,
|
||||||
|
'group' => 'saasshop',
|
||||||
|
'remark' => '用于测试:从 DB 自动注入到 config()',
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 重新执行 provider 的 boot(测试环境中应用在插入前已启动,因此需要手动触发)
|
||||||
|
(new AppServiceProvider($this->app))->boot();
|
||||||
|
|
||||||
|
$this->assertSame(0.05, config('saasshop.amounts.tolerance'));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user