Skip to main content

alun_plugin/
serial_plugin.rs

1//! 单号生成器插件:遵循 `TaskPlugin` 模式的薄封装
2//!
3//! 插件本身不持有任何数据库连接或 SQL 语句。
4//! 所有持久化逻辑通过 `SerialGenerator` trait 委托给业务方实现。
5//!
6//! # 使用方式
7//!
8//! ## 方式一:内存后端(零配置)
9//!
10//! ```toml
11//! plugins.enabled = ["serial"]
12//!
13//! [plugins.serial]
14//! backend = "memory"
15//!
16//! [[plugins.serial.rules]]
17//! key = "order"
18//! format = "ORD{YYYY}{MM}{DD}{SEQ:8}"
19//! cycle = "daily"
20//! ```
21//!
22//! ## 方式二:自定义后端(参考 TaskPlugin)
23//!
24//! ```ignore
25//! // 业务方实现 SerialGenerator trait
26//! let my_backend = Arc::new(MyDbSerialBackend::new(db_pool));
27//! let plugin = SerialPlugin::new(cfg.plugins.serial, my_backend);
28//! app.plugin(plugin).scan().start().await;
29//! ```
30//!
31//! ## 在 Handler 中使用
32//!
33//! ```ignore
34//! #[alun::get("/api/serial/next/{rule_key}")]
35//! async fn next_serial(
36//!     Extension(plugin): Extension<SerialPlugin>,
37//!     Path(rule_key): Path<String>,
38//! ) -> Result<Res<String>, ApiError> {
39//!     let no = plugin.generator().generate(&rule_key).await
40//!         .map_err(|e| ApiError::internal(e.to_string()))?;
41//!     Ok(Res::ok(no))
42//! }
43//! ```
44
45use std::sync::Arc;
46use async_trait::async_trait;
47use alun_core::{Plugin, Result as CoreResult};
48use alun_config::SerialConfig;
49use alun_utils::{SerialGenerator, SerialRule, MemorySerialBackend, CyclePeriod, IncrementStrategy};
50use tracing::info;
51
52/// 单号生成器插件
53///
54/// 遵循 `TaskPlugin` 模式——插件是薄封装,不持有任何数据库连接或 SQL。
55/// 持久化逻辑通过 `Arc<dyn SerialGenerator>` 委托给业务方。
56///
57/// # 自动配置模式
58///
59/// 若未通过 `new()` 传入自定义生成器,插件在 `start()` 时自动创建 Memory 后端。
60/// 对于 Redis/PG 后端,需通过 `new(serial_config, arc_generator)` 注入。
61pub struct SerialPlugin {
62    /// 单号生成器配置
63    config: SerialConfig,
64    /// 实际使用的生成器实例(构造时或 start() 时设置)
65    generator: parking_lot::RwLock<Option<Arc<dyn SerialGenerator>>>,
66    /// 预注入的生成器(new() 传入时非空)
67    preset_generator: Option<Arc<dyn SerialGenerator>>,
68}
69
70impl SerialPlugin {
71    /// 使用自定义生成器创建插件(推荐——遵循 TaskPlugin 模式)
72    ///
73    /// # 示例
74    ///
75    /// ```ignore
76    /// let backend = Arc::new(RedisSerialBackend::new(redis_conn));
77    /// let plugin = SerialPlugin::new(cfg.plugins.serial, backend);
78    /// ```
79    pub fn new(config: SerialConfig, generator: Arc<dyn SerialGenerator>) -> Self {
80        Self {
81            preset_generator: Some(generator),
82            generator: parking_lot::RwLock::new(None),
83            config,
84        }
85    }
86
87    /// 使用默认 memory 后端创建插件(零依赖自动配置)
88    pub fn with_memory(config: SerialConfig) -> Self {
89        Self {
90            config,
91            generator: parking_lot::RwLock::new(None),
92            preset_generator: None,
93        }
94    }
95
96    /// 获取生成器引用
97    ///
98    /// # Panics
99    ///
100    /// 插件未启动时调用会 panic。
101    pub fn generator(&self) -> Arc<dyn SerialGenerator> {
102        self.generator.read().clone()
103            .expect("SerialPlugin 尚未启动,请确保插件已注册并启动")
104    }
105
106    /// 从 SerialRuleConfig 转换为 SerialRule
107    fn convert_rule(rc: &alun_config::SerialRuleConfig) -> SerialRule {
108        let cycle = match rc.cycle.as_str() {
109            "daily" => CyclePeriod::Daily,
110            "monthly" => CyclePeriod::Monthly,
111            "yearly" => CyclePeriod::Yearly,
112            _ => CyclePeriod::NoCycle,
113        };
114        let step = if let Some(max_str) = rc.step.strip_prefix("random:") {
115            let max: u64 = max_str.parse().unwrap_or(1);
116            IncrementStrategy::Random { max }
117        } else {
118            IncrementStrategy::Sequential
119        };
120        SerialRule {
121            key: rc.key.clone(),
122            format: rc.format.clone(),
123            cycle,
124            initial_value: rc.initial_value,
125            step,
126            is_enabled: true,
127        }
128    }
129}
130
131#[async_trait]
132impl Plugin for SerialPlugin {
133    fn name(&self) -> &str { "serial" }
134
135    async fn start(&self) -> CoreResult<()> {
136        // 使用预注入的生成器,否则默认 memory
137        let gen = self.preset_generator.clone()
138            .unwrap_or_else(|| {
139                info!("单号生成器使用 memory 后端(默认)");
140                Arc::new(MemorySerialBackend::new())
141            });
142
143        // 注册配置文件中定义的静态规则
144        for rc in &self.config.rules {
145            let rule = Self::convert_rule(rc);
146            gen.register_rule(rule.clone()).await
147                .map_err(|e| alun_core::Error::Msg(
148                    format!("注册单号规则 '{}' 失败: {}", rule.key, e)
149                ))?;
150            info!("单号规则已注册: key={}, format={}", rule.key, rule.format);
151        }
152
153        *self.generator.write() = Some(gen);
154        let rule_count = self.config.rules.len();
155        info!("单号生成器就绪,后端={}, 规则数={}", self.config.backend, rule_count);
156        Ok(())
157    }
158
159    async fn stop(&self) -> CoreResult<()> {
160        info!("单号生成器插件已停止");
161        Ok(())
162    }
163}