游戏社交部分往往需要邀请码。以下是我的实现。供参考。其中有一些依赖的框架的特定代码,但不影响理解逻辑。值得强调的点是,去掉了几个在显示时容易混淆的字符。
import "math/rand"
const (
invitationCodeMinLength = 6
invitationCodeMaxLength = 10
// 排除的字符包括:数字0和1,大写字母D、I和O,小写字母l和o
invitationCodeChars = `23456789ABCEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz`
)
func GenInvitationCode() (string, err2.AppError) {
for i := invitationCodeMinLength; i <= invitationCodeMaxLength; i++ {
for j := 0; j < 3; j++ {
code := randCodeWithLen(i)
u, err := isUnique(code)
if err != nil {
return "", err
}
if u {
return code, nil
}
}
}
return "", err2.AppErrorFromProtoEnum(msg.ErrorCode_SERVICE_INTERNAL_ERROR)
}
func randCodeWithLen(length int) string {
cs := make([]byte, 0, length)
for i := 0; i < length; i++ {
n := rand.Intn(len(invitationCodeChars))
c := invitationCodeChars[n]
cs = append(cs, c)
}
return string(cs)
}
func isUnique(code string) (bool, err2.AppError) {
// 查询数据库
}
下面是单元测试。
func Test_randCodeWithLen_dup(t *testing.T) {
a := assert.New(t)
const count int = 1e8
codes := make(map[string]struct{}, count)
var dupCount int
for j := 0; j < count; j++ {
var dup bool
for i := invitationCodeMinLength; i <= invitationCodeMaxLength; i++ {
code := randCodeWithLen(i)
a.Equal(i, len(code))
if _, ok := codes[code]; ok {
dup = true
} else {
dup = false
codes[code] = struct{}{}
break
}
}
if dup {
dupCount++
}
}
t.Logf("count=%d, dupCount=%d", count, dupCount)
}
一亿次生成,重复次数为0。可以说明重复的概率很低。
建表脚本如下。
CREATE TABLE IF NOT EXISTS `invitation_code` (
`code` VARCHAR(10) NOT NULL,
`uid` BIGINT UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;