【JAVA】业务系统订单号,流水号生成规则工具类

设计业务系统订单号,流水号注意事项

  1. 唯一性:确保在分布式环境下ID不重复

  2. 有序性:ID随时间递增,有利于数据库索引性能

  3. 可读性:包含时间信息,便于人工识别

  4. 扩展性:支持业务前缀和类型区分

  5. 性能:本地生成,无网络开销

  6. 高并发:考虑线程安全,避免阻塞

根据实际业务需求,可以调整各部分位数分配,例如增加机器ID位数或减少序列号位数。

下面是一个完整的订单号和流水号生成工具类实现,考虑了高并发、分布式环境下的唯一性需求。

订单号/流水号生成工具类

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 业务系统订单号/流水号生成工具
 */
public class SerialNumberGenerator {

    // 日期时间格式化器(线程安全)
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
    
    // 自增序列(单机环境下使用)
    private static final AtomicLong SEQUENCE = new AtomicLong(0);
    
    // 机器ID(分布式环境下使用)
    private static final String MACHINE_ID = initMachineId();
    
    /**
     * 生成订单号(格式: ORDER + 时间 + 机器ID + 序列号)
     * 示例: ORDER2024051215230100110001
     */
    public static String generateOrderNo() {
        return generateSerialNumber("ORDER");
    }
    
    /**
     * 生成交易流水号(格式: TRANS + 时间 + 机器ID + 序列号)
     * 示例: TRANS2024051215230100110001
     */
    public static String generateTransNo() {
        return generateSerialNumber("TRANS");
    }
    
    /**
     * 通用序列号生成方法
     * @param prefix 前缀(如ORDER, TRANS等)
     */
    private static String generateSerialNumber(String prefix) {
        StringBuilder sb = new StringBuilder(prefix);
        
        // 1. 添加时间部分(14位)
        sb.append(DATE_TIME_FORMATTER.format(LocalDateTime.now()));
        
        // 2. 添加机器ID(4位)
        sb.append(MACHINE_ID);
        
        // 3. 添加序列号(6位)
        sb.append(String.format("%06d", SEQUENCE.incrementAndGet() % 999999));
        
        // 4. 添加随机后缀(2位)防止猜测
        sb.append(String.format("%02d", ThreadLocalRandom.current().nextInt(0, 99)));
        
        return sb.toString();
    }
    
    /**
     * 初始化机器ID(实际项目中可以从配置中心获取)
     */
    private static String initMachineId() {
        // 这里简单演示,实际项目中可以从配置文件或配置中心获取
        String machineId = System.getProperty("machine.id", "0001");
        return String.format("%04d", Integer.parseInt(machineId));
    }
    
    /**
     * 带业务类型的订单号生成
     * @param bizType 业务类型(2位字母)
     */
    public static String generateBizOrderNo(String bizType) {
        if(bizType == null || bizType.length() != 2) {
            throw new IllegalArgumentException("业务类型必须为2位字母");
        }
        return generateSerialNumber("ORD" + bizType.toUpperCase());
    }
}

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicLong;/*** 业务系统订单号/流水号生成工具*/
public class SerialNumberGenerator {// 日期时间格式化器(线程安全)private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");// 自增序列(单机环境下使用)private static final AtomicLong SEQUENCE = new AtomicLong(0);// 机器ID(分布式环境下使用)private static final String MACHINE_ID = initMachineId();/*** 生成订单号(格式: ORDER + 时间 + 机器ID + 序列号)* 示例: ORDER2024051215230100110001*/public static String generateOrderNo() {return generateSerialNumber("ORDER");}/*** 生成交易流水号(格式: TRANS + 时间 + 机器ID + 序列号)* 示例: TRANS2024051215230100110001*/public static String generateTransNo() {return generateSerialNumber("TRANS");}/*** 通用序列号生成方法* @param prefix 前缀(如ORDER, TRANS等)*/private static String generateSerialNumber(String prefix) {StringBuilder sb = new StringBuilder(prefix);// 1. 添加时间部分(14位)sb.append(DATE_TIME_FORMATTER.format(LocalDateTime.now()));// 2. 添加机器ID(4位)sb.append(MACHINE_ID);// 3. 添加序列号(6位)sb.append(String.format("%06d", SEQUENCE.incrementAndGet() % 999999));// 4. 添加随机后缀(2位)防止猜测sb.append(String.format("%02d", ThreadLocalRandom.current().nextInt(0, 99)));return sb.toString();}/*** 初始化机器ID(实际项目中可以从配置中心获取)*/private static String initMachineId() {// 这里简单演示,实际项目中可以从配置文件或配置中心获取String machineId = System.getProperty("machine.id", "0001");return String.format("%04d", Integer.parseInt(machineId));}/*** 带业务类型的订单号生成* @param bizType 业务类型(2位字母)*/public static String generateBizOrderNo(String bizType) {if(bizType == null || bizType.length() != 2) {throw new IllegalArgumentException("业务类型必须为2位字母");}return generateSerialNumber("ORD" + bizType.toUpperCase());}
}

第二种。雪花算法

对于分布式系统,推荐使用雪花算法(Snowflake)改进版:

import java.time.Instant;

/**
 * 分布式ID生成器(基于雪花算法改进)
 */
public class DistributedIdGenerator {
    // 起始时间戳(2024-01-01)
    private final static long START_TIMESTAMP = 1704067200000L;
    
    // 机器ID所占位数
    private final static long WORKER_ID_BITS = 5L;
    
    // 数据中心ID所占位数
    private final static long DATACENTER_ID_BITS = 5L;
    
    // 序列号所占位数
    private final static long SEQUENCE_BITS = 12L;
    
    // 最大机器ID
    private final static long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
    
    // 最大数据中心ID
    private final static long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BITS);
    
    // 机器ID左移位数
    private final static long WORKER_ID_SHIFT = SEQUENCE_BITS;
    
    // 数据中心ID左移位数
    private final static long DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
    
    // 时间戳左移位数
    private final static long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS;
    
    // 序列号掩码
    private final static long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);
    
    private final long workerId;
    private final long datacenterId;
    private long sequence = 0L;
    private long lastTimestamp = -1L;
    
    public DistributedIdGenerator(long workerId, long datacenterId) {
        if (workerId > MAX_WORKER_ID || workerId < 0) {
            throw new IllegalArgumentException("workerId不合法");
        }
        if (datacenterId > MAX_DATACENTER_ID || datacenterId < 0) {
            throw new IllegalArgumentException("datacenterId不合法");
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }
    
    public synchronized long nextId() {
        long timestamp = timeGen();
        
        if (timestamp < lastTimestamp) {
            throw new RuntimeException("时钟回拨异常");
        }
        
        if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & SEQUENCE_MASK;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        
        lastTimestamp = timestamp;
        
        return ((timestamp - START_TIMESTAMP) << TIMESTAMP_LEFT_SHIFT)
                | (datacenterId << DATACENTER_ID_SHIFT)
                | (workerId << WORKER_ID_SHIFT)
                | sequence;
    }
    
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }
    
    private long timeGen() {
        return Instant.now().toEpochMilli();
    }
    
    /**
     * 将生成的ID转换为订单号
     */
    public static String convertToOrderNo(long id) {
        return "ORD" + id;
    }
}

import java.time.Instant;/*** 分布式ID生成器(基于雪花算法改进)*/
public class DistributedIdGenerator {// 起始时间戳(2024-01-01)private final static long START_TIMESTAMP = 1704067200000L;// 机器ID所占位数private final static long WORKER_ID_BITS = 5L;// 数据中心ID所占位数private final static long DATACENTER_ID_BITS = 5L;// 序列号所占位数private final static long SEQUENCE_BITS = 12L;// 最大机器IDprivate final static long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);// 最大数据中心IDprivate final static long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BITS);// 机器ID左移位数private final static long WORKER_ID_SHIFT = SEQUENCE_BITS;// 数据中心ID左移位数private final static long DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;// 时间戳左移位数private final static long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS;// 序列号掩码private final static long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);private final long workerId;private final long datacenterId;private long sequence = 0L;private long lastTimestamp = -1L;public DistributedIdGenerator(long workerId, long datacenterId) {if (workerId > MAX_WORKER_ID || workerId < 0) {throw new IllegalArgumentException("workerId不合法");}if (datacenterId > MAX_DATACENTER_ID || datacenterId < 0) {throw new IllegalArgumentException("datacenterId不合法");}this.workerId = workerId;this.datacenterId = datacenterId;}public synchronized long nextId() {long timestamp = timeGen();if (timestamp < lastTimestamp) {throw new RuntimeException("时钟回拨异常");}if (timestamp == lastTimestamp) {sequence = (sequence + 1) & SEQUENCE_MASK;if (sequence == 0) {timestamp = tilNextMillis(lastTimestamp);}} else {sequence = 0L;}lastTimestamp = timestamp;return ((timestamp - START_TIMESTAMP) << TIMESTAMP_LEFT_SHIFT)| (datacenterId << DATACENTER_ID_SHIFT)| (workerId << WORKER_ID_SHIFT)| sequence;}private long tilNextMillis(long lastTimestamp) {long timestamp = timeGen();while (timestamp <= lastTimestamp) {timestamp = timeGen();}return timestamp;}private long timeGen() {return Instant.now().toEpochMilli();}/*** 将生成的ID转换为订单号*/public static String convertToOrderNo(long id) {return "ORD" + id;}
}

使用

// 修改日期格式
private static final String DATE_FORMAT = "yyyyMMddHHmmss";// 修改序列号长度
private static final int SERIAL_NUMBER_LENGTH = 4;// 修改订单前缀
private static final String ORDER_PREFIX = "ORD";

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/80510.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【嵌入式开发-SPI】

嵌入式开发-SPI ■ SPI简介■ SPI &#xff08;Standard SPI&#xff09;■ DSPI &#xff08;Dual SPI&#xff09;■ QSPI是 Queued SPI的简写 ■ SPI简介 SPI协议其实是包括&#xff1a;Standard SPI、Dual SPI和Queued SPI三种协议接口&#xff0c;分别对应3-wire, 4-wire…

基于HTTP头部字段的SQL注入:SQLi-labs第17-20关

前置知识&#xff1a;HTTP头部介绍 HTTP&#xff08;超文本传输协议&#xff09;头部&#xff08;Headers&#xff09;是客户端和服务器在通信时传递的元数据&#xff0c;用于控制请求和响应的行为、传递附加信息或定义内容类型等。它们分为请求头&#xff08;Request Headers&…

基于Qt开发的http/https客户端

成果展示&#xff1a; 使用Qt开发HTTP客户端主要依赖QNetworkAccessManager、QNetworkRequest和QNetworkReply三大核心类。以下是具体实现要点及最佳实践&#xff1a; 一、核心类与基础流程​​ 1.QNetworkAccessManager​​ 作为HTTP请求的管理者&#xff0c;负责异步处理…

自适应蒙特卡洛定位-AMCL

自适应蒙特卡洛定位&#xff0c;简称AMCL&#xff0c;主要提供定位功能并以/tf形式输出 蒙特卡洛算法的基本思想&#xff1a;当所要求的问题是某种事件出现的概率或者是某个变量的期望值时&#xff0c;它们可以通过某种"试验"的方法&#xff0c;得到这种事件出现的概…

鲁滨逊归结原理详解:期末考点+解题指南

1. 引言 归结原理&#xff08;Resolution Principle&#xff09; 是自动定理证明和逻辑推理的核心技术&#xff0c;由约翰艾伦罗宾逊&#xff08;John Alan Robinson&#xff09;于1965年提出。它是一阶谓词逻辑的机械化推理方法&#xff0c;广泛应用于人工智能&#xff08;如…

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1商用服务开通教程以及模型体验

在当今数字化浪潮迅猛推进的时代&#xff0c;云计算与人工智能技术的深度融合正不断催生出众多创新应用与服务&#xff0c;为企业和个人用户带来了前所未有的便利与发展机遇。本文将重点聚焦于在华为云这一行业领先的云计算平台上&#xff0c;对 DeepSeek-V3/R1 商用服务展开的…

Matlab基于PSO-MVMD粒子群算法优化多元变分模态分解

Matlab基于PSO-MVMD粒子群算法优化多元变分模态分解 目录 Matlab基于PSO-MVMD粒子群算法优化多元变分模态分解效果一览基本介绍程序设计参考资料效果一览 基本介绍 PSO-MVMD粒子群算法优化多元变分模态分解 可直接运行 分解效果好 适合作为创新点(Matlab完整源码和数据),以包…

自然语言处理NLP中的连续词袋(Continuous bag of words,CBOW)方法、优势、作用和程序举例

自然语言处理NLP中的连续词袋&#xff08;Continuous bag of words&#xff0c;CBOW&#xff09;方法、优势、作用和程序举例 目录 自然语言处理NLP中的连续词袋&#xff08;Continuous bag of words&#xff0c;CBOW&#xff09;方法、优势、作用和程序举例一、连续词袋( Cont…

商业模式解密:鸣鸣很忙下沉市场的隐忧,破局之路在何方?

文 | 大力财经 作者 | 魏力 在零售行业的版图中&#xff0c;“鸣鸣很忙”凭借独特的商业模式&#xff0c;在下沉市场异军突起&#xff0c;成为不可忽视的力量。555亿GMV、广泛的县域覆盖以及高比例的乡镇门店&#xff0c;无疑彰显了其在下沉市场的王者地位。然而&#xff0c;…

YOLOv5推理代码解析

代码如下 import cv2 import numpy as np import onnxruntime as ort import time import random# 画一个检测框 def plot_one_box(x, img, colorNone, labelNone, line_thicknessNone):"""description: 在图像上绘制一个矩形框。param:x: 框的坐标 [x1, y1, x…

CATIA高效工作指南——常规配置篇(二)

一、结构树&#xff08;Specification Tree&#xff09;操作技巧精讲 结构树是CATIA设计中记录模型历史与逻辑关系的核心模块&#xff0c;其高效管理直接影响设计效率。本节从基础操作到高级技巧进行系统梳理。 1.1 结构树激活与移动 ​​激活方式​​&#xff1a; ​​白线…

批量重命名bat

作为一名程序员&#xff0c;怎么可以自己一个个改文件名呢&#xff01; Windows的批量重命名会自动加上括号和空格&#xff0c;看着很不爽&#xff0c;写一个bat处理吧&#xff01;❥(ゝω・✿ฺ) 功能&#xff1a;将当前目录下的所有文件名里面当括号和空格都去掉。 用法&…

嵌入式软件开发常见warning之 warning: implicit declaration of function

文章目录 &#x1f9e9; 1. C 编译流程回顾&#xff08;背景&#xff09;&#x1f4cd; 2. 出现 warning 的具体阶段&#xff1a;**编译阶段&#xff08;Compilation&#xff09;**&#x1f9ec; 2.1 词法分析&#xff08;Lexical Analysis&#xff09;&#x1f332; 2.2 语法分…

【人工智能-agent】--Dify中MCP工具存数据到MySQL

本文记录的工作如下&#xff1a; 自定义MCP工具&#xff0c;爬取我的钢铁网数据爬取的数据插值处理自定义MCP工具&#xff0c;把爬取到的数据&#xff08;str&#xff09;存入本地excel表格中自定义MCP工具&#xff0c;把爬取到的数据&#xff08;str&#xff09;存入本地MySQ…

Golang 应用的 CI/CD 与 K8S 自动化部署全流程指南

一、CI/CD 流程设计与工具选择 1. 技术栈选择 版本控制&#xff1a;Git&#xff08;推荐 GitHub/GitLab&#xff09;CI 工具&#xff1a;Jenkins/GitLab CI/GitHub Actions&#xff08;本文以 GitHub Actions 为例&#xff09;容器化&#xff1a;Docker Docker Compose制品库…

网络基础1(应用层、传输层)

目录 一、应用层 1.1 序列化和反序列化 1.2 HTTP协议 1.2.1 URL 1.2.2 HTTP协议格式 1.2.3 HTTP服务器示例 二、传输层 2.1 端口号 2.1.1 netstat 2.1.2 pidof 2.2 UDP协议 2.2.1 UDP的特点 2.2.2 基于UDP的应用层…

基于大模型预测的吉兰 - 巴雷综合征综合诊疗方案研究报告大纲

目录 一、引言(一)研究背景(二)研究目的与意义二、大模型预测吉兰 - 巴雷综合征的理论基础与技术架构(一)大模型原理概述(二)技术架构设计三、术前预测与手术方案制定(一)术前预测内容(二)手术方案制定依据与策略四、术中监测与麻醉方案调整(一)术中监测指标与数…

【言语】刷题2

front&#xff1a;刷题1 ⭐ 前对策的说理类 题干 新时代是转型关口&#xff0c;要创新和开放&#xff08;前对策&#xff09;创新和开放不能一蹴而就&#xff0c;但是对于现代化很重要 BC片面&#xff0c;排除 A虽然表达出了创新和开放很重要&#xff0c;体现了现代化&#xf…

Blueprints - Gameplay Message Subsystem

一些学习笔记归档&#xff1b; Gameplay Message是C插件&#xff0c;安装方式是把插件文件夹拷贝到Plugins中&#xff08;没有的话需要新建该文件夹&#xff09;&#xff0c;然后再刷新源码&#xff0c;运行项目&#xff1b; 安装后还需要在插件中激活&#xff1a; 这样&#…

火山云网站搭建

使用火山引擎的 **火山云&#xff08;Volcano Engine Cloud&#xff09;** 搭建网站&#xff0c;主要涉及云服务器、存储、网络等核心云服务的配置。以下是搭建网站的基本步骤和关键点&#xff1a; --- ### **一、准备工作** 1. **注册火山引擎账号** - 访问火山引擎官网&…