use std::collections::HashMap;
use std::fmt;
use async_trait::async_trait;
use chrono::{Local, Datelike};
use serde::{Deserialize, Serialize};
use tokio::sync::Mutex;
#[derive(Debug, Clone)]
pub enum SerialError {
RuleNotFound(String),
RuleDisabled(String),
FormatError(String),
CounterOverflow(String),
StorageError(String),
}
impl fmt::Display for SerialError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::RuleNotFound(k) => write!(f, "单号规则未找到: {}", k),
Self::RuleDisabled(k) => write!(f, "单号规则已禁用: {}", k),
Self::FormatError(msg) => write!(f, "格式错误: {}", msg),
Self::CounterOverflow(msg) => write!(f, "计数器溢出: {}", msg),
Self::StorageError(msg) => write!(f, "存储错误: {}", msg),
}
}
}
impl std::error::Error for SerialError {}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum CyclePeriod {
#[default]
NoCycle,
Daily,
Monthly,
Yearly,
}
impl CyclePeriod {
pub fn current_value(&self) -> String {
let now = Local::now();
match self {
Self::NoCycle => String::new(),
Self::Daily => format!("{:04}{:02}{:02}", now.year(), now.month(), now.day()),
Self::Monthly => format!("{:04}{:02}", now.year(), now.month()),
Self::Yearly => format!("{:04}", now.year()),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum IncrementStrategy {
Sequential,
Random { max: u64 },
}
impl Default for IncrementStrategy {
fn default() -> Self { Self::Sequential }
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SerialRule {
pub key: String,
pub format: String,
#[serde(default)]
pub cycle: CyclePeriod,
#[serde(default = "default_initial_value")]
pub initial_value: u64,
#[serde(default)]
pub step: IncrementStrategy,
#[serde(default = "default_true")]
pub is_enabled: bool,
}
fn default_initial_value() -> u64 { 1 }
fn default_true() -> bool { true }
impl SerialRule {
pub fn new(key: impl Into<String>, format: impl Into<String>) -> Self {
Self {
key: key.into(),
format: format.into(),
cycle: CyclePeriod::NoCycle,
initial_value: 1,
step: IncrementStrategy::Sequential,
is_enabled: true,
}
}
pub fn with_cycle(mut self, cycle: CyclePeriod) -> Self {
self.cycle = cycle;
self
}
pub fn with_initial_value(mut self, val: u64) -> Self {
self.initial_value = val;
self
}
pub fn with_step(mut self, step: IncrementStrategy) -> Self {
self.step = step;
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SerialRecord {
pub rule_key: String,
pub serial_no: String,
pub counter: u64,
pub cycle_value: String,
pub created_at: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum FormatSegment {
Literal(String),
Year(bool),
Month,
Day,
Seq(u32),
Random(u32),
Timestamp(bool),
}
#[derive(Debug, Clone)]
pub struct FormatEngine {
segments: Vec<FormatSegment>,
}
impl FormatEngine {
pub fn compile(format: &str) -> Result<Self, SerialError> {
let mut segments = Vec::new();
let chars: Vec<char> = format.chars().collect();
let len = chars.len();
let mut i = 0;
while i < len {
if chars[i] == '{' {
let mut j = i + 1;
while j < len && chars[j] != '}' {
j += 1;
}
if j >= len {
return Err(SerialError::FormatError(format!(
"未闭合的 '{{': 位置 {} 格式 '{}'", i, format
)));
}
let token: String = chars[i + 1..j].iter().collect();
segments.push(Self::parse_token(&token, format)?);
i = j + 1;
} else {
let start = i;
while i < len && chars[i] != '{' {
i += 1;
}
let lit: String = chars[start..i].iter().collect();
if !lit.is_empty() {
segments.push(FormatSegment::Literal(lit));
}
}
}
Ok(Self { segments })
}
fn parse_token(token: &str, full_format: &str) -> Result<FormatSegment, SerialError> {
match token {
"YYYY" => Ok(FormatSegment::Year(false)),
"YY" => Ok(FormatSegment::Year(true)),
"MM" => Ok(FormatSegment::Month),
"DD" => Ok(FormatSegment::Day),
"TS" => Ok(FormatSegment::Timestamp(false)),
"TSMS" => Ok(FormatSegment::Timestamp(true)),
_ => {
if let Some(w) = token.strip_prefix("SEQ:") {
let width: u32 = w.parse().map_err(|_| {
SerialError::FormatError(format!("SEQ 位数无效: '{}'", token))
})?;
if width == 0 {
return Err(SerialError::FormatError("SEQ 位数必须大于 0".into()));
}
Ok(FormatSegment::Seq(width))
} else if let Some(w) = token.strip_prefix("RAND:") {
let width: u32 = w.parse().map_err(|_| {
SerialError::FormatError(format!("RAND 位数无效: '{}'", token))
})?;
if width == 0 {
return Err(SerialError::FormatError("RAND 位数必须大于 0".into()));
}
Ok(FormatSegment::Random(width))
} else {
Err(SerialError::FormatError(format!(
"未知占位符: '{{{}}}' 在格式 '{}'", token, full_format
)))
}
}
}
}
pub fn render(&self, seq: u64, ts: Option<i64>) -> String {
let now = ts.and_then(|t| chrono::DateTime::from_timestamp(t, 0))
.unwrap_or_else(|| {
Local::now().naive_local().and_utc()
});
let mut result = String::new();
for seg in &self.segments {
match seg {
FormatSegment::Literal(s) => result.push_str(s),
FormatSegment::Year(short) => {
let y = now.year();
if *short {
result.push_str(&format!("{:02}", y % 100));
} else {
result.push_str(&format!("{:04}", y));
}
}
FormatSegment::Month => result.push_str(&format!("{:02}", now.month())),
FormatSegment::Day => result.push_str(&format!("{:02}", now.day())),
FormatSegment::Seq(w) => {
result.push_str(&format!("{:0width$}", seq, width = *w as usize));
}
FormatSegment::Random(w) => {
let max = 10u64.pow(*w);
let rand_val: u64 = rand::random::<u64>() % max;
result.push_str(&format!("{:0width$}", rand_val, width = *w as usize));
}
FormatSegment::Timestamp(ms) => {
if *ms {
result.push_str(&format!("{}", now.timestamp_millis()));
} else {
result.push_str(&format!("{}", now.timestamp()));
}
}
}
}
result
}
}
fn detect_seq_width(segments: &[FormatSegment]) -> u32 {
for seg in segments {
if let FormatSegment::Seq(w) = seg {
return *w;
}
}
20 }
#[async_trait]
pub trait SerialGenerator: Send + Sync {
async fn generate(&self, rule_key: &str) -> Result<String, SerialError>;
async fn batch_generate(
&self, rule_key: &str, count: u32,
) -> Result<Vec<String>, SerialError>;
async fn peek(&self, rule_key: &str) -> Result<String, SerialError>;
async fn register_rule(&self, rule: SerialRule) -> Result<(), SerialError>;
async fn remove_rule(&self, rule_key: &str) -> Result<(), SerialError>;
async fn enable_rule(&self, rule_key: &str) -> Result<(), SerialError>;
async fn disable_rule(&self, rule_key: &str) -> Result<(), SerialError>;
async fn query_records(
&self, rule_key: &str, page: u64, page_size: u64,
) -> Result<(Vec<SerialRecord>, u64), SerialError>;
async fn list_rules(&self) -> Result<Vec<SerialRule>, SerialError>;
}
#[derive(Debug, Clone)]
struct CounterState {
value: u64,
cycle: String,
}
pub struct MemorySerialBackend {
rules: Mutex<HashMap<String, SerialRule>>,
counters: Mutex<HashMap<String, CounterState>>,
engines: Mutex<HashMap<String, FormatEngine>>,
records: Mutex<Vec<SerialRecord>>,
max_records: usize,
}
impl MemorySerialBackend {
pub fn new() -> Self {
Self {
rules: Mutex::new(HashMap::new()),
counters: Mutex::new(HashMap::new()),
engines: Mutex::new(HashMap::new()),
records: Mutex::new(Vec::new()),
max_records: 10000,
}
}
pub fn with_max_records(mut self, max: usize) -> Self {
self.max_records = max;
self
}
fn counter_key(rule_key: &str, cycle_value: &str) -> String {
if cycle_value.is_empty() {
rule_key.to_string()
} else {
format!("{}:{}", rule_key, cycle_value)
}
}
async fn get_or_init_counter(
&self, rule: &SerialRule,
) -> Result<(String, u64), SerialError> {
let cycle_val = rule.cycle.current_value();
let ck = Self::counter_key(&rule.key, &cycle_val);
let mut counters = self.counters.lock().await;
if let Some(state) = counters.get(&ck) {
if state.cycle == cycle_val {
return Ok((ck, state.value));
}
}
let init = rule.initial_value;
counters.insert(
ck.clone(),
CounterState { value: init, cycle: cycle_val },
);
Ok((ck, init))
}
}
impl Default for MemorySerialBackend {
fn default() -> Self { Self::new() }
}
#[async_trait]
impl SerialGenerator for MemorySerialBackend {
async fn generate(&self, rule_key: &str) -> Result<String, SerialError> {
let rules = self.rules.lock().await;
let rule = rules
.get(rule_key)
.ok_or_else(|| SerialError::RuleNotFound(rule_key.to_string()))?
.clone();
drop(rules);
if !rule.is_enabled {
return Err(SerialError::RuleDisabled(rule_key.to_string()));
}
let engine = {
let mut engines = self.engines.lock().await;
if !engines.contains_key(rule_key) {
let eng = FormatEngine::compile(&rule.format)?;
engines.insert(rule_key.to_string(), eng);
}
engines.get(rule_key).unwrap().clone()
};
let (ck, current_val) = self.get_or_init_counter(&rule).await?;
let next_val = match &rule.step {
IncrementStrategy::Sequential => current_val + 1,
IncrementStrategy::Random { max } => {
let max = *max;
let step: u64 = rand::random::<u64>() % if max > 0 { max } else { 1 } + 1;
current_val + step
}
};
let seq_width = detect_seq_width(&engine.segments);
let max_val = 10u64.pow(seq_width).saturating_sub(1);
if next_val > max_val {
return Err(SerialError::CounterOverflow(format!(
"规则 '{}' 序列号溢出: {} > 最大值 {}", rule_key, next_val, max_val
)));
}
let serial_no = engine.render(current_val, None);
{
let mut counters = self.counters.lock().await;
if let Some(state) = counters.get_mut(&ck) {
state.value = next_val;
}
}
{
let mut records = self.records.lock().await;
records.push(SerialRecord {
rule_key: rule_key.to_string(),
serial_no: serial_no.clone(),
counter: current_val,
cycle_value: rule.cycle.current_value(),
created_at: Local::now().format("%Y-%m-%dT%H:%M:%S%.3fZ").to_string(),
});
while records.len() > self.max_records {
records.remove(0);
}
}
Ok(serial_no)
}
async fn batch_generate(
&self, rule_key: &str, count: u32,
) -> Result<Vec<String>, SerialError> {
let mut result = Vec::with_capacity(count as usize);
for _ in 0..count {
result.push(self.generate(rule_key).await?);
}
Ok(result)
}
async fn peek(&self, rule_key: &str) -> Result<String, SerialError> {
let rules = self.rules.lock().await;
let rule = rules
.get(rule_key)
.ok_or_else(|| SerialError::RuleNotFound(rule_key.to_string()))?
.clone();
drop(rules);
let engine = {
let mut engines = self.engines.lock().await;
if !engines.contains_key(rule_key) {
let eng = FormatEngine::compile(&rule.format)?;
engines.insert(rule_key.to_string(), eng);
}
engines.get(rule_key).unwrap().clone()
};
let cycle_val = rule.cycle.current_value();
let ck = Self::counter_key(rule_key, &cycle_val);
let current_val = {
let counters = self.counters.lock().await;
counters.get(&ck).map(|s| s.value).unwrap_or(rule.initial_value)
};
Ok(engine.render(current_val, None))
}
async fn register_rule(&self, rule: SerialRule) -> Result<(), SerialError> {
FormatEngine::compile(&rule.format)?;
let key = rule.key.clone();
self.rules.lock().await.insert(key.clone(), rule);
self.engines.lock().await.remove(&key);
Ok(())
}
async fn remove_rule(&self, rule_key: &str) -> Result<(), SerialError> {
self.rules.lock().await.remove(rule_key);
self.engines.lock().await.remove(rule_key);
Ok(())
}
async fn enable_rule(&self, rule_key: &str) -> Result<(), SerialError> {
let mut rules = self.rules.lock().await;
let rule = rules
.get_mut(rule_key)
.ok_or_else(|| SerialError::RuleNotFound(rule_key.to_string()))?;
rule.is_enabled = true;
Ok(())
}
async fn disable_rule(&self, rule_key: &str) -> Result<(), SerialError> {
let mut rules = self.rules.lock().await;
let rule = rules
.get_mut(rule_key)
.ok_or_else(|| SerialError::RuleNotFound(rule_key.to_string()))?;
rule.is_enabled = false;
Ok(())
}
async fn query_records(
&self, rule_key: &str, page: u64, page_size: u64,
) -> Result<(Vec<SerialRecord>, u64), SerialError> {
let records = self.records.lock().await;
let filtered: Vec<&SerialRecord> = records
.iter()
.filter(|r| r.rule_key == rule_key)
.collect();
let total = filtered.len() as u64;
let offset = ((page.saturating_sub(1)) * page_size) as usize;
let data: Vec<SerialRecord> = filtered
.into_iter().rev()
.skip(offset).take(page_size as usize).cloned().collect();
Ok((data, total))
}
async fn list_rules(&self) -> Result<Vec<SerialRule>, SerialError> {
Ok(self.rules.lock().await.values().cloned().collect())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_compile_basic() {
let eng = FormatEngine::compile("ORD{YYYY}{MM}{DD}{SEQ:8}").unwrap();
assert_eq!(eng.segments.len(), 5);
}
#[test]
fn test_format_compile_all_types() {
let eng = FormatEngine::compile("P{YY}{MM}{DD}-{RAND:4}{SEQ:6}").unwrap();
assert_eq!(eng.segments.len(), 7);
}
#[test]
fn test_format_render() {
let eng = FormatEngine::compile("TST{YYYY}{MM}{DD}{SEQ:4}").unwrap();
let ts = chrono::NaiveDate::from_ymd_opt(2024, 5, 24)
.unwrap()
.and_hms_opt(0, 0, 0)
.unwrap()
.and_utc()
.timestamp();
assert_eq!(eng.render(1, Some(ts)), "TST202405240001");
}
#[test]
fn test_format_render_padding() {
let eng = FormatEngine::compile("SEQ:{SEQ:6}").unwrap();
assert_eq!(eng.render(123, None), "SEQ:000123");
}
#[test]
fn test_format_parse_error() {
assert!(FormatEngine::compile("X{UNKNOWN}").is_err());
}
#[tokio::test]
async fn test_memory_generate_simple() {
let backend = MemorySerialBackend::new();
backend.register_rule(SerialRule::new("t", "T{SEQ:4}").with_initial_value(1)).await.unwrap();
assert_eq!(backend.generate("t").await.unwrap(), "T0001");
assert_eq!(backend.generate("t").await.unwrap(), "T0002");
}
#[tokio::test]
async fn test_memory_daily_cycle() {
let backend = MemorySerialBackend::new();
let rule = SerialRule::new("d", "{SEQ:3}").with_cycle(CyclePeriod::Daily).with_initial_value(1);
backend.register_rule(rule).await.unwrap();
assert_eq!(backend.generate("d").await.unwrap(), "001");
assert_eq!(backend.generate("d").await.unwrap(), "002");
}
#[tokio::test]
async fn test_memory_peek() {
let backend = MemorySerialBackend::new();
backend.register_rule(SerialRule::new("p", "{SEQ:3}").with_initial_value(100)).await.unwrap();
assert_eq!(backend.peek("p").await.unwrap(), "100");
assert_eq!(backend.generate("p").await.unwrap(), "100");
}
#[tokio::test]
async fn test_memory_register_remove() {
let backend = MemorySerialBackend::new();
backend.register_rule(SerialRule::new("tmp", "{SEQ:2}")).await.unwrap();
assert!(backend.list_rules().await.unwrap().len() == 1);
backend.remove_rule("tmp").await.unwrap();
assert!(backend.list_rules().await.unwrap().is_empty());
assert!(backend.generate("tmp").await.is_err());
}
}