You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
95 lines
4.4 KiB
95 lines
4.4 KiB
package comm
|
|
|
|
import (
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// 部署身份(应用名/区域)解析——全服务唯一入口。
|
|
//
|
|
// 一份部署只用一组环境变量声明「我是谁」,就是统计上报那一组:
|
|
// - ANALYZE_APP_NAME —— 应用名,须与 console 注册表 app_registry 的 name 与 app_name
|
|
// 【两列同时】完全一致(大小写敏感)
|
|
// - ANALYZE_REGION —— 区域,pb.Region 枚举整数(1=中国 2=美国 …,见 region.go)
|
|
//
|
|
// ⚠️ 为什么是「两列同时」:这一个变量被两处以不同口径消费,缺一不可——
|
|
// - 按应用作用域读配置(本文件的 AppName()):配置表按 app_registry.app_name 存;
|
|
// - 统计落库:console 消费快照时先过 isRegisteredApp() → getAppByName(),那里查的是
|
|
// `app_registry.name`(见 modules/console/model_registry.go)。对不上就整条丢弃,
|
|
// 只在 console 日志留一行 `console.stat: 快照应用未在 console 注册,未落库`,
|
|
// 数据滞留在业务侧 Redis,看板恒为 0 且不报任何错。
|
|
// 所以注册应用时要把 name 与 app_name 填成同一个值;只满足一半必然坏掉另一半。
|
|
// (2026-09-01 在阿龙测试机实测:两列不一致,实时统计累计 152 条被拒、一条都没落库。)
|
|
//
|
|
// 历史上还有一组同义的 APP_NAME / APP_REGION(服务配置托管、会议记录编排在用),
|
|
// 与 ANALYZE_* 语义完全重复,却因为一个走 yaml options、一个走裸 env 而长期并存:
|
|
// 线上普遍只填了 ANALYZE_*,于是按作用域读配置的地方全部静默退化成「全局默认」,
|
|
// 后台明明按应用配好了却下发不到客户端。2026-08-12 已删除 APP_NAME/APP_REGION,
|
|
// 所有消费方一律走本文件的 AppName/AppRegion/AppRegionId。
|
|
|
|
// AppName 返回本部署的应用名(console 注册表 app_registry.app_name;该列须与 name 列同值,
|
|
// 理由见上方说明)。返回 "" 表示未声明部署身份,调用方按「全局默认」作用域处理。
|
|
func AppName() string {
|
|
return strings.TrimSpace(os.Getenv("ANALYZE_APP_NAME"))
|
|
}
|
|
|
|
// AppRegionId 返回本部署的区域枚举值(pb.Region int32);未配置或非法返回 0(未知)。
|
|
// 按 int32 分层的表(会议记录编排 echomeet_orch 等)用它。
|
|
func AppRegionId() int32 {
|
|
v := strings.TrimSpace(os.Getenv("ANALYZE_REGION"))
|
|
if v == "" {
|
|
return 0
|
|
}
|
|
n, err := strconv.ParseInt(v, 10, 32)
|
|
if err != nil {
|
|
// 容错:有人按老习惯填了短代码(cn/us…)也认。
|
|
return RegionFromCode(strings.ToLower(v))
|
|
}
|
|
return int32(n)
|
|
}
|
|
|
|
// AppRegion 返回 app_registry.region 口径的区域字符串,用于按 (app_name, region) 查注册表
|
|
// 与服务配置托管表 app_service_config。
|
|
//
|
|
// ⚠️ 不是 RegionCode() 的细分短代码:后台把区域收敛成「国内(cn) / 海外(hw)」两档
|
|
// (见 admin app/utils/regions.ts),注册表里存的就是这两个值,所以这里也归一成两档——
|
|
// 直接用 "us"/"ea" 这种细分码去匹配注册表会一行都查不到。未配置返回 ""(全局默认)。
|
|
func AppRegion() string {
|
|
switch id := AppRegionId(); {
|
|
case id == 0:
|
|
return ""
|
|
case RegionCode(id) == RegionCodeCN:
|
|
return RegionCodeCN
|
|
default:
|
|
return RegionCodeOverseas
|
|
}
|
|
}
|
|
|
|
// AppNamesHas 判断「绑定应用名 CSV」是否覆盖 app(DBProduct.Appnames 这种一对多绑定列专用)。
|
|
//
|
|
// 语义:
|
|
// - csv 为空 = **未绑定任何应用** → 返回 true,不限应用。存量产品大多没填这一列,
|
|
// 按「未绑定即不下发」处理会让一整批设备的绑定/OTA 当场失效,失败方向太重。
|
|
// - csv 非空 → 按逗号切开逐项**精确等值**比较(两侧 TrimSpace)。
|
|
//
|
|
// ⚠️ 绝不能退化成 SQL 的 `appnames LIKE '%app%'`:应用名之间存在真实的前缀包含关系
|
|
// (线上就有 `EAIMAR` 与 `EAIMAR-TEST`),子串匹配会让正式应用命中测试应用的产品。
|
|
// 所以这里只做内存里的精确比对,不进 SQL。
|
|
func AppNamesHas(csv, app string) bool {
|
|
csv = strings.TrimSpace(csv)
|
|
if csv == "" {
|
|
return true
|
|
}
|
|
app = strings.TrimSpace(app)
|
|
if app == "" {
|
|
// 未声明部署身份:与 AppName() 的约定一致,按「不限作用域」处理,别把配置读空。
|
|
return true
|
|
}
|
|
for _, p := range strings.Split(csv, ",") {
|
|
if strings.TrimSpace(p) == app {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|