use crate::config::ConfigSummary;
use crate::{configuration_error, validation_error, with_context, Config, Result};
use openlark_core::error::ErrorTrait;
use std::env;
pub fn check_env_config() -> Result<()> {
let app_id = env::var("OPENLARK_APP_ID")
.map_err(|_| configuration_error("环境变量检查失败 [variable: OPENLARK_APP_ID]"))?;
if app_id.is_empty() {
return with_context(
Err(validation_error(
"OPENLARK_APP_ID",
"应用ID环境变量不能为空",
)),
"validation",
"env_config",
);
}
let app_secret = env::var("OPENLARK_APP_SECRET")
.map_err(|_| configuration_error("环境变量检查失败 [variable: OPENLARK_APP_SECRET]"))?;
if app_secret.is_empty() {
return with_context(
Err(validation_error(
"OPENLARK_APP_SECRET",
"应用密钥环境变量不能为空",
)),
"validation",
"env_config",
);
}
if let Ok(base_url) = env::var("OPENLARK_BASE_URL") {
if !base_url.starts_with("http://") && !base_url.starts_with("https://") {
return with_context(
Err(validation_error(
"OPENLARK_BASE_URL",
"基础URL必须以http://或https://开头",
)),
"validation",
"env_config",
);
}
}
if let Ok(timeout_str) = env::var("OPENLARK_TIMEOUT") {
if timeout_str.parse::<u64>().is_err() {
return with_context(
Err(validation_error(
"OPENLARK_TIMEOUT",
"超时设置必须是有效的数字(秒数)",
)),
"validation",
"env_config",
);
}
}
Ok(())
}
pub fn create_config_from_env() -> Result<Config> {
check_env_config()?;
let app_id = env::var("OPENLARK_APP_ID").unwrap();
let app_secret = env::var("OPENLARK_APP_SECRET").unwrap();
let base_url =
env::var("OPENLARK_BASE_URL").unwrap_or_else(|_| "https://open.feishu.cn".to_string());
let timeout = env::var("OPENLARK_TIMEOUT")
.ok()
.and_then(|t| t.parse().ok())
.map(std::time::Duration::from_secs);
let enable_log = env::var("OPENLARK_ENABLE_LOG")
.ok()
.and_then(|l| l.parse().ok())
.unwrap_or(false);
let mut config = Config::builder()
.app_id(app_id)
.app_secret(app_secret)
.base_url(base_url)
.enable_log(enable_log);
if let Some(timeout_duration) = timeout {
config = config.timeout(timeout_duration);
}
with_context(config.build(), "operation", "create_config_from_env")
}
pub fn get_config_summary(config: &Config) -> ConfigSummary {
config.summary()
}
pub fn get_enabled_features() -> Vec<&'static str> {
#[allow(unused_mut)]
let mut features = vec!["auth"];
#[cfg(feature = "communication")]
features.push("communication");
#[cfg(feature = "docs")]
features.push("docs");
#[cfg(feature = "security")]
features.push("security");
#[cfg(feature = "hr")]
features.push("hr");
#[cfg(feature = "ai")]
features.push("ai");
features
}
pub fn validate_feature_dependencies() -> Result<Vec<String>> {
let enabled_features = get_enabled_features();
let mut issues = Vec::new();
if enabled_features.contains(&"communication") && !enabled_features.contains(&"auth") {
issues.push("通讯服务 (communication) 需要启用认证服务 (auth)".to_string());
}
if enabled_features.contains(&"docs") && !enabled_features.contains(&"auth") {
issues.push("文档服务 (docs) 需要启用认证服务 (auth)".to_string());
}
if enabled_features.contains(&"hr") && !enabled_features.contains(&"auth") {
issues.push("人力资源服务 (hr) 需要启用认证服务 (auth)".to_string());
}
if enabled_features.contains(&"ai") && !enabled_features.contains(&"auth") {
issues.push("AI服务 (ai) 需要启用认证服务 (auth)".to_string());
}
if enabled_features.contains(&"calendar") && !enabled_features.contains(&"auth") {
issues.push("日历服务 (calendar) 需要启用认证服务 (auth)".to_string());
}
if enabled_features.contains(&"admin") && !enabled_features.contains(&"hr") {
issues.push("管理服务 (admin) 建议启用人力资源服务 (hr) 以获得完整功能".to_string());
}
if enabled_features.contains(&"approval") && !enabled_features.contains(&"auth") {
issues.push("审批服务 (approval) 需要启用认证服务 (auth)".to_string());
}
if issues.is_empty() {
Ok(issues)
} else {
with_context(
Err(configuration_error(format!(
"发现 {} 个功能依赖问题: {}",
issues.len(),
issues.join("; ")
))),
"validation",
"feature_dependencies",
)
}
}
pub fn diagnose_system() -> SystemDiagnostics {
let mut diagnostics = SystemDiagnostics::new();
match check_env_config() {
Ok(()) => {
diagnostics.env_config_status = "✅ 正常".to_string();
}
Err(error) => {
diagnostics.env_config_status =
format!("❌ {}", error.user_message().unwrap_or("未知错误"));
diagnostics.add_issue("环境变量", error.user_message().unwrap_or("未知错误"));
}
}
match validate_feature_dependencies() {
Ok(_) => {
diagnostics.feature_deps_status = "✅ 正常".to_string();
}
Err(error) => {
diagnostics.feature_deps_status =
format!("❌ {}", error.user_message().unwrap_or("未知错误"));
diagnostics.add_issue("功能依赖", error.user_message().unwrap_or("未知错误"));
}
}
diagnostics.enabled_features = get_enabled_features()
.into_iter()
.map(|s| s.to_string())
.collect();
diagnostics
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct SystemDiagnostics {
pub env_config_status: String,
pub feature_deps_status: String,
pub enabled_features: Vec<String>,
pub issues: Vec<DiagnosticIssue>,
}
impl SystemDiagnostics {
pub fn new() -> Self {
Self {
env_config_status: "未检查".to_string(),
feature_deps_status: "未检查".to_string(),
enabled_features: Vec::new(),
issues: Vec::new(),
}
}
pub fn add_issue(&mut self, category: &str, description: &str) {
self.issues.push(DiagnosticIssue {
category: category.to_string(),
description: description.to_string(),
});
}
pub fn health_summary(&self) -> String {
let healthy_count = self.issues.len();
if healthy_count == 0 {
"🟢 系统配置健康".to_string()
} else {
format!("🟡 发现 {} 个配置问题", healthy_count)
}
}
pub fn has_critical_issues(&self) -> bool {
self.issues
.iter()
.any(|issue| issue.category.contains("环境变量") || issue.category.contains("功能依赖"))
}
}
impl Default for SystemDiagnostics {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct DiagnosticIssue {
pub category: String,
pub description: String,
}
#[cfg(test)]
#[allow(unused_imports)]
mod tests {
#[test]
fn test_serialization_roundtrip() {
let json = r#"{"test": "value"}"#;
assert!(serde_json::from_str::<serde_json::Value>(json).is_ok());
}
#[test]
fn test_deserialization_from_json() {
let json = r#"{"field": "data"}"#;
let value: serde_json::Value = serde_json::from_str(json).unwrap();
assert_eq!(value["field"], "data");
}
}