最新发现经常使用的
order_no生成订单号直接重复,且很多项目都使用的是如下的方式生成。这里不实际说明与数据库效验的唯一性,根据需要自行验证处理即可。
function order_no()
{
return date('Ymd') . substr(implode(null, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
}
var_dump(order_no()); ===> string(16) "2023071297101571"
var_dump(order_no()); ===> string(16) "2023071297101571"
var_dump(order_no()); ===> string(16) "2023071297101571"
解析实现过程
- uniqid(prefix, more_entropy)
uniqid() 函数基于以微秒计的当前时间,生成一个唯一的 ID 相对而言,足以支持系统生成唯一性的ID,但是大型并发系统不能够100%保证。
- prefix 可选参数。规定唯一 ID 的前缀。如果两个脚本恰好在相同的微秒生成 ID,该参数很有用。
- more_entropy 可选。规定位于返回值末尾的更多的熵。这将让结果更具唯一性。当设置为 TRUE,返回字符串为 23 个字符。默认是 FALSE,返回字符串为 13 个字符。
- substr(uniqid(), 7, 13)
uniqid 不传参数,则默认长度为13位,获取7 - 13的字符,可保证唯一性
- str_split
函数把字符串分割到数组中:字符串按照每个字符归类到数组
- array_map('ord', str_split(substr(uniqid(), 7, 13), 1))
array_map 将用户自定义函数作用到数组中的每个值上,并返回用户自定义函数作用后的带有新的值的数组 ord 返回字符串中第一个字符的 ASCII 值
- 通过
array_map调用ord方法,获取str_split(substr(uniqid(), 7, 13)为数组中的每个值转换为ASCII值
- 通过
- implode(null, array_map('ord', str_split(substr(uniqid(), 7, 13), 1)))
返回一个由数组元素组合成的字符串,即把数组中的值按照顺序拼接成一个字符串。
- substr(implode(null, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8)
最主要的原因是这个逻辑导致订单重复!!! substr 返回字符串的一部分
substr(string, 0, 8) 说明是截取前8位,偶吼?什么意思.implode转换array_map为字符串之后,字符串长度基本上为12位,但是同时触发这个逻辑string(12) "535498984854" string(12) "535498984899" string(13) "5354989848100"- 发现,最后的一个字符是13位。
- 发现规则:字符串的前面部分基本上是一致,唯一的区别就是后面几位数是不一致的;而substr截取逻辑确实仅截取前8位,那么有很大可能性拿到的字符就是一致的,而正确的做法应该截取后8位。
- 应使用
substr(string, -8, 8)的方式截取。 这里只说截取的,因为限定了本身订单的长度;也可移除截取逻辑,保证单号唯一性的问题。
调整之后的生成订单编号order_no方法
function order_no()
{
return date('Ymd') . substr(implode(null, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), -8, 8);
}
var_dump(order_no()); ===> string(16) "2023071210010253"
var_dump(order_no()); ===> string(16) "2023071210010298"
var_dump(order_no()); ===> string(16) "2023071200102100"
本文在这里仅说明此方法的优化方式,也可使用雪花算法等。
追加随机数、机器码等组合的方式生成订单编号。
不错不错||ヽ( ̄▽ ̄)ノミ|Ю