package config import ( "errors" "fmt" "dario.cat/mergo" "git.ikuban.com/server/yaml" "github.com/elliotchance/orderedmap/v3" "github.com/go-kratos/kratos/v2/config" ) func Load(sources []Source, bc any) error { configSources := make([]config.Source, 0) sourceMap := orderedmap.NewOrderedMap[string, any]() // 有序map控制优先级 for _, source := range sources { if !source.Validate() { continue } s, err := source.NewSource() if err != nil { return fmt.Errorf("%v, new source error: %v", source.String(), err) } kvs, err := s.Load() if err != nil { return fmt.Errorf("%v, source load error: %v", source.String(), err) } for _, v := range kvs { sourceMap.Set(v.Key, map[string]any{}) } configSources = append(configSources, s) } if sourceMap.Len() == 0 { return errors.New("config source is empty") } c := config.New( config.WithSource(configSources...), config.WithDecoder(func(kv *config.KeyValue, v map[string]interface{}) error { ok := sourceMap.Has(kv.Key) if !ok { return errors.New("unknown config key") } next := map[string]any{} err := yaml.Unmarshal(kv.Value, next) if err != nil { return err } sourceMap.Set(kv.Key, convertMap(next)) merged := make(map[string]any) for c := range sourceMap.Values() { err = mergo.Merge(&merged, c, mergo.WithOverride) if err != nil { return err } } b, err := yaml.Marshal(merged) if err != nil { return err } err = yaml.Unmarshal(b, bc) if err != nil { return err } return nil }), ) if err := c.Load(); err != nil { return err } return nil } func convertMap(src interface{}) interface{} { switch m := src.(type) { case map[string]interface{}: dst := make(map[string]interface{}, len(m)) for k, v := range m { dst[k] = convertMap(v) } return dst case map[interface{}]interface{}: dst := make(map[interface{}]interface{}, len(m)) for k, v := range m { dst[k] = convertMap(v) } return dst case []interface{}: dst := make([]interface{}, len(m)) for k, v := range m { dst[k] = convertMap(v) } return dst case []byte: // there will be no binary data in the config data return string(m) default: return src } }