公众号推文发现一个介绍小米生成订单号生成规则,原文:https://mp.weixin.qq.com/s/YQO_PA-kumg5ZOgjDugtIQ
剩下的主要工作就是我们如何去设计一个订单号规则!
在设计规则之前,我们先来看看互联网几个大厂的订单号格式。
京东商城订单号格式:157444499
苏宁易购订单号格式:2000839647
凡客诚品订单号格式:213052230059
银泰网订单号格式:10030522161715
小米订单号格式:1111218032345170
我们先来分析一下凡客诚品和银泰网的订单号生成规则。
凡客诚品和银泰网订单号都含有 0522,这是因为这 2 张订单都是2013年5月22号下的订单。
基本猜测一下,凡客的订单规则是:业务编码+年的后2位+月+日+订单数;泰网的订单号规则:年的第三位数+业务编码+年的后1位+月+日+订单数;而京东商城和苏宁易购的订单号看不出规则。
最后我们来分析一下小米订单号1111218032345170
,可以将其分解成四个部分1——111218—03234—5170
。
- 第一部分,1 表示购买,2 表示退货。
- 第二部分,表示 2011 年 12 月 18 日下的单,前面两位省掉了。
- 第三部分,时间戳对应
00:53:54
,换算成秒是03234
秒。 - 最后一部分,表示在同一秒内下的第 5170 单,也就是说,小米认为,在一秒内不会超过一万个订单。
总结起来,小米的订单规则是:业务编码+年的后 2 位+月+日+秒+订单数,固定长度为16
,这种订单号规则可以保证 100 年不会重复!
===================分割线======================
然后我去小米下了个订单,发现中间的时分秒规则,和推文介绍并不一致,不过,这种思路对于大多数应用(并发量没那么高)来说,足够了
下面实现一把(第一位业务编码不要了):
依赖工具:redis、hutool工具类,StringUtils
package com.doc.test; import cn.hutool.core.date.DatePattern; import cn.hutool.core.date.DateUtil; import com.xdf.ucan.CloudDocWebApplication; import org.apache.commons.lang3.StringUtils; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.test.context.junit4.SpringRunner; import java.time.LocalDateTime; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** * @author lihaoyang * @date 2022/2/4 */ @RunWith(value = SpringRunner.class) @SpringBootTest(classes = CloudDocWebApplication.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) public class TestOrderNo { @Autowired StringRedisTemplate stringRedisTemplate; /** * 小米订单号1-11-12-18-03234-5170 * 第一部分,1 表示购买,2 表示退货。 * 第二部分,表示 2011 年 12 月 18 日下的单,前面两位省掉了。 * 第三部分,时间戳对应00:53:54,换算成秒是03234秒。 * 最后一部分,表示在同一秒内下的第 5170 单,也就是说,小米认为,在一秒内不会超过一万个订单。 * <p> * 小米的订单规则是:业务编码+年的后 2 位+月+日+秒+订单数,固定长度为16,这种订单号规则可以保证 100 年不会重复! * * 220205706030202 * 220205706030203 * 220205706030204 * 220205706030207 * 220205706030206 * 220205706030205 * 220205706030209 * 220205706030208 * 220205706030210 * * 111218032345170 */ @Test public void tttt() throws Exception{ ExecutorService executorService = Executors.newFixedThreadPool(10); for(int i=0;i<1000;i++){ executorService.execute(()->{ System.out.println(genOrderNo()); }); } Thread.sleep(10000); } private String genOrderNo(){ //获取当前时间 LocalDateTime now = LocalDateTime.now(); String YYMMDD = StringUtils.substring(DateUtil.format(now, DatePattern.PURE_DATE_PATTERN),2); String seconds = now.getHour()*3600+now.getMinute()*60+now.getSecond()+""; String secondsFMT = StringUtils.leftPad(seconds, 5, "0"); String orderNoPrefix = YYMMDD+secondsFMT; String redisKeyPrefix = "order_no:"+orderNoPrefix; Long increment = stringRedisTemplate.boundValueOps(redisKeyPrefix).increment(); if(increment == 1){ //每一秒的第一个订单号,设置一下key过期时间 stringRedisTemplate.boundValueOps(redisKeyPrefix).expire(60, TimeUnit.SECONDS); } return orderNoPrefix+StringUtils.leftPad(String.valueOf(increment),4,"0"); } }
生成一批订单号看看:
220205762470003
220205762470007
220205762470008
220205762470005
220205762470006
220205762470002
220205762470004
220205762470010
220205762470009
220205762470001
220205762470012
220205762470011
220205762470015
220205762470016
220205762470013
220205762470014
220205762470019
220205762470018
220205762470017
220205762470020
220205762470021
220205762470027
220205762470025
220205762470023
220205762470024
220205762470026
220205762470028
220205762470029
220205762470022
220205762470032
220205762470037
220205762470038
220205762470039
220205762470030
220205762470031
220205762470033
220205762470035
第一秒生成了410个订单号,第二秒生成了590个,正好1000个。