package console import ( "sort" "strings" "yunyan/comm" "yunyan/lego/sys/log" "yunyan/lego/sys/postgres" ) // ============================ 作用域键一致性巡检(只读,只打日志) ============================ // // 按应用隔离的配置(svc_config / 会议模板 …)用 app_name 当作用域键,而这个键有三个来源, // 三者必须是同一个字符串,错一个就会「后台改半天不生效」或「后台整页空白」,且**不报任何错**: // // ① 业务部署 .env 的 ANALYZE_APP_NAME —— 运行时 comm.AppName(),决定客户端读哪一套; // ② 应用注册表 app_registry.app_name —— 迁移/复制/后台下拉用它; // ③ 应用注册表 app_registry.name —— 统计落库认它(console.stat 的 getAppByName 查的是 name)。 // // 线上真实踩过:测试机 app_registry 是 (name=EAIMAR-TEST, app_name=EAIMAR),而部署的 // ANALYZE_APP_NAME=EAIMAR-TEST。于是配置全落在 'EAIMAR-TEST' 作用域(客户端读得到), // 后台下拉给出的却是 'EAIMAR'(app_name||name)→ 选它看到的是空页面,在那里新建的服务 // 客户端一个都读不到。两边都不报错,只有对着库查才看得出来。 // // 巡检不改数据(作用域键是运维决定的,自动改名可能把线上配置搬到没人读的地方), // 只把对不上的地方点名打出来。 func checkScopeConsistency(apps []*AppRegistry) { known := map[string]bool{} // 注册表认得的作用域键(app_name 与 name 都算) declared := map[string]bool{} // 后台下拉会给出的键(前端取 app_name||name,与它一致) for _, a := range apps { if !a.Enabled { continue } n, an := strings.TrimSpace(a.Name), strings.TrimSpace(a.AppName) if n != "" { known[n] = true } if an != "" { known[an] = true } if an != "" && n != "" && an != n { log.Warnf("console.scope: 应用注册表 id=%d 的 name(%q) 与 app_name(%q) 不同值——"+ "配置作用域、后台下拉、统计落库三者会各认一个,务必改成同值(见 comm/appscope.go)", a.Id, n, an) } if d := an; d != "" { declared[d] = true } else if n != "" { declared[n] = true } } if len(known) == 0 { return } for _, table := range []string{comm.TableSvcConfig, comm.TableEchomeetTemplate} { var scopes []string if err := postgres.Table(table).Group("app_name").Pluck("app_name", &scopes).Error; err != nil { log.Warnf("console.scope: 巡检 %s 的作用域键失败(跳过): %v", table, err) continue } orphan := make([]string, 0) for _, s := range scopes { s = strings.TrimSpace(s) if s == "" || known[s] { continue } orphan = append(orphan, s) } if len(orphan) > 0 { sort.Strings(orphan) log.Warnf("console.scope: %s 里有配置挂在注册表不认识的作用域 %v ——"+ "若某个业务部署的 ANALYZE_APP_NAME 正是它,那套配置正在生效却在后台**看不到也改不了**;"+ "对齐办法是把 app_registry 的 name/app_name 都改成这个值(不要改业务部署的 .env,"+ "那会让客户端立刻读到另一套配置)", table, orphan) } // 反向:后台下拉会给出、但库里一行配置都没有的作用域——选中它就是空白页。 empty := make([]string, 0) for d := range declared { found := false for _, s := range scopes { if strings.TrimSpace(s) == d { found = true break } } if !found { empty = append(empty, d) } } if len(empty) > 0 && len(orphan) > 0 { sort.Strings(empty) log.Warnf("console.scope: %s 里作用域 %v 一行都没有,而后台下拉正会给出它们——"+ "配合上面那条孤儿作用域看,多半是同一个应用的两个名字", table, empty) } } }