From e9fba1178590e7bae66ebb9ca9bee0c8594bff4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=90=9D=E5=8D=9C?= Date: Sat, 14 Mar 2026 03:21:51 +0000 Subject: [PATCH] feat(config): autoload system_configs into config() for governance --- app/Providers/AppServiceProvider.php | 48 +++++++++++++++++++ .../ConfigAutoloadFromSystemConfigsTest.php | 33 +++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 tests/Feature/ConfigAutoloadFromSystemConfigsTest.php diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 452e6b6..90afa04 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,6 +2,8 @@ namespace App\Providers; +use App\Models\SystemConfig; +use Illuminate\Support\Facades\Schema; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider @@ -19,6 +21,52 @@ class AppServiceProvider extends ServiceProvider */ 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; + } } } diff --git a/tests/Feature/ConfigAutoloadFromSystemConfigsTest.php b/tests/Feature/ConfigAutoloadFromSystemConfigsTest.php new file mode 100644 index 0000000..dbd91a2 --- /dev/null +++ b/tests/Feature/ConfigAutoloadFromSystemConfigsTest.php @@ -0,0 +1,33 @@ +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')); + } +}