优化结构

This commit is contained in:
Yvan
2026-08-28 09:19:54 +08:00
parent 03a4131f6e
commit 279692375b
57 changed files with 778 additions and 799 deletions
+41
View File
@@ -10,3 +10,44 @@ func CloneAdmin(value *Admin) *Admin { return cloneAdmin(value) }
func CloneDatabase(value *Database) *Database { return clonePtr(value) }
func CloneStorage(value *Storage) *Storage { return cloneStorage(value) }
func CloneEmail(value *Email) *Email { return clonePtr(value) }
// MergeRuntimeConfig applies the runtime replacement contract shared by file
// watching and management updates. Omitted top-level sections keep their
// current values, while database-backed storage/email settings are never
// erased merely because they are absent from config.yaml.
func MergeRuntimeConfig(current, next *Config) *Config {
merged := cloneConfig(next)
if merged == nil {
merged = &Config{}
}
if current == nil {
if merged.Admin == nil {
merged.Admin = &Admin{}
}
return merged
}
if merged.Server == nil {
merged.Server = clonePtr(current.Server)
}
if merged.Data == nil {
merged.Data = cloneData(current.Data)
}
if merged.Admin == nil {
merged.Admin = cloneAdmin(current.Admin)
}
if merged.Admin == nil {
merged.Admin = &Admin{}
}
if current.Admin != nil {
if merged.Admin.Storage == nil {
merged.Admin.Storage = cloneStorage(current.Admin.Storage)
}
if merged.Admin.Email == nil {
merged.Admin.Email = clonePtr(current.Admin.Email)
}
if merged.Admin.ConfigPath == "" {
merged.Admin.ConfigPath = current.Admin.ConfigPath
}
}
return merged
}
+7
View File
@@ -45,6 +45,13 @@ func (d *Document) Set(key string, value any) error {
// Delete removes the nested key path if present.
func (d *Document) Delete(keys ...string) { deleteYAMLMapping(&d.node, keys...) }
// DeleteIntegrationConfig removes settings owned by the database-backed
// integration store rather than by config.yaml.
func (d *Document) DeleteIntegrationConfig() {
d.Delete("admin", "storage")
d.Delete("admin", "email")
}
// Has reports whether the nested key path exists.
func (d *Document) Has(keys ...string) bool { return yamlMappingValue(&d.node, keys...) != nil }
+1 -17
View File
@@ -268,23 +268,7 @@ func (r *Store) watchLoop(watcher *fsnotify.Watcher, stop <-chan struct{}, done
// take down a running process or publish invalid state.
continue
}
current := r.Snapshot()
if next.Admin == nil {
next.Admin = &Admin{}
}
// Storage and Email move to database-backed integration settings after
// first initialization. External config file edits must not erase the
// active values when those sections are absent from config.yaml.
if current != nil && current.Admin != nil {
if next.Admin.Storage == nil {
next.Admin.Storage = cloneStorage(current.Admin.Storage)
}
if next.Admin.Email == nil && current.Admin.Email != nil {
email := *current.Admin.Email
next.Admin.Email = &email
}
}
r.Replace(next)
r.Replace(MergeRuntimeConfig(r.Snapshot(), next))
case _, ok := <-errors:
// fsnotify errors are intentionally non-fatal; the watcher remains
// useful for subsequent events and Close always terminates it.
+23
View File
@@ -145,3 +145,26 @@ func TestStoreWatchPreservesDatabaseBackedIntegrationSettings(t *testing.T) {
}
t.Fatal("watcher did not publish the changed file")
}
func TestMergeRuntimeConfigPreservesOmittedSections(t *testing.T) {
current := &Config{
Server: &Server{HTTP: &ServerHTTP{Addr: ":8080"}},
Data: &Data{Database: &Database{Source: "current-dsn"}},
Admin: &Admin{ConfigPath: "config.yaml", Storage: &Storage{Type: "local"}, Email: &Email{Host: "smtp.example.com"}},
}
next := &Config{Admin: &Admin{App: &App{AppID: "next"}}}
merged := MergeRuntimeConfig(current, next)
if merged.Server == nil || merged.Server.HTTP == nil || merged.Server.HTTP.Addr != ":8080" {
t.Fatalf("server was not preserved: %#v", merged.Server)
}
if merged.Data == nil || merged.Data.Database == nil || merged.Data.Database.Source != "current-dsn" {
t.Fatalf("data was not preserved: %#v", merged.Data)
}
if merged.Admin.Storage == nil || merged.Admin.Storage.Type != "local" || merged.Admin.Email == nil || merged.Admin.Email.Host != "smtp.example.com" {
t.Fatalf("integration settings were not preserved: %#v", merged.Admin)
}
merged.Admin.Storage.Type = "changed"
if current.Admin.Storage.Type != "local" {
t.Fatal("MergeRuntimeConfig leaked mutable storage state into current config")
}
}