请教一下大家如何生成唯一的会员卡号

我想的是日期+用户id,但随着用户id增加,会变的长

需要固定长度吗?比如10位,大家有好的办法吗?

备注:不需要参考我的想法,只要是10位唯一卡号就行

《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 44

我的會員編號直接從9位數開始固定一個起始值 字段類型設置為bigint

public function generateUserNo(): int
    {
        // 先查詢redis是否存在key
        // 如果存在 則使用key的值進行+100-200之間的隨機數
        // 如果不存在 則查找最新一筆會員編號值 +100-200 存入到redis
        // 如果不存在會員 則使用初始值:127347943
        $script = <<<LUA
            local val = redis.call('GET', KEYS[1])

            if val ~= false then
               val = val + ARGV[2]
            else
               val = ARGV[1] + ARGV[2]
            end

            redis.call('SET', KEYS[1], val)

            return val
        LUA;

        // 這一步可以做優化 不用每註冊一個用戶都去查一次最後編號
        $no = User::getLatestNo() ?? 127347943;

        return (int)redis()->eval($script, 1, 'latest_user_no', $no, mt_rand(100, 500));
    }
2个月前 评论

$number = 123;  
$length = 9; // 补全后的总长度  

// 将数字转换为字符串,因为str_pad()需要字符串作为输入  
$numberAsString = (string) $number;  

// 使用str_pad()在左侧补全0  
$paddedNumber = '1'.str_pad($numberAsString, $length, '0', STR_PAD_LEFT);  

echo $paddedNumber; // 输出 "1000000123"

简单点 别想的太复杂

2个月前 评论
雷雷 (作者) 2个月前
kis龍 2个月前
臭鼬 (楼主) 2个月前
qianfan 2个月前
臭鼬 (楼主) 2个月前

本质就是弄个唯一字符串,位数自己截取就可以

  • 时间+随机字符串
  • 数据库唯一索引
  • 雪花算法等专门生成唯一串
2个月前 评论

如果外露的话直接拼接肯定不行,一个是长度不一致,另外太好猜了,时间戳+用户id hash吧

2个月前 评论
臭鼬 (楼主) 2个月前

goalng版本

package code

import "strings"

const (
    BASE    = "A1B2C3D4E5F6G7H8I9JKLMNPQRSTUVWXYZ"
    DECIMAL = 34
    PAD     = "0"
    LEN     = 10
)

// id转code

func Encode(uid uint64) string {
    id := uid
    mod := uint64(0)
    res := ""
    for id != 0 {
        mod = id % DECIMAL
        id = id / DECIMAL
        res += string(BASE[mod])
    }
    resLen := len(res)
    if resLen < LEN {
        res += PAD
        for i := 0; i < LEN-resLen-1; i++ {
            res += string(BASE[(int(uid)+i)%DECIMAL])
        }
    }
    return res
}

func Decode(code string) uint64 {
    res := uint64(0)
    lenCode := len(code)
    baseArr := []byte(BASE)       // 字符串进制转换为byte数组
    baseRev := make(map[byte]int) // 进制数据键值转换为map
    for k, v := range baseArr {
        baseRev[v] = k
    }
    // 查找补位字符的位置
    isPad := strings.Index(code, PAD)
    if isPad != -1 {
        lenCode = isPad
    }
    r := 0
    for i := 0; i < lenCode; i++ {
        // 补充字符直接跳过
        if string(code[i]) == PAD {
            continue
        }
        index, ok := baseRev[code[i]]
        if !ok {
            return 0
        }
        b := uint64(1)
        for j := 0; j < r; j++ {
            b *= DECIMAL
        }
        res += uint64(index) * b
        r++
    }
    return res
}

uid可以转为唯一的字符串,字符串也可以反解uid,仅供参考

2个月前 评论

php版本

<?php

namespace Code;

class EncoderDecoder
{
    const BASE = "A1B2C3D4E5F6G7H8I9JKLMNPQRSTUVWXYZ";
    const DECIMAL = 34;
    const PAD = "0";
    const LEN = 10;

    public static function encode($uid)
    {
        $id = $uid;
        $mod = 0;
        $res = "";

        while ($id != 0) {
            $mod = $id % self::DECIMAL;
            $id = (int)($id / self::DECIMAL);
            $res .= self::BASE[$mod];
        }

        $resLen = strlen($res);

        if ($resLen < self::LEN) {
            $res .= self::PAD;

            for ($i = 0; $i < (self::LEN - $resLen - 1); $i++) {
                $res .= self::BASE[($uid + $i) % self::DECIMAL];
            }
        }

        return $res;
    }

    public static function decode($code)
    {
        $res = 0;
        $lenCode = strlen($code);
        $baseArr = str_split(self::BASE);
        $baseRev = array_flip($baseArr);

        $isPad = strpos($code, self::PAD);

        if ($isPad !== false) {
            $lenCode = $isPad;
        }

        $r = 0;

        for ($i = 0; $i < $lenCode; $i++) {
            if ($code[$i] == self::PAD) {
                continue;
            }

            $index = $baseRev[$code[$i]];

            if ($index === null) {
                return 0;
            }

            $b = 1;

            for ($j = 0; $j < $r; $j++) {
                $b *= self::DECIMAL;
            }

            $res += $index * $b;
            $r++;
        }

        return $res;
    }
}

// 例子
$uid = 123456789;
$encoded = EncoderDecoder::encode($uid);
$decoded = EncoderDecoder::decode($encoded);

echo "Original UID: $uid\n";
echo "Encoded: $encoded\n";
echo "Decoded: $decoded\n";

?>
翻译来自chatgpt
2个月前 评论

为什么要用用户id呢

2个月前 评论
臭鼬 (楼主) 2个月前

用户id最大位数补前导0?

2个月前 评论
Mutoulee

sprintf("%09d", $uid)或者就直接用uid吧,既然是会员号,就要彰显尊贵的前排数字、靓号;

2个月前 评论
臭鼬 (楼主) 2个月前
Mutoulee (作者) 2个月前
臭鼬 (楼主) 2个月前

我的會員編號直接從9位數開始固定一個起始值 字段類型設置為bigint

public function generateUserNo(): int
    {
        // 先查詢redis是否存在key
        // 如果存在 則使用key的值進行+100-200之間的隨機數
        // 如果不存在 則查找最新一筆會員編號值 +100-200 存入到redis
        // 如果不存在會員 則使用初始值:127347943
        $script = <<<LUA
            local val = redis.call('GET', KEYS[1])

            if val ~= false then
               val = val + ARGV[2]
            else
               val = ARGV[1] + ARGV[2]
            end

            redis.call('SET', KEYS[1], val)

            return val
        LUA;

        // 這一步可以做優化 不用每註冊一個用戶都去查一次最後編號
        $no = User::getLatestNo() ?? 127347943;

        return (int)redis()->eval($script, 1, 'latest_user_no', $no, mt_rand(100, 500));
    }
2个月前 评论
/**
 * 生成邀请码
 */
function get_invite_code($length = 8): string
{
    $codeAlphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    $token = '';
    for ($i = 0; $i < $length; ++$i) {
        $token .= $codeAlphabet[mt_rand(0, strlen($codeAlphabet) - 1)];
    }

    return $token;
}
2个月前 评论
sanders
sprintf('%010s', base_convert($userId,10,36));
2个月前 评论
/**
    * 生成卡号
    * @param int|string $userId 用户 ID
    * @return string
    */
function generate_card_no($userId): string
{
    do {
        $cardNo = sprintf('%d%02d%d', date('ymd'), random_int(0, 99), substr((string) $userId, -1));
    } while (User::query()->where('card_no', $cardNo)->exists());
    return $cardNo;
}

dd(generate_card_no(123));
// 240221533

日期六位,随机数两位,再加一位用户 ID,参考淘宝订单号生成规则,最后需要判断一下是否有重复的卡号

2个月前 评论

直接雪花算法生成不就好了

2个月前 评论
skarner

最简单、最简单、最简单的方法

用脚本先提前把卡号生成,并写入表中

等用户注册的时候, 在从表中逐个取出分发给用户即可

2个月前 评论
skarner (作者) 2个月前

写个定时脚本一次生成一亿,会员id,随机数就行,去重,保存起来,起码能用一年。

2个月前 评论

自增id,然自增起始值设置个10000000,类似这样的8为会员卡,就不用额外管理这个规则。 或者提前把卡号生成好,去获取

2个月前 评论

如果需要绝对不重复。bip39 了解一下。一般服务器不间断的生成到地球爆炸都不会有一个重复的

2个月前 评论

时间戳加随机数,长度太短估计不行的吧

2个月前 评论

redis 原子递增就可以了呀

2个月前 评论

提前生成好,随用随取就好了。

1个月前 评论

1.数据表字段唯一索引 2.搞个定时任务 redis 集合里面提前生成

1个月前 评论

你想保证绝对唯一的话,直接用订单自增ID才能保证真正唯一,什么算法都说徒增复杂度,浪费性能。先新建订单数据,拿到自增ID。然后对自增ID进行编码,然后修改订单编号字段。

1个月前 评论

直接使用时间戳,正好10位

1个月前 评论

直接使用UUID吧

1个月前 评论

為什麼搞那麼複雜呢,時間戳+隨機數 不就完事了,除非併發厲害,否則按併發數考量同時在線人數生成位數不就 ok了。沒所謂併發的話,直接時間戳就完事,還能知道客戶什麼時候註冊,多方便

1个月前 评论

1000000000+$id

1个月前 评论

这种卡号一般有以下特点:

  • 固定长度
  • 纯数字
  • 随机性
  • 唯一性
  • 其他(暂未想到)

可以先根据用户数量级限定一个卡号范围,比如十位数字,可以是 0000000000-9999999999

限定范围以后,可以使用脚本生成一个卡号池

这里可以使用 Redis 的 Set 类型存储。

接下来就是分配卡号逻辑了。使用时,使用 spop 命令随机获取一个卡号即可。

当卡号作废,或者有回收逻辑时,可以使用 sadd 命令重新加回卡号池。

1个月前 评论

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