18 位无重复订单号生成代码

这个系列相当于一些小的“工具”,拿来即用,为便捷存在。

思路说明

监听order模型创建事件,在写入数据库之前进行判断,订单号为空时生成18位有效数字编码。订单号的生成规则是8(年月日)+ 6(随机数)+ 4(时间戳后四位)

protected static function boot()
    {
        parent::boot();
        // 监听模型创建事件,在写入数据库之前触发
        static::creating(function ($model) {
            // 判断订单号字段no是否为空,为空的话调用订单号生成方法
            if (!$model->no) {
                $model->no = static::findAvailableNo();
                // 如果生成失败,就返回false
                if (!$model->no) {
                    return false;
                }
            }
        });
    }

public static function findAvailableNo()
    {
        $prefix = date('Ymd');
        for ($i = 0; $i < 18; $i++) {
            // 随机生成 6 位的数字,并创建订单号
            $no = $prefix.random_int(100000, 999999).substr(microtime(true),-4);
            // 判断是否已经存在
            if (!static::query()->where('no', $no)->exists()) {
                return $no;
            }
        }
        //写入日志
        \Log::warning('find order no failed');

        return false;
    }

个人理解

这个是在实战教程L05的基础上改的。将原来的20位改成了18位,弃用了时分秒,在后一部分使用了毫秒级时间戳的后四位,稍微测试了一下,在我本地环境生成10000条不重复的订单号耗时10s。

本作品采用《CC 协议》,转载必须注明作者和本文链接
空舟湖上~      ——Jouzeyu
lochpure
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 12

for循环的意思是,尝试18次吗

4年前 评论
lochpure (楼主) 4年前

个人认为:
不必杞人忧天的做很多无用的检测,这样反而拖慢了程序的运行效率。

这是我使用的方法(不知道你喜不喜欢)
1、生成规则
8(年月日)+ 6(随机数)+ 4(时间戳后四位)
2、数据库订单字段设唯一索引
3、在执行添加的时候,如果重复,重新执行一次生成规则
理解:毫米后四位+6位随机数基本很少出现重复值了,如果有重新执行方法。

4年前 评论
lochpure (楼主) 4年前
看上隔壁小花了啦 (作者) 4年前
苏亦坤 4年前
rovast

@看上隔壁小花了啦 赞同呀。只要在订单号掺入唯一建就能保证唯一性啦,比如把 order 表的 maxId 放到订单号里

4年前 评论

参考一下 snowflake 算法,64bit 的。一个 long 的事。

4年前 评论

这个太依赖系统时间了, findAvailableNo 返回值结构都不唯一?

4年前 评论

谨记,批量创建的时候,单号绝对不能用于排序!

4年前 评论

查数据库??这样很明显不好吧,我们之前是生成一批到内存中再取

4年前 评论
iwzh 4年前
rufo (作者) 3年前
gaorenhua

把 for 换成 do...while 会不会好点。

4年前 评论

第一种

function get_uuid($len = 0)
{
    $int = '';

    while (strlen($int) != $len) {
        $int .= mt_rand(0, 9);
    }

    return date('Ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8) . $int;
}

第二种

function get_order_no($prefix = '')
{
list($s1, $s2) = explode(' ', microtime());
return $prefix . substr(date("Ymdis"), 2) . substr(sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000), -2);
}
4年前 评论
lochpure (楼主) 4年前

年月日时分秒+随机6位 应该就不会重复吧?

4年前 评论

snowflake 了解下?

4年前 评论

测试了下订单生成带了点,导致微信提示无用订单号

3年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!