从零构建工业级流水号引擎:Spring Boot 2.7 + Redis 原子递增 + AntD Pro 全栈实战

【MES系统实战】从零构建高性能分布式流水号生成器 (Spring Boot 2.7 + Ant Design Pro 6)

💡 业务背景

在 MES(制造执行系统)中,流水号(如生产订单号、设备条码、批次号)是贯穿生产全生命周期的"身份证"。它通常面临以下挑战:

  1. 全局唯一性:在分布式部署环境下绝不能重号。
  2. 业务语义化 :需要支持 前缀 + 日期 + 序号 的灵活组合。
  3. 高并发性能:在高频报工、打印条码场景下,生成延迟必须极低。

本文将分享如何通过 Redis 原子计数 + MyBatis-Plus 配置化 + AntD Pro 自动化联动 实现一套工业级流水号引擎。


🏗 一、 系统架构设计

系统采用"配置驱动"的设计理念。所有的生成规则(位数、重置策略、模板)均持久化在数据库中,运行时由 Redis 承载高频计数。

核心模型设计 (Entity)

Java

typescript 复制代码
@TableName("sys_sequence_config")
public class SequenceConfig {
    private String bizCode;      // 业务唯一标识:如 ORDER, WORK_TICKET
    private String prefix;       // 编号前缀:如 WO
    private Integer digitLength; // 流水号位数:如 3位即 001
    private String pattern;      // 生成模板:{prefix}{date}{sequence}
    private Boolean resetPolicy; // 重置策略:1-每天重置
    // ... 审计字段 createdAt, updatedAt
}

💻 二、 后端核心实现:高性能与原子性

1. 统一响应封装

为了让前端拦截器能统一处理异常,我们封装了标准的 Result<T>

Java

csharp 复制代码
@Data
public class Result<T> {
    private Integer code;
    private String message;
    private T data;
    
    public static <T> Result<T> success(T data) {
        return new Result<>(200, "操作成功", data);
    }
}

2. Redis 原子自增逻辑

针对最常用的 年月日 + 当日序号 (例如 20260318001),我们利用 Redis 的 increment 指令:

Java

arduino 复制代码
/**
 * 定制化时间戳生成: 年月日 + 当日序号
 */
private String generateDateSequence(String bizCode, int length) {
    // 1. 生成日期前缀 (YYYYMMDD)
    String dateStr = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
    
    // 2. 构造隔离的 Redis Key
    String redisKey = "mes:seq:" + bizCode + ":" + dateStr;
    
    // 3. 执行原子自增,保证高并发下不重号
    Long sequence = redisTemplate.opsForValue().increment(redisKey);
    
    // 4. 首个序号设置 24h 过期,防止内存堆积
    if (sequence != null && sequence == 1) {
        redisTemplate.expire(redisKey, 24, TimeUnit.HOURS);
    }

    // 5. 格式化补零: 1 -> 001
    String seqStr = String.format("%0" + length + "d", sequence);
    return dateStr + seqStr;
}

🎨 三、 前端全栈联动:从 OpenAPI 到 UI

1. 生产力提升:max openapi

借助 Umi 4 的插件,我们直接从后端的 Swagger 文档生成 TypeScript 类型声明和 API 方法:

Bash

arduino 复制代码
# 执行命令一键生成 service 代码
npm run openapi

生成的调用代码示例:

TypeScript

csharp 复制代码
export async function getNextCode(params: API.getNextCodeParams) {
  return request<API.ResultString>('/system/sequence/generate', {
    method: 'GET',
    params: { ...params },
  });
}

2. 交互设计:可视化测试

在管理页面中,我们集成了一个"编码测试"功能。用户只需选择策略(时间戳、UUID、随机串),即可实时预览生成效果。

TypeScript

ini 复制代码
// 核心测试逻辑
const handleTestGenerate = async () => {
  setLoading(true);
  const res = await getNextCode({ generateType: 'timestamp', bizCode: 'ORDER' });
  if (res.code === 200) {
    setResultCode(res.data);
    message.success('生成成功!');
  }
  setLoading(false);
};

🚀 四、 进阶优化与避坑

1. 容器优化 (Undertow)

application.yml 中,我们弃用了默认的 Tomcat,转而使用 Undertow。Undertow 在处理小数据量高频请求时,内存占用更低,吞吐量更强,非常适合 MES 这种 API 密集型系统。

2. 对象转换 (MapStruct)

为了避免在 Controller 和 Service 之间频繁写 set/get,利用 MapStruct 在编译期生成转换代码,既保证了代码整洁,又拥有接近原生的性能。

3. 数据自愈建议

Q: 如果 Redis 宕机重启,计数器清零了怎么办?

A: 我们在配置表中设计了 targetTabletargetColumn。当 Redis 计数值为空时,系统可以自动去对应的业务表中执行 SELECT MAX(column),实现计数值的自动恢复(自愈)


📝 五、 结语

流水号生成器虽然只是系统的一个微小组件,但它体现了后端对并发安全 的把控和前端对开发效率的追求。通过这套方案,我们不仅解决了重号痛点,更将新业务编码规则的上线时间从"小时级"缩短到了"分钟级"。


如果这篇文章对你有帮助,欢迎点赞、收藏!你在分布式 ID 生成上有什么独特的见解?欢迎在评论区交流。

相关推荐
Oneslide2 小时前
达梦数据库开启自动提交
后端
jolimark2 小时前
Spring Boot 集成 Kettle
java·spring boot·后端
用户7344028193422 小时前
Spring Boot使用@Async实现异步调用:ThreadPoolTaskScheduler线程池的优雅关闭
后端
sthnyph2 小时前
Spring Framework 中文官方文档
java·后端·spring
zb200641202 小时前
Spring Boot 实战篇(四):实现用户登录与注册功能
java·spring boot·后端
青柠代码录3 小时前
【MySQL】事务:事务的隔离级别
后端
分享牛3 小时前
Operaton入门到精通22-Operaton 2.0 升级指南:Spring Boot 4 核心变更详解
java·spring boot·后端
jinanmichael3 小时前
SpringBoot 如何调用 WebService 接口
java·spring boot·后端
深蓝轨迹3 小时前
吃透 Spring Boot dataSource与Starter
java·spring boot·笔记·后端