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 前端工作流等。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 12

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

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

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

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

4年前 评论

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

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

第一种

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年前

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

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

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

4年前 评论

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

4年前 评论

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

4年前 评论
gaorenhua

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

4年前 评论

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

4年前 评论

snowflake 了解下?

4年前 评论

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

3年前 评论

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