兑换码的设计
需求背景
每个活动需要多次生成兑换码批次并导出兑换码,且记录兑换码的使用状态.每个兑换码只能使用一次.
需求分析
兑换码的生成要做到防暴力破解和防猜.
使用redis记录兑换码的使用情况 未使用/已使用
使用redis锁住该兑换码,避免兑换码被重复使用
方案设计
使用数字+24位字母(排除I和O),共34个字符组成的13位字符串来作为兑换码.
2位随机码
5位用做批次id:共支持45,435,424个批次(批次表的主键id)
4位用做库存顺序码共支持1,336,336
2位校验码,由前面的11位字符串进行CRC16编码得到int数,然后对34的2次方进行取余,确保生成的34进制字符最多两个,如果不足则补0
最后将以上13位字符按预定的打乱规则进行打乱,规则可以由13位中的某一位来指定,该位不进行打乱操作即可.
工具类
34进制的定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 34进制的字符存储
private static final char [] chars = new char [ 34 ] ;
static {
int in = 0 ;
// 0 - 9
for ( int i = 48 ; i <= 57 ; i ++ ) {
chars [ in ] = ( char ) i ;
in ++ ;
}
// A - Z
for ( int i = 65 ; i <= 90 ; i ++ ) {
if ( i == 73 || i == 79 ) {
// I O
continue ;
}
chars [ in ] = ( char ) i ;
in ++ ;
}
}
将十进制的数转换为34进制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* 将十进制的数转换成34进制
*
* @param number 十进制数
* @param length 34进制数的总长度,
* 如果转换后的不足该长度则补0,
* 超出该长度则删除末尾超出的部分
* @return 34进制数
*/
private static String transRadix10To34 ( int number , int length ) {
int radix = 34 ;
StringBuilder sb = new StringBuilder ();
while ( number != 0 ) {
sb . append ( chars [ number % radix ] );
number = number / radix ;
}
if ( length > 0 ) {
while ( sb . length () != length ) {
if ( sb . length () < length ) {
sb . append ( chars [ 0 ] );
}
if ( sb . length () > length ) {
sb . deleteCharAt ( sb . length () - 1 );
}
}
}
return sb . reverse (). toString ();
}
将34进制数转换成十进制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* 将34进制数转换成十进制
*
* @param s 34进制数
* @return 十进制数
*/
private static int transRadix34To10 ( String s ) {
int radix = 34 ;
int sum = 0 ;
int m = 1 ;
int length = s . length ();
while ( length > 0 ) {
length -- ;
char c = s . charAt ( length );
int index = findCharIndex ( c );
sum += index * m ;
m *= radix ;
}
return sum ;
}
/**
* 根据字符查询在34进制中的索引位置
*/
private static int findCharIndex ( char c ) {
int index = - 1 ;
for ( int i = 0 ; i < chars . length ; i ++ ) {
if ( chars [ i ] == c ) {
index = i ;
break ;
}
}
return index ;
}
13位的兑换码的正则校验
1
2
3
4
/**
* 13位的兑换码的正则校验
*/
private static final Pattern CDKEY_PATTERN = Pattern . compile ( "^[0-9A-HJ-NP-Z]{13}$" );
Licensed under CC BY-NC-SA 4.0