1use std::collections::HashMap;
31use std::fmt;
32use async_trait::async_trait;
33use chrono::{Local, Datelike};
34use serde::{Deserialize, Serialize};
35use tokio::sync::Mutex;
36
37#[derive(Debug, Clone)]
41pub enum SerialError {
42 RuleNotFound(String),
44 RuleDisabled(String),
46 FormatError(String),
48 CounterOverflow(String),
50 StorageError(String),
52}
53
54impl fmt::Display for SerialError {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 match self {
57 Self::RuleNotFound(k) => write!(f, "单号规则未找到: {}", k),
58 Self::RuleDisabled(k) => write!(f, "单号规则已禁用: {}", k),
59 Self::FormatError(msg) => write!(f, "格式错误: {}", msg),
60 Self::CounterOverflow(msg) => write!(f, "计数器溢出: {}", msg),
61 Self::StorageError(msg) => write!(f, "存储错误: {}", msg),
62 }
63 }
64}
65
66impl std::error::Error for SerialError {}
67
68#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
72#[serde(rename_all = "snake_case")]
73pub enum CyclePeriod {
74 #[default]
76 NoCycle,
77 Daily,
79 Monthly,
81 Yearly,
83}
84
85impl CyclePeriod {
86 pub fn current_value(&self) -> String {
88 let now = Local::now();
89 match self {
90 Self::NoCycle => String::new(),
91 Self::Daily => format!("{:04}{:02}{:02}", now.year(), now.month(), now.day()),
92 Self::Monthly => format!("{:04}{:02}", now.year(), now.month()),
93 Self::Yearly => format!("{:04}", now.year()),
94 }
95 }
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
102#[serde(rename_all = "snake_case")]
103pub enum IncrementStrategy {
104 Sequential,
106 Random { max: u64 },
108}
109
110impl Default for IncrementStrategy {
111 fn default() -> Self { Self::Sequential }
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize)]
122pub struct SerialRule {
123 pub key: String,
125 pub format: String,
127 #[serde(default)]
129 pub cycle: CyclePeriod,
130 #[serde(default = "default_initial_value")]
132 pub initial_value: u64,
133 #[serde(default)]
135 pub step: IncrementStrategy,
136 #[serde(default = "default_true")]
138 pub is_enabled: bool,
139}
140
141fn default_initial_value() -> u64 { 1 }
142fn default_true() -> bool { true }
143
144impl SerialRule {
145 pub fn new(key: impl Into<String>, format: impl Into<String>) -> Self {
147 Self {
148 key: key.into(),
149 format: format.into(),
150 cycle: CyclePeriod::NoCycle,
151 initial_value: 1,
152 step: IncrementStrategy::Sequential,
153 is_enabled: true,
154 }
155 }
156
157 pub fn with_cycle(mut self, cycle: CyclePeriod) -> Self {
159 self.cycle = cycle;
160 self
161 }
162
163 pub fn with_initial_value(mut self, val: u64) -> Self {
165 self.initial_value = val;
166 self
167 }
168
169 pub fn with_step(mut self, step: IncrementStrategy) -> Self {
171 self.step = step;
172 self
173 }
174}
175
176#[derive(Debug, Clone, Serialize, Deserialize)]
180pub struct SerialRecord {
181 pub rule_key: String,
183 pub serial_no: String,
185 pub counter: u64,
187 pub cycle_value: String,
189 pub created_at: String,
191}
192
193#[derive(Debug, Clone, PartialEq, Eq)]
197enum FormatSegment {
198 Literal(String),
200 Year(bool),
202 Month,
204 Day,
206 Seq(u32),
208 Random(u32),
210 Timestamp(bool),
212}
213
214#[derive(Debug, Clone)]
218pub struct FormatEngine {
219 segments: Vec<FormatSegment>,
220}
221
222impl FormatEngine {
223 pub fn compile(format: &str) -> Result<Self, SerialError> {
229 let mut segments = Vec::new();
230 let chars: Vec<char> = format.chars().collect();
231 let len = chars.len();
232 let mut i = 0;
233
234 while i < len {
235 if chars[i] == '{' {
236 let mut j = i + 1;
237 while j < len && chars[j] != '}' {
238 j += 1;
239 }
240 if j >= len {
241 return Err(SerialError::FormatError(format!(
242 "未闭合的 '{{': 位置 {} 格式 '{}'", i, format
243 )));
244 }
245 let token: String = chars[i + 1..j].iter().collect();
246 segments.push(Self::parse_token(&token, format)?);
247 i = j + 1;
248 } else {
249 let start = i;
250 while i < len && chars[i] != '{' {
251 i += 1;
252 }
253 let lit: String = chars[start..i].iter().collect();
254 if !lit.is_empty() {
255 segments.push(FormatSegment::Literal(lit));
256 }
257 }
258 }
259
260 Ok(Self { segments })
261 }
262
263 fn parse_token(token: &str, full_format: &str) -> Result<FormatSegment, SerialError> {
264 match token {
265 "YYYY" => Ok(FormatSegment::Year(false)),
266 "YY" => Ok(FormatSegment::Year(true)),
267 "MM" => Ok(FormatSegment::Month),
268 "DD" => Ok(FormatSegment::Day),
269 "TS" => Ok(FormatSegment::Timestamp(false)),
270 "TSMS" => Ok(FormatSegment::Timestamp(true)),
271 _ => {
272 if let Some(w) = token.strip_prefix("SEQ:") {
273 let width: u32 = w.parse().map_err(|_| {
274 SerialError::FormatError(format!("SEQ 位数无效: '{}'", token))
275 })?;
276 if width == 0 {
277 return Err(SerialError::FormatError("SEQ 位数必须大于 0".into()));
278 }
279 Ok(FormatSegment::Seq(width))
280 } else if let Some(w) = token.strip_prefix("RAND:") {
281 let width: u32 = w.parse().map_err(|_| {
282 SerialError::FormatError(format!("RAND 位数无效: '{}'", token))
283 })?;
284 if width == 0 {
285 return Err(SerialError::FormatError("RAND 位数必须大于 0".into()));
286 }
287 Ok(FormatSegment::Random(width))
288 } else {
289 Err(SerialError::FormatError(format!(
290 "未知占位符: '{{{}}}' 在格式 '{}'", token, full_format
291 )))
292 }
293 }
294 }
295 }
296
297 pub fn render(&self, seq: u64, ts: Option<i64>) -> String {
302 let now = ts.and_then(|t| chrono::DateTime::from_timestamp(t, 0))
303 .unwrap_or_else(|| {
304 Local::now().naive_local().and_utc()
305 });
306
307 let mut result = String::new();
308 for seg in &self.segments {
309 match seg {
310 FormatSegment::Literal(s) => result.push_str(s),
311 FormatSegment::Year(short) => {
312 let y = now.year();
313 if *short {
314 result.push_str(&format!("{:02}", y % 100));
315 } else {
316 result.push_str(&format!("{:04}", y));
317 }
318 }
319 FormatSegment::Month => result.push_str(&format!("{:02}", now.month())),
320 FormatSegment::Day => result.push_str(&format!("{:02}", now.day())),
321 FormatSegment::Seq(w) => {
322 result.push_str(&format!("{:0width$}", seq, width = *w as usize));
323 }
324 FormatSegment::Random(w) => {
325 let max = 10u64.pow(*w);
326 let rand_val: u64 = rand::random::<u64>() % max;
327 result.push_str(&format!("{:0width$}", rand_val, width = *w as usize));
328 }
329 FormatSegment::Timestamp(ms) => {
330 if *ms {
331 result.push_str(&format!("{}", now.timestamp_millis()));
332 } else {
333 result.push_str(&format!("{}", now.timestamp()));
334 }
335 }
336 }
337 }
338 result
339 }
340}
341
342fn detect_seq_width(segments: &[FormatSegment]) -> u32 {
344 for seg in segments {
345 if let FormatSegment::Seq(w) = seg {
346 return *w;
347 }
348 }
349 20 }
351
352#[async_trait]
368pub trait SerialGenerator: Send + Sync {
369 async fn generate(&self, rule_key: &str) -> Result<String, SerialError>;
371
372 async fn batch_generate(
374 &self, rule_key: &str, count: u32,
375 ) -> Result<Vec<String>, SerialError>;
376
377 async fn peek(&self, rule_key: &str) -> Result<String, SerialError>;
379
380 async fn register_rule(&self, rule: SerialRule) -> Result<(), SerialError>;
382
383 async fn remove_rule(&self, rule_key: &str) -> Result<(), SerialError>;
385
386 async fn enable_rule(&self, rule_key: &str) -> Result<(), SerialError>;
388
389 async fn disable_rule(&self, rule_key: &str) -> Result<(), SerialError>;
391
392 async fn query_records(
394 &self, rule_key: &str, page: u64, page_size: u64,
395 ) -> Result<(Vec<SerialRecord>, u64), SerialError>;
396
397 async fn list_rules(&self) -> Result<Vec<SerialRule>, SerialError>;
399}
400
401#[derive(Debug, Clone)]
405struct CounterState {
406 value: u64,
407 cycle: String,
408}
409
410pub struct MemorySerialBackend {
422 rules: Mutex<HashMap<String, SerialRule>>,
423 counters: Mutex<HashMap<String, CounterState>>,
424 engines: Mutex<HashMap<String, FormatEngine>>,
425 records: Mutex<Vec<SerialRecord>>,
426 max_records: usize,
427}
428
429impl MemorySerialBackend {
430 pub fn new() -> Self {
432 Self {
433 rules: Mutex::new(HashMap::new()),
434 counters: Mutex::new(HashMap::new()),
435 engines: Mutex::new(HashMap::new()),
436 records: Mutex::new(Vec::new()),
437 max_records: 10000,
438 }
439 }
440
441 pub fn with_max_records(mut self, max: usize) -> Self {
443 self.max_records = max;
444 self
445 }
446
447 fn counter_key(rule_key: &str, cycle_value: &str) -> String {
448 if cycle_value.is_empty() {
449 rule_key.to_string()
450 } else {
451 format!("{}:{}", rule_key, cycle_value)
452 }
453 }
454
455 async fn get_or_init_counter(
456 &self, rule: &SerialRule,
457 ) -> Result<(String, u64), SerialError> {
458 let cycle_val = rule.cycle.current_value();
459 let ck = Self::counter_key(&rule.key, &cycle_val);
460
461 let mut counters = self.counters.lock().await;
462 if let Some(state) = counters.get(&ck) {
463 if state.cycle == cycle_val {
464 return Ok((ck, state.value));
465 }
466 }
467
468 let init = rule.initial_value;
469 counters.insert(
470 ck.clone(),
471 CounterState { value: init, cycle: cycle_val },
472 );
473 Ok((ck, init))
474 }
475}
476
477impl Default for MemorySerialBackend {
478 fn default() -> Self { Self::new() }
479}
480
481#[async_trait]
482impl SerialGenerator for MemorySerialBackend {
483 async fn generate(&self, rule_key: &str) -> Result<String, SerialError> {
484 let rules = self.rules.lock().await;
485 let rule = rules
486 .get(rule_key)
487 .ok_or_else(|| SerialError::RuleNotFound(rule_key.to_string()))?
488 .clone();
489 drop(rules);
490
491 if !rule.is_enabled {
493 return Err(SerialError::RuleDisabled(rule_key.to_string()));
494 }
495
496 let engine = {
497 let mut engines = self.engines.lock().await;
498 if !engines.contains_key(rule_key) {
499 let eng = FormatEngine::compile(&rule.format)?;
500 engines.insert(rule_key.to_string(), eng);
501 }
502 engines.get(rule_key).unwrap().clone()
503 };
504
505 let (ck, current_val) = self.get_or_init_counter(&rule).await?;
506
507 let next_val = match &rule.step {
508 IncrementStrategy::Sequential => current_val + 1,
509 IncrementStrategy::Random { max } => {
510 let max = *max;
511 let step: u64 = rand::random::<u64>() % if max > 0 { max } else { 1 } + 1;
512 current_val + step
513 }
514 };
515
516 let seq_width = detect_seq_width(&engine.segments);
517 let max_val = 10u64.pow(seq_width).saturating_sub(1);
518 if next_val > max_val {
519 return Err(SerialError::CounterOverflow(format!(
520 "规则 '{}' 序列号溢出: {} > 最大值 {}", rule_key, next_val, max_val
521 )));
522 }
523
524 let serial_no = engine.render(current_val, None);
525
526 {
527 let mut counters = self.counters.lock().await;
528 if let Some(state) = counters.get_mut(&ck) {
529 state.value = next_val;
530 }
531 }
532
533 {
534 let mut records = self.records.lock().await;
535 records.push(SerialRecord {
536 rule_key: rule_key.to_string(),
537 serial_no: serial_no.clone(),
538 counter: current_val,
539 cycle_value: rule.cycle.current_value(),
540 created_at: Local::now().format("%Y-%m-%dT%H:%M:%S%.3fZ").to_string(),
541 });
542 while records.len() > self.max_records {
543 records.remove(0);
544 }
545 }
546
547 Ok(serial_no)
548 }
549
550 async fn batch_generate(
551 &self, rule_key: &str, count: u32,
552 ) -> Result<Vec<String>, SerialError> {
553 let mut result = Vec::with_capacity(count as usize);
554 for _ in 0..count {
555 result.push(self.generate(rule_key).await?);
556 }
557 Ok(result)
558 }
559
560 async fn peek(&self, rule_key: &str) -> Result<String, SerialError> {
561 let rules = self.rules.lock().await;
562 let rule = rules
563 .get(rule_key)
564 .ok_or_else(|| SerialError::RuleNotFound(rule_key.to_string()))?
565 .clone();
566 drop(rules);
567
568 let engine = {
569 let mut engines = self.engines.lock().await;
570 if !engines.contains_key(rule_key) {
571 let eng = FormatEngine::compile(&rule.format)?;
572 engines.insert(rule_key.to_string(), eng);
573 }
574 engines.get(rule_key).unwrap().clone()
575 };
576
577 let cycle_val = rule.cycle.current_value();
578 let ck = Self::counter_key(rule_key, &cycle_val);
579
580 let current_val = {
581 let counters = self.counters.lock().await;
582 counters.get(&ck).map(|s| s.value).unwrap_or(rule.initial_value)
583 };
584
585 Ok(engine.render(current_val, None))
586 }
587
588 async fn register_rule(&self, rule: SerialRule) -> Result<(), SerialError> {
589 FormatEngine::compile(&rule.format)?;
590 let key = rule.key.clone();
591 self.rules.lock().await.insert(key.clone(), rule);
592 self.engines.lock().await.remove(&key);
593 Ok(())
594 }
595
596 async fn remove_rule(&self, rule_key: &str) -> Result<(), SerialError> {
597 self.rules.lock().await.remove(rule_key);
598 self.engines.lock().await.remove(rule_key);
599 Ok(())
600 }
601
602 async fn enable_rule(&self, rule_key: &str) -> Result<(), SerialError> {
603 let mut rules = self.rules.lock().await;
604 let rule = rules
605 .get_mut(rule_key)
606 .ok_or_else(|| SerialError::RuleNotFound(rule_key.to_string()))?;
607 rule.is_enabled = true;
608 Ok(())
609 }
610
611 async fn disable_rule(&self, rule_key: &str) -> Result<(), SerialError> {
612 let mut rules = self.rules.lock().await;
613 let rule = rules
614 .get_mut(rule_key)
615 .ok_or_else(|| SerialError::RuleNotFound(rule_key.to_string()))?;
616 rule.is_enabled = false;
617 Ok(())
618 }
619
620 async fn query_records(
621 &self, rule_key: &str, page: u64, page_size: u64,
622 ) -> Result<(Vec<SerialRecord>, u64), SerialError> {
623 let records = self.records.lock().await;
624 let filtered: Vec<&SerialRecord> = records
625 .iter()
626 .filter(|r| r.rule_key == rule_key)
627 .collect();
628 let total = filtered.len() as u64;
629 let offset = ((page.saturating_sub(1)) * page_size) as usize;
630 let data: Vec<SerialRecord> = filtered
631 .into_iter().rev()
632 .skip(offset).take(page_size as usize).cloned().collect();
633 Ok((data, total))
634 }
635
636 async fn list_rules(&self) -> Result<Vec<SerialRule>, SerialError> {
637 Ok(self.rules.lock().await.values().cloned().collect())
638 }
639}
640
641#[cfg(test)]
644mod tests {
645 use super::*;
646
647 #[test]
648 fn test_format_compile_basic() {
649 let eng = FormatEngine::compile("ORD{YYYY}{MM}{DD}{SEQ:8}").unwrap();
650 assert_eq!(eng.segments.len(), 5);
651 }
652
653 #[test]
654 fn test_format_compile_all_types() {
655 let eng = FormatEngine::compile("P{YY}{MM}{DD}-{RAND:4}{SEQ:6}").unwrap();
656 assert_eq!(eng.segments.len(), 7);
657 }
658
659 #[test]
660 fn test_format_render() {
661 let eng = FormatEngine::compile("TST{YYYY}{MM}{DD}{SEQ:4}").unwrap();
662 let ts = chrono::NaiveDate::from_ymd_opt(2024, 5, 24)
663 .unwrap()
664 .and_hms_opt(0, 0, 0)
665 .unwrap()
666 .and_utc()
667 .timestamp();
668 assert_eq!(eng.render(1, Some(ts)), "TST202405240001");
669 }
670
671 #[test]
672 fn test_format_render_padding() {
673 let eng = FormatEngine::compile("SEQ:{SEQ:6}").unwrap();
674 assert_eq!(eng.render(123, None), "SEQ:000123");
675 }
676
677 #[test]
678 fn test_format_parse_error() {
679 assert!(FormatEngine::compile("X{UNKNOWN}").is_err());
680 }
681
682 #[tokio::test]
683 async fn test_memory_generate_simple() {
684 let backend = MemorySerialBackend::new();
685 backend.register_rule(SerialRule::new("t", "T{SEQ:4}").with_initial_value(1)).await.unwrap();
686 assert_eq!(backend.generate("t").await.unwrap(), "T0001");
687 assert_eq!(backend.generate("t").await.unwrap(), "T0002");
688 }
689
690 #[tokio::test]
691 async fn test_memory_daily_cycle() {
692 let backend = MemorySerialBackend::new();
693 let rule = SerialRule::new("d", "{SEQ:3}").with_cycle(CyclePeriod::Daily).with_initial_value(1);
694 backend.register_rule(rule).await.unwrap();
695 assert_eq!(backend.generate("d").await.unwrap(), "001");
696 assert_eq!(backend.generate("d").await.unwrap(), "002");
697 }
698
699 #[tokio::test]
700 async fn test_memory_peek() {
701 let backend = MemorySerialBackend::new();
702 backend.register_rule(SerialRule::new("p", "{SEQ:3}").with_initial_value(100)).await.unwrap();
703 assert_eq!(backend.peek("p").await.unwrap(), "100");
704 assert_eq!(backend.generate("p").await.unwrap(), "100");
705 }
706
707 #[tokio::test]
708 async fn test_memory_register_remove() {
709 let backend = MemorySerialBackend::new();
710 backend.register_rule(SerialRule::new("tmp", "{SEQ:2}")).await.unwrap();
711 assert!(backend.list_rules().await.unwrap().len() == 1);
712 backend.remove_rule("tmp").await.unwrap();
713 assert!(backend.list_rules().await.unwrap().is_empty());
714 assert!(backend.generate("tmp").await.is_err());
715 }
716}