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}