起因
公司项目需要做几套编码,礼券码、接待码、兑换码等等,但是录入码的入口希望只有一个。这就需要编码上本身就要保存,可以用于识别功能的固定码。
原来一个12位随机码,现在要使用两位来标识功能。例如,11代表礼券12代表接待,那么编码上就会展现一定的规律。无论把标识码放在第几位使用几次都是比较容易识别的。
在网上找了一下也没有发现合适的方法来解决此问题,加密算法一般都是带字母的,我们必须要纯数字的。
还有就是可以使用密言来解决,增加另一个12位随机数作为密言,然后两个12位数进行异或操作可以解决此问题。但是因为一些遗留问题我们并不希望在数据库中增加额外的密言字段。
解决
最后我想到了一个办法,既不用密言,又可以让原来有规律的编码看上去规律没那么明显。
此方法不是加密算法所以肯定不能达到加密的效果,我暂且叫做把数字串打乱的方法。
其实道理很简单,把十进制数字转换成二进制数字,然后在二进制数固定的位数上增加0或1。还原的时候去掉固定位值上的数字即可。根据个人需要插入在第几位,插入几个值都是可以自由调整的。
这里需要注意无论是十进制数字还是二进制数字,都要想办法避免第一位是0,否则转换准确率将受到挑战。我的办法是将标识码第一位放在编码首位,并且不允许首位这个码为0。
我自己的代码做了几次一百万次的试验,还没有发现转换误差。
代码
package com.plsoft.pmall.utils;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 生成二维码使用的编码
* 如:消费者名片 - 10、活动礼券码 - 11、接待码 - 12、商品兑换码 - 13
*/
public class QRcodeNumber {
/**
* 传入功能标识获得编码
* @param flag 标识 必须是两位数 并且第一位不能是0
* @return 入参是null返回null 位数不对返回null 不是数字返回null 第一位是0返回null
*/
public String getCode(String flag){
//入参是null返回null
if(flag == null || flag.equals("")){
return null;
}
//位数不对返回null
if(flag.length() != 2){
return null;
}
//不是数字返回null
Pattern pattern = Pattern.compile("[0-9]*");
Matcher isNum = pattern.matcher(flag);
if( !isNum.matches() ){
return null;
}
//第一位是0返回null
if(flag.substring(0,1).equals(0)){
return null;
}
//编码规则:标识第一位 + 10位随机数 + 标识第二位
String random_number = String.format("%010d", Math.abs(new Random().nextLong()) % 10000000000L);//10位随机数;
String code = flag.substring(0,1) + random_number +flag.substring(1);
return code;
}
/**
* 获取功能标识码
* @param code 编码
* @return 位数不对返回null 不是数字返回null
*/
public String getFlag(String code){
//入参是null返回null
if(code == null || code.equals("")){
return null;
}
//位数不对返回null
if(code.length() != 12){
return null;
}
//不是数字返回null
Pattern pattern = Pattern.compile("[0-9]*");
Matcher isNum = pattern.matcher(code);
if( !isNum.matches() ){
return null;
}
//编码的第一位和最后一位
String flag = code.substring(0,1) + code.substring(code.length()-1);
return flag;
}
/**
* 打乱编码
* @param code 正常编码
* @return
*/
public String codeDisorganize(String code){
//入参是null返回null
if(code == null || code.equals("")){
return null;
}
//位数不对返回null
if(code.length() != 12){
return null;
}
//不是数字返回null
Pattern pattern = Pattern.compile("[0-9]*");
Matcher isNum = pattern.matcher(code);
if( !isNum.matches() ){
return null;
}
//转换成二进制并在第10位插入0,打乱编码
String two = Long.toBinaryString(Long.valueOf(code));
two = two.substring(0,10) + "0" + two.substring(10);
String disorganizeCode = Long.valueOf(two,2).toString();
return disorganizeCode;
}
/**
* 恢复编码
* @param disorganizeCode 打乱的编码
* @return
*/
public String codeRestore(String disorganizeCode){
//入参是null返回null
if(disorganizeCode == null || disorganizeCode.equals("")){
return null;
}
//不是数字返回null
Pattern pattern = Pattern.compile("[0-9]*");
Matcher isNum = pattern.matcher(disorganizeCode);
if( !isNum.matches() ){
return null;
}
//转换成二进制去掉第十位数字
String two = Long.toBinaryString(Long.valueOf(disorganizeCode));
two = two.substring(0,10) + two.substring(10+1);
String code = Long.valueOf(two,2).toString();
return code;
}
/**
* 生成并打乱编码
* @param flag 标识
* @return
*/
public String getCodeDisorganize(String flag){
String code = getCode(flag);
String disorganizeCode = codeDisorganize(code);
return disorganizeCode;
}
/**
* 测试方法
* @param args
*/
public static void main(String[] args) {
QRcodeNumber test = new QRcodeNumber();
for(int l=0;l<1000000;l++){
String code = test.getCode("12");
String disorganizeCode = test.codeDisorganize(code);
String code1 = test.codeRestore(disorganizeCode);
String flag = test.getFlag(code1);
System.out.print("编码:"+code+" 打乱编码:"+disorganizeCode+" 恢复编码:"+code1+" 功能标识:"+flag+" 是否准确还原:"+code.equals(code1));
System.out.println("");
}
}
}
结果
只是一百万次的一部分
编码:190938747752 打乱编码:381796356968 恢复编码:190938747752 功能标识:12 是否准确还原:true
编码:198317140492 打乱编码:396422507020 恢复编码:198317140492 功能标识:12 是否准确还原:true
编码:192320558332 打乱编码:384520344828 恢复编码:192320558332 功能标识:12 是否准确还原:true
编码:125924331642 打乱编码:251820560506 恢复编码:125924331642 功能标识:12 是否准确还原:true
编码:189146322322 打乱编码:378124883346 恢复编码:189146322322 功能标识:12 是否准确还原:true
编码:131905761882 打乱编码:263707570778 恢复编码:131905761882 功能标识:12 是否准确还原:true
编码:121553498692 打乱编码:243020542532 恢复编码:121553498692 功能标识:12 是否准确还原:true
编码:106515401172 打乱编码:212950059476 恢复编码:106515401172 功能标识:12 是否准确还原:true
编码:188032064632 打乱编码:375936883832 恢复编码:188032064632 功能标识:12 是否准确还原:true
编码:126401147142 打乱编码:252700029190 恢复编码:126401147142 功能标识:12 是否准确还原:true
编码:198738210362 打乱编码:397380447802 恢复编码:198738210362 功能标识:12 是否准确还原:true
编码:140145652832 打乱编码:280268960864 恢复编码:140145652832 功能标识:12 是否准确还原:true
编码:176124884942 打乱编码:352218544078 恢复编码:176124884942 功能标识:12 是否准确还原:true
编码:133437939942 打乱编码:266850361574 恢复编码:133437939942 功能标识:12 是否准确还原:true
编码:182091456312 打乱编码:364090695480 恢复编码:182091456312 功能标识:12 是否准确还原:true
编码:150122544032 打乱编码:300177963936 恢复编码:150122544032 功能标识:12 是否准确还原:true
编码:122366359692 打乱编码:244638709900 恢复编码:122366359692 功能标识:12 是否准确还原:true
编码:132578551622 打乱编码:265051449158 恢复编码:132578551622 功能标识:12 是否准确还原:true
编码:125089602772 打乱编码:250046307540 恢复编码:125089602772 功能标识:12 是否准确还原:true
编码:157058911002 打乱编码:314093652762 恢复编码:157058911002 功能标识:12 是否准确还原:true
编码:121568404182 打乱编码:243035448022 恢复编码:121568404182 功能标识:12 是否准确还原:true
编码:169999958902 打乱编码:339919602550 恢复编码:169999958902 功能标识:12 是否准确还原:true
编码:138663454862 打乱编码:277176150158 恢复编码:138663454862 功能标识:12 是否准确还原:true
编码:115203985932 打乱编码:230362796556 恢复编码:115203985932 功能标识:12 是否准确还原:true