1use crate::config::ConfigSummary;
2use crate::{configuration_error, validation_error, with_context, Config, Result};
3use openlark_core::error::ErrorTrait;
4use std::env;
5
6pub fn check_env_config() -> Result<()> {
29 let app_id = env::var("OPENLARK_APP_ID")
31 .map_err(|_| configuration_error("环境变量检查失败 [variable: OPENLARK_APP_ID]"))?;
32
33 if app_id.is_empty() {
34 return with_context(
35 Err(validation_error(
36 "OPENLARK_APP_ID",
37 "应用ID环境变量不能为空",
38 )),
39 "validation",
40 "env_config",
41 );
42 }
43
44 let app_secret = env::var("OPENLARK_APP_SECRET")
46 .map_err(|_| configuration_error("环境变量检查失败 [variable: OPENLARK_APP_SECRET]"))?;
47
48 if app_secret.is_empty() {
49 return with_context(
50 Err(validation_error(
51 "OPENLARK_APP_SECRET",
52 "应用密钥环境变量不能为空",
53 )),
54 "validation",
55 "env_config",
56 );
57 }
58
59 if let Ok(base_url) = env::var("OPENLARK_BASE_URL") {
61 if !base_url.starts_with("http://") && !base_url.starts_with("https://") {
62 return with_context(
63 Err(validation_error(
64 "OPENLARK_BASE_URL",
65 "基础URL必须以http://或https://开头",
66 )),
67 "validation",
68 "env_config",
69 );
70 }
71 }
72
73 if let Ok(timeout_str) = env::var("OPENLARK_TIMEOUT") {
75 if timeout_str.parse::<u64>().is_err() {
76 return with_context(
77 Err(validation_error(
78 "OPENLARK_TIMEOUT",
79 "超时设置必须是有效的数字(秒数)",
80 )),
81 "validation",
82 "env_config",
83 );
84 }
85 }
86
87 Ok(())
88}
89
90pub fn create_config_from_env() -> Result<Config> {
98 check_env_config()?;
100
101 let app_id = env::var("OPENLARK_APP_ID").unwrap();
102 let app_secret = env::var("OPENLARK_APP_SECRET").unwrap();
103
104 let base_url =
105 env::var("OPENLARK_BASE_URL").unwrap_or_else(|_| "https://open.feishu.cn".to_string());
106
107 let timeout = env::var("OPENLARK_TIMEOUT")
108 .ok()
109 .and_then(|t| t.parse().ok())
110 .map(std::time::Duration::from_secs);
111
112 let enable_log = env::var("OPENLARK_ENABLE_LOG")
113 .ok()
114 .and_then(|l| l.parse().ok())
115 .unwrap_or(false);
116
117 let mut config = Config::builder()
118 .app_id(app_id)
119 .app_secret(app_secret)
120 .base_url(base_url)
121 .enable_log(enable_log);
122
123 if let Some(timeout_duration) = timeout {
124 config = config.timeout(timeout_duration);
125 }
126
127 with_context(config.build(), "operation", "create_config_from_env")
128}
129
130pub fn get_config_summary(config: &Config) -> ConfigSummary {
134 config.summary()
135}
136
137pub fn get_enabled_features() -> Vec<&'static str> {
141 #[allow(unused_mut)]
143 let mut features = vec!["auth"];
144
145 #[cfg(feature = "communication")]
147 features.push("communication");
148
149 #[cfg(feature = "docs")]
150 features.push("docs");
151
152 #[cfg(feature = "security")]
153 features.push("security");
154
155 #[cfg(feature = "hr")]
156 features.push("hr");
157
158 #[cfg(feature = "ai")]
159 features.push("ai");
160
161 features
162}
163
164pub fn validate_feature_dependencies() -> Result<Vec<String>> {
168 let enabled_features = get_enabled_features();
169 let mut issues = Vec::new();
170
171 if enabled_features.contains(&"communication") && !enabled_features.contains(&"auth") {
173 issues.push("通讯服务 (communication) 需要启用认证服务 (auth)".to_string());
174 }
175
176 if enabled_features.contains(&"docs") && !enabled_features.contains(&"auth") {
177 issues.push("文档服务 (docs) 需要启用认证服务 (auth)".to_string());
178 }
179
180 if enabled_features.contains(&"hr") && !enabled_features.contains(&"auth") {
181 issues.push("人力资源服务 (hr) 需要启用认证服务 (auth)".to_string());
182 }
183
184 if enabled_features.contains(&"ai") && !enabled_features.contains(&"auth") {
185 issues.push("AI服务 (ai) 需要启用认证服务 (auth)".to_string());
186 }
187
188 if enabled_features.contains(&"calendar") && !enabled_features.contains(&"auth") {
189 issues.push("日历服务 (calendar) 需要启用认证服务 (auth)".to_string());
190 }
191
192 if enabled_features.contains(&"admin") && !enabled_features.contains(&"hr") {
193 issues.push("管理服务 (admin) 建议启用人力资源服务 (hr) 以获得完整功能".to_string());
194 }
195
196 if enabled_features.contains(&"approval") && !enabled_features.contains(&"auth") {
197 issues.push("审批服务 (approval) 需要启用认证服务 (auth)".to_string());
198 }
199
200 if issues.is_empty() {
201 Ok(issues)
202 } else {
203 with_context(
204 Err(configuration_error(format!(
205 "发现 {} 个功能依赖问题: {}",
206 issues.len(),
207 issues.join("; ")
208 ))),
209 "validation",
210 "feature_dependencies",
211 )
212 }
213}
214
215pub fn diagnose_system() -> SystemDiagnostics {
219 let mut diagnostics = SystemDiagnostics::new();
220
221 match check_env_config() {
223 Ok(()) => {
224 diagnostics.env_config_status = "✅ 正常".to_string();
225 }
226 Err(error) => {
227 diagnostics.env_config_status =
228 format!("❌ {}", error.user_message().unwrap_or("未知错误"));
229 diagnostics.add_issue("环境变量", error.user_message().unwrap_or("未知错误"));
230 }
231 }
232
233 match validate_feature_dependencies() {
235 Ok(_) => {
236 diagnostics.feature_deps_status = "✅ 正常".to_string();
237 }
238 Err(error) => {
239 diagnostics.feature_deps_status =
240 format!("❌ {}", error.user_message().unwrap_or("未知错误"));
241 diagnostics.add_issue("功能依赖", error.user_message().unwrap_or("未知错误"));
242 }
243 }
244
245 diagnostics.enabled_features = get_enabled_features()
247 .into_iter()
248 .map(|s| s.to_string())
249 .collect();
250
251 diagnostics
252}
253
254#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
256pub struct SystemDiagnostics {
257 pub env_config_status: String,
259 pub feature_deps_status: String,
261 pub enabled_features: Vec<String>,
263 pub issues: Vec<DiagnosticIssue>,
265}
266
267impl SystemDiagnostics {
268 pub fn new() -> Self {
270 Self {
271 env_config_status: "未检查".to_string(),
272 feature_deps_status: "未检查".to_string(),
273 enabled_features: Vec::new(),
274 issues: Vec::new(),
275 }
276 }
277
278 pub fn add_issue(&mut self, category: &str, description: &str) {
280 self.issues.push(DiagnosticIssue {
281 category: category.to_string(),
282 description: description.to_string(),
283 });
284 }
285
286 pub fn health_summary(&self) -> String {
288 let healthy_count = self.issues.len();
289 if healthy_count == 0 {
290 "🟢 系统配置健康".to_string()
291 } else {
292 format!("🟡 发现 {} 个配置问题", healthy_count)
293 }
294 }
295
296 pub fn has_critical_issues(&self) -> bool {
298 self.issues
299 .iter()
300 .any(|issue| issue.category.contains("环境变量") || issue.category.contains("功能依赖"))
301 }
302}
303
304impl Default for SystemDiagnostics {
305 fn default() -> Self {
306 Self::new()
307 }
308}
309
310#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
312pub struct DiagnosticIssue {
313 pub category: String,
315 pub description: String,
317}
318
319#[cfg(test)]
320#[allow(unused_imports)]
321mod tests {
322
323 #[test]
324 fn test_serialization_roundtrip() {
325 let json = r#"{"test": "value"}"#;
327 assert!(serde_json::from_str::<serde_json::Value>(json).is_ok());
328 }
329
330 #[test]
331 fn test_deserialization_from_json() {
332 let json = r#"{"field": "data"}"#;
334 let value: serde_json::Value = serde_json::from_str(json).unwrap();
335 assert_eq!(value["field"], "data");
336 }
337}