Skip to main content

alun_plugin/
scheduler.rs

1//! 定时任务插件:cron 表达式调度
2
3use async_trait::async_trait;
4use alun_core::{Plugin, Result};
5use std::collections::HashMap;
6use std::sync::Arc;
7use parking_lot::RwLock;
8
9type JobFn = Box<dyn Fn() -> tokio::task::JoinHandle<()> + Send + Sync>;
10
11/// 定时任务插件:cron 表达式调度
12///
13/// 支持注册/移除/手动触发/列举任务。注意:本实现为任务注册中心,
14/// 实际调度需搭配 cron runner(如 `tokio-cron-scheduler`)。
15pub struct SchedulerPlugin {
16    /// 任务注册表(name → ScheduledJob)
17    jobs: Arc<RwLock<HashMap<String, ScheduledJob>>>,
18}
19
20struct ScheduledJob {
21    cron: String,
22    description: String,
23    runner: JobFn,
24}
25
26impl SchedulerPlugin {
27    /// 创建空的调度器
28    pub fn new() -> Self {
29        Self { jobs: Arc::new(RwLock::new(HashMap::new())) }
30    }
31
32    /// 注册定时任务
33    ///
34    /// ```ignore
35    /// scheduler.register("cleanup", "0 */5 * * * *", "每5分钟清理", || {
36    ///     tokio::spawn(async { cleanup_task().await })
37    /// });
38    /// ```
39    pub fn register<F>(
40        &self,
41        name: &str,
42        cron: &str,
43        desc: &str,
44        runner: F,
45    ) where
46        F: Fn() -> tokio::task::JoinHandle<()> + Send + Sync + 'static,
47    {
48        self.jobs.write().insert(name.to_string(), ScheduledJob {
49            cron: cron.to_string(),
50            description: desc.to_string(),
51            runner: Box::new(runner),
52        });
53    }
54
55    /// 移除定时任务
56    pub fn remove(&self, name: &str) {
57        self.jobs.write().remove(name);
58    }
59
60    /// 列出所有注册的任务
61    pub fn list(&self) -> Vec<(String, String, String)> {
62        self.jobs.read()
63            .iter()
64            .map(|(k, v)| (k.clone(), v.cron.clone(), v.description.clone()))
65            .collect()
66    }
67
68    /// 手动触发指定的定时任务
69    pub fn trigger(&self, name: &str) -> Option<tokio::task::JoinHandle<()>> {
70        let guard = self.jobs.read();
71        guard.get(name).map(|job| (job.runner)())
72    }
73}
74
75#[async_trait]
76impl Plugin for SchedulerPlugin {
77    fn name(&self) -> &str { "scheduler" }
78
79    async fn start(&self) -> Result<()> {
80        let count = self.jobs.read().len();
81        tracing::info!("定时任务插件: {} 个任务已注册", count);
82        for (name, job) in self.jobs.read().iter() {
83            tracing::info!("  - {} [{}] {}", name, job.cron, job.description);
84        }
85        Ok(())
86    }
87
88    async fn stop(&self) -> Result<()> {
89        tracing::info!("定时任务插件: 已停止");
90        Ok(())
91    }
92}
93
94impl Default for SchedulerPlugin {
95    fn default() -> Self { Self::new() }
96}