secra_plugins 0.1.32

生产级插件系统 - 插件的生命周期
Documentation
//! 插件路由相关操作

use crate::error::{PluginManagerError, PluginManagerResult};
use crate::manager::types::PluginMap;
use actix_web::web;
use tracing::{debug, error, info, trace, warn};
use futures::executor::block_on;

/// 注册所有插件的路由
///
/// 为所有处于运行状态且配置了路由的插件注册 HTTP 路由。注册顺序会优先处理主插件,
/// 然后处理子插件,确保路由注册的正确顺序。
///
/// # 参数
/// * `plugins` - 插件实例的共享映射表,使用 `Arc<RwLock<>>` 保证线程安全
/// * `cfg` - Actix Web 的服务配置对象,用于注册路由
///
/// # 返回值
/// * `PluginManagerResult<()>` - 成功时返回 `Ok(())`
///
/// # 行为
/// * 只注册状态为 `Running` 且配置了路由的插件
/// * 按照主插件优先、子插件其次的顺序注册
/// * 如果某个插件路由注册失败,会记录错误但继续注册其他插件
/// * 注册完成后会输出统计信息(成功数、失败数、总数)
///
/// # 错误
/// * 通常不会返回错误,即使部分插件注册失败也会继续执行
///
/// # 示例
/// ```no_run
/// use std::sync::Arc;
/// use tokio::sync::RwLock;
/// use std::collections::HashMap;
/// use actix_web::web;
/// 
/// # async fn example() {
/// let plugins = Arc::new(RwLock::new(HashMap::new()));
/// let mut cfg = web::ServiceConfig::default();
/// register_all_plugin_routes(plugins, &mut cfg).await?;
/// # }
/// ```
pub async fn register_all_plugin_routes(
    plugins: PluginMap,
    cfg: &mut web::ServiceConfig,
) -> PluginManagerResult<()> {
    trace!("开始注册所有插件路由");
    info!("开始注册插件路由...");

    let plugin_routes: Vec<(String, String, Option<String>, bool)> = {
        trace!("获取读锁,查找需要注册路由的插件");
        let plugins = plugins.read().await;

        plugins
            .iter()
            .filter_map(|(id, instance)| {
                if instance.status.is_running() && instance.metadata.has_routes() {
                    trace!("发现需要注册路由的插件: {} (路由前缀: {:?})", 
                           id, instance.metadata.route_prefix);
                    Some((
                        id.clone(),
                        instance.metadata.name.clone(),
                        instance.metadata.route_prefix.clone(),
                        instance.metadata.is_sub_plugin,
                    ))
                } else {
                    None
                }
            })
            .collect()
    };

    if plugin_routes.is_empty() {
        info!("没有需要注册路由的插件");
        return Ok(());
    }

    let mut sorted_routes = plugin_routes;
    sorted_routes.sort_by(|a, b| {
        match (a.3, b.3) {
            (false, true) => std::cmp::Ordering::Less,
            (true, false) => std::cmp::Ordering::Greater,
            _ => a.0.cmp(&b.0)
        }
    });
    debug!("路由注册顺序已排序");

    let total_count = sorted_routes.len();
    info!("发现 {} 个插件需要注册路由", total_count);

    let mut success_count = 0;
    let mut failure_count = 0;

    for (plugin_id, plugin_name, route_prefix, _) in sorted_routes {
        trace!("注册插件路由: {} (路由前缀: {:?})", plugin_id, route_prefix);
        
        let register_result = {
            let plugins = plugins.read().await;

            let plugin_instance = plugins.get(&plugin_id);

            match plugin_instance {
                Some(instance) if instance.status.is_running() => {
                    debug!("调用插件 register_routes 方法: {}", plugin_id);
                    let result = instance.plugin.register_routes(cfg);
                    drop(plugins);
                    result
                }
                _ => {
                    drop(plugins);
                    warn!(
                        plugin_id = %plugin_id,
                        "插件不存在或状态已变更,跳过路由注册"
                    );
                    continue;
                }
            }
        };

        match register_result {
            Ok(_) => {
                success_count += 1;
                info!(
                    plugin_id = %plugin_id,
                    plugin_name = %plugin_name,
                    route_prefix = ?route_prefix,
                    "插件路由注册成功"
                );
                trace!("路由注册成功: {}", plugin_id);
            }
            Err(e) => {
                failure_count += 1;
                error!(
                    plugin_id = %plugin_id,
                    plugin_name = %plugin_name,
                    route_prefix = ?route_prefix,
                    error = %e,
                    "插件路由注册失败"
                );
            }
        }
    }

    info!(
        success_count = success_count,
        failure_count = failure_count,
        total_count = total_count,
        "插件路由注册完成"
    );

    if failure_count > 0 {
        warn!(
            "有 {} 个插件的路由注册失败,但已继续注册其他插件",
            failure_count
        );
    }

    trace!("路由注册流程完成");
    Ok(())
}

/// 注册所有插件的路由(同步版本)
///
/// 为所有处于运行状态且配置了路由的插件注册 HTTP 路由。注册顺序会优先处理主插件,
/// 然后处理子插件,确保路由注册的正确顺序。
///
/// 这是 `register_all_plugin_routes` 的同步版本,可以在非异步上下文中调用。
/// 内部会尝试使用当前的 Tokio 运行时句柄,如果不存在则创建新的运行时。
///
/// # 参数
/// * `plugins` - 插件实例的共享映射表,使用 `Arc<RwLock<>>` 保证线程安全
/// * `cfg` - Actix Web 的服务配置对象,用于注册路由
///
/// # 返回值
/// * `PluginManagerResult<()>` - 成功时返回 `Ok(())`
///
/// # 行为
/// * 只注册状态为 `Running` 且配置了路由的插件
/// * 按照主插件优先、子插件其次的顺序注册
/// * 如果某个插件路由注册失败,会记录错误但继续注册其他插件
/// * 注册完成后会输出统计信息(成功数、失败数、总数)
///
/// # 错误
/// * 通常不会返回错误,即使部分插件注册失败也会继续执行
/// * 如果无法获取或创建 Tokio 运行时,会返回错误
///
/// # 示例
/// ```no_run
/// use std::sync::Arc;
/// use tokio::sync::RwLock;
/// use std::collections::HashMap;
/// use actix_web::web;
/// 
/// let plugins = Arc::new(RwLock::new(HashMap::new()));
/// let mut cfg = web::ServiceConfig::default();
/// register_all_plugin_routes_sync(plugins, &mut cfg)?;
/// ```
pub fn register_all_plugin_routes_sync(
    plugins: PluginMap,
    cfg: &mut web::ServiceConfig,
) -> PluginManagerResult<()> {
    // 尝试获取当前的 Tokio 运行时句柄
    match tokio::runtime::Handle::try_current() {
        Ok(handle) => {
            // 如果已经在运行时中,我们需要使用一个技巧来执行异步代码
            // 由于 ServiceConfig 不是 Send,我们不能跨线程传递它
            // 使用 Handle::enter() 来在当前运行时上下文中执行
            // 然后使用 futures::executor::block_on 来执行异步代码
            // 注意:这会在运行时线程中阻塞,但在 configure 回调中这是可以接受的,
            // 因为 configure 只在应用启动时调用一次,且是同步的
            let _guard = handle.enter();
            block_on(register_all_plugin_routes(plugins, cfg))
        }
        Err(_) => {
            // 如果没有运行时上下文,创建新的运行时
            let rt = tokio::runtime::Runtime::new()
                .map_err(|e| {
                    PluginManagerError::Io(
                        std::io::Error::new(
                            std::io::ErrorKind::Other,
                            format!("无法创建 Tokio 运行时: {}", e)
                        )
                    )
                })?;
            rt.block_on(register_all_plugin_routes(plugins, cfg))
        }
    }
}