package config import ( "fmt" "reflect" "sort" "time" ) // FieldSource 是单个配置字段的来源解释(KTD17:只报告来源/状态,不打印值)。 type FieldSource struct { Field string // json 标签名(无标签时为小写字段名) Layer string // default | env-file: | process-env | cli Set bool // 是否被显式设置(非默认值即视为被设置) } // snapshotFields 用反射导出 Config 的全部字段值(格式化字符串)。 // 字段均为基本类型/时间片,无指针与嵌套结构;新增字段自动纳入。 func snapshotFields(c *Config) map[string]string { out := map[string]string{} v := reflect.ValueOf(*c) t := v.Type() for i := 0; i < t.NumField(); i++ { f := t.Field(i) if f.PkgPath != "" { continue // 未导出 } name := f.Tag.Get("json") if idx := len(name); idx > 0 { // 去 omitempty 等选项 for j := 0; j < len(name); j++ { if name[j] == ',' { name = name[:j] break } } } if name == "" { name = lowerFirst(f.Name) } fv := v.Field(i) var s string if fv.Type() == reflect.TypeOf(time.Duration(0)) { s = fv.Interface().(time.Duration).String() } else { s = fmt.Sprintf("%v", fv.Interface()) } out[name] = s } return out } func lowerFirst(s string) string { if s == "" { return s } b := []byte(s) if b[0] >= 'A' && b[0] <= 'Z' { b[0] += 'a' - 'A' } return string(b) } // diffLayers 依序比较各阶段快照,返回每个字段的最后变更层(即生效来源)。 // phases 按应用顺序给出(层名, 快照);字段未出现于任何快照时归为 default。 func diffLayers(phases []struct { Layer string Snap map[string]string }) map[string]FieldSource { src := map[string]FieldSource{} var prev map[string]string for _, ph := range phases { for field, val := range ph.Snap { fs := src[field] fs.Field = field if prev == nil || prev[field] != val { fs.Layer = ph.Layer fs.Set = prev == nil || prev[field] != val && ph.Layer != phases[0].Layer } src[field] = fs } prev = ph.Snap } // default 层不算"显式设置" for field, fs := range src { if fs.Layer == phases[0].Layer { fs.Set = false src[field] = fs } } return src } // SortedFieldSources 排序输出(稳定渲染用)。 func SortedFieldSources(m map[string]FieldSource) []FieldSource { out := make([]FieldSource, 0, len(m)) for _, fs := range m { out = append(out, fs) } sort.Slice(out, func(i, j int) bool { return out[i].Field < out[j].Field }) return out }