需求
需要根据用户 ID(数值型)生成一个唯一的的邀请码,用于邀请新用户注册。
需求分析
从业务需求上来看,邀请码有以下几个强制性的要求:
- 不可重复:不同用户 ID 生成的邀请码是不同的。
- 唯一确定:一个用户 ID 只能生成一个邀请码。
- 长度不能太长,6-10 位是合适的区间
- 是否可逆:是否需要通过邀请码反推对应的用户 ID 是什么
在这些需求的约束下,我们来看看相对来说比较通用的邀请码生成算法。
26个字母+10个数字,去掉容易混淆的,O、0、I、1等字符就剩下32个。
<?php
class InvitationCodeUtil {
// 定义字符集
private static array $CHARS = ["Y","2","U","K","X","V","C","F","N","S","6","8","G","Z","Q","7","A","9","P","H","5","M","R","L","D","J","4","T","W","E","3","B"];
private const CHARS_LENGTH = 32;
private const SLAT = 3312427;
private const PRIME1 = 3;
private const PRIME2 = 11;
// 生成邀请码
public static function gen($id, $length = 6): string
{
// 对 ID 进行加密处理
$id = $id * self::PRIME1 + self::SLAT;
$b = [];
$b[0] = $id;
for ($i = 0; $i < $length - 1; $i++) {
$b[$i + 1] = $b[$i] / self::CHARS_LENGTH;
$b[$i] = (int) ($b[$i] + $i * $b[0]) % self::CHARS_LENGTH;
}
// 计算邀请码索引
$tmp = 0;
for ($i = 0; $i < $length - 2; $i++) {
$tmp += $b[$i];
}
$b[$length - 1] = $tmp * self::PRIME1 % self::CHARS_LENGTH;
// 混淆生成邀请码
$codeIndexArray = [];
for ($i = 0; $i < $length; $i++) {
$codeIndexArray[$i] = $b[$i * self::PRIME2 % $length];
}
$buffer = '';
foreach ($codeIndexArray as $index) {
$buffer .= self::$CHARS[$index];
}
return $buffer;
}
// 解密邀请码获取原始 ID
public static function decode($code): ?int
{
$length = strlen($code);
// 将字符转换为对应数字
$a = [];
for ($i = 0; $i < $length; $i++) {
$c = $code[$i];
$index = self::findIndex($c);
if ($index == -1) {
return null;
}
$a[$i * self::PRIME2 % $length] = $index;
}
// 逆向计算出原始 ID
$b = [];
for ($i = $length - 2; $i >= 0; $i--) {
$b[$i] = ($a[$i] - $a[0] * $i + self::CHARS_LENGTH * $i) % self::CHARS_LENGTH;
}
$res = 0;
for ($i = $length - 2; $i >= 0; $i--) {
$res += $b[$i];
$res *= ($i > 0 ? self::CHARS_LENGTH : 1);
}
return (int) (($res - self::SLAT) / self::PRIME1);
}
// 查找字符在字符集中的位置
public static function findIndex($c): int
{
foreach (self::$CHARS as $key => $char) {
if ($char == $c) {
return $key;
}
}
return -1;
}
}
// 测试示例
$id = 123456;
$encryptedCode = InvitationCodeUtil::gen($id);
echo "加密后的邀请码: $encryptedCode\n"; // 8N79U4
$decodedUID = InvitationCodeUtil::decode($encryptedCode);
echo "解密后的 UID: $decodedUID\n"; // 123456