Blame view

config/tenant_config_loader.py 6.62 KB
0064e946   tangwang   feat: 增量索引服务、租户配置...
1
2
3
  """
  租户配置加载器。
  
038e4e2f   tangwang   refactor(i18n): t...
4
5
  从统一配置文件(config.yaml)加载租户配置,包括主语言和索引语言(index_languages)。
  支持旧配置 translate_to_en / translate_to_zh 的兼容解析。
0064e946   tangwang   feat: 增量索引服务、租户配置...
6
7
8
  """
  
  import logging
038e4e2f   tangwang   refactor(i18n): t...
9
  from typing import Dict, Any, Optional, List
0064e946   tangwang   feat: 增量索引服务、租户配置...
10
11
12
  
  logger = logging.getLogger(__name__)
  
038e4e2f   tangwang   refactor(i18n): t...
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
  # 支持的索引语言:code -> display name(供商家勾选主市场语言等场景使用)
  SUPPORTED_INDEX_LANGUAGES: Dict[str, str] = {
      "en": "English",
      "zh": "Chinese",
      "zh_tw": "Traditional Chinese",
      "ru": "Russian",
      "ja": "Japanese",
      "ko": "Korean",
      "es": "Spanish",
      "fr": "French",
      "pt": "Portuguese",
      "de": "German",
      "it": "Italian",
      "th": "Thai",
      "vi": "Vietnamese",
      "id": "Indonesian",
      "ms": "Malay",
      "ar": "Arabic",
      "hi": "Hindi",
      "he": "Hebrew",
      "my": "Burmese",
      "ta": "Tamil",
      "ur": "Urdu",
      "bn": "Bengali",
      "pl": "Polish",
      "nl": "Dutch",
      "ro": "Romanian",
      "tr": "Turkish",
      "km": "Khmer",
      "lo": "Lao",
      "yue": "Cantonese",
      "cs": "Czech",
      "el": "Greek",
      "sv": "Swedish",
      "hu": "Hungarian",
      "da": "Danish",
      "fi": "Finnish",
      "uk": "Ukrainian",
      "bg": "Bulgarian",
  }
  
038e4e2f   tangwang   refactor(i18n): t...
54
55
56
  def normalize_index_languages(value: Any, primary_language: str = "en") -> List[str]:
      """
       index_languages 配置规范化为合法语言代码列表。
26b910bd   tangwang   refactor service ...
57
      仅做规范化,不做默认值兜底。
038e4e2f   tangwang   refactor(i18n): t...
58
      """
26b910bd   tangwang   refactor service ...
59
      del primary_language
038e4e2f   tangwang   refactor(i18n): t...
60
      if value is None:
26b910bd   tangwang   refactor service ...
61
          return []
038e4e2f   tangwang   refactor(i18n): t...
62
      if not isinstance(value, (list, tuple)):
26b910bd   tangwang   refactor service ...
63
          return []
038e4e2f   tangwang   refactor(i18n): t...
64
65
66
67
68
69
70
71
72
      valid: List[str] = []
      seen: set = set()
      for item in value:
          code = (item or "").strip().lower()
          if not code or code in seen:
              continue
          if code in SUPPORTED_INDEX_LANGUAGES:
              valid.append(code)
              seen.add(code)
26b910bd   tangwang   refactor service ...
73
      return valid
038e4e2f   tangwang   refactor(i18n): t...
74
75
  
  
26b910bd   tangwang   refactor service ...
76
77
78
79
  def resolve_index_languages(
      tenant_config: Dict[str, Any],
      default_index_languages: List[str],
  ) -> List[str]:
038e4e2f   tangwang   refactor(i18n): t...
80
81
82
83
84
      """
      从租户配置解析 index_languages
      若存在 index_languages 则用之;否则按旧配置 translate_to_en / translate_to_zh 推导。
      """
      if "index_languages" in tenant_config:
26b910bd   tangwang   refactor service ...
85
          normalized = normalize_index_languages(
038e4e2f   tangwang   refactor(i18n): t...
86
87
88
              tenant_config["index_languages"],
              tenant_config.get("primary_language") or "en",
          )
26b910bd   tangwang   refactor service ...
89
          return normalized if normalized else list(default_index_languages)
038e4e2f   tangwang   refactor(i18n): t...
90
91
92
93
94
95
96
97
98
99
      primary = (tenant_config.get("primary_language") or "en").strip().lower()
      to_en = bool(tenant_config.get("translate_to_en"))
      to_zh = bool(tenant_config.get("translate_to_zh"))
      langs: List[str] = []
      if primary and primary in SUPPORTED_INDEX_LANGUAGES:
          langs.append(primary)
      for code in ("en", "zh"):
          if code not in langs and ((code == "en" and to_en) or (code == "zh" and to_zh)):
              if code in SUPPORTED_INDEX_LANGUAGES:
                  langs.append(code)
26b910bd   tangwang   refactor service ...
100
      return langs if langs else list(default_index_languages)
038e4e2f   tangwang   refactor(i18n): t...
101
  
0064e946   tangwang   feat: 增量索引服务、租户配置...
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
  
  class TenantConfigLoader:
      """租户配置加载器。"""
  
      def __init__(self):
          """初始化租户配置加载器。"""
          self._config: Optional[Dict[str, Any]] = None
  
      def load_config(self) -> Dict[str, Any]:
          """
          加载租户配置(从统一配置文件)。
  
          Returns:
              租户配置字典,格式:{"tenants": {...}, "default": {...}}
          """
          if self._config is not None:
              return self._config
  
          try:
              from config import ConfigLoader
26b910bd   tangwang   refactor service ...
122
  
0064e946   tangwang   feat: 增量索引服务、租户配置...
123
124
              config_loader = ConfigLoader()
              search_config = config_loader.load_config()
26b910bd   tangwang   refactor service ...
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
              tenant_cfg = search_config.tenant_config
              if not isinstance(tenant_cfg, dict):
                  raise RuntimeError("tenant_config must be an object")
  
              default_cfg = tenant_cfg.get("default")
              if not isinstance(default_cfg, dict):
                  raise RuntimeError("tenant_config.default must be configured in config.yaml")
              default_primary = (default_cfg.get("primary_language") or "en").strip().lower()
              default_index_languages = normalize_index_languages(
                  default_cfg.get("index_languages"),
                  default_primary,
              )
              if not default_index_languages:
                  raise RuntimeError(
                      "tenant_config.default.index_languages must include at least one supported language"
                  )
  
              tenants_cfg = tenant_cfg.get("tenants", {})
              if not isinstance(tenants_cfg, dict):
                  raise RuntimeError("tenant_config.tenants must be an object")
  
              normalized_default = dict(default_cfg)
              normalized_default["primary_language"] = default_primary
              normalized_default["index_languages"] = default_index_languages
  
              self._config = {
                  "default": normalized_default,
                  "tenants": tenants_cfg,
              }
0064e946   tangwang   feat: 增量索引服务、租户配置...
154
155
156
157
              logger.info("Loaded tenant config from unified config.yaml")
              return self._config
          except Exception as e:
              logger.error(f"Failed to load tenant config: {e}", exc_info=True)
26b910bd   tangwang   refactor service ...
158
              raise
0064e946   tangwang   feat: 增量索引服务、租户配置...
159
160
161
162
163
164
165
166
167
  
      def get_tenant_config(self, tenant_id: str) -> Dict[str, Any]:
          """
          获取指定租户的配置。
  
          Args:
              tenant_id: 租户ID
  
          Returns:
038e4e2f   tangwang   refactor(i18n): t...
168
              租户配置字典,若租户不存在则用默认配置。始终包含已解析的 index_languages
0064e946   tangwang   feat: 增量索引服务、租户配置...
169
170
171
          """
          config = self.load_config()
          tenant_id_str = str(tenant_id)
26b910bd   tangwang   refactor service ...
172
          default = config["default"]
0064e946   tangwang   feat: 增量索引服务、租户配置...
173
          tenants = config.get("tenants", {})
26b910bd   tangwang   refactor service ...
174
175
176
          raw = tenants[tenant_id_str] if tenant_id_str in tenants else {}
          if raw and not isinstance(raw, dict):
              raise RuntimeError(f"tenant_config.tenants.{tenant_id_str} must be an object")
038e4e2f   tangwang   refactor(i18n): t...
177
          if tenant_id_str not in tenants:
0064e946   tangwang   feat: 增量索引服务、租户配置...
178
              logger.debug(f"Tenant {tenant_id} not found in config, using default")
26b910bd   tangwang   refactor service ...
179
180
181
182
183
184
185
          merged = dict(default)
          merged.update(raw)
          out = dict(merged)
          out["index_languages"] = resolve_index_languages(
              merged,
              default_index_languages=default["index_languages"],
          )
038e4e2f   tangwang   refactor(i18n): t...
186
          return out
0064e946   tangwang   feat: 增量索引服务、租户配置...
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
  
      def reload(self):
          """重新加载配置(用于配置更新)。"""
          self._config = None
          return self.load_config()
  
  
  # 全局实例
  _tenant_config_loader: Optional[TenantConfigLoader] = None
  
  
  def get_tenant_config_loader() -> TenantConfigLoader:
      """获取全局租户配置加载器实例。"""
      global _tenant_config_loader
      if _tenant_config_loader is None:
          _tenant_config_loader = TenantConfigLoader()
      return _tenant_config_loader