根据 GB32100-2015 的算法校验企业统一社会信用代码
需求:对用户输入的企业统一社会信用代码进行校验,必须输入正确的信用代码才能提交
参照标准,根据国标的算法进行校验
“企业统一社会信用代码”的介绍,参见c.gb688.cn/bzgk/gb/sho…
从官方文档中,可以得到第 18 位(校验码)的计算公式:
其中:
- MOD(n, m),表示取模,对应 JS 中的“%”运算
- i:表示在 18 位企业统一社会信用代码中的序号,PS:从 1 开始
- Ci:表示第 i 位置上的代码字符的值,字符对应的值为:
- C18:第 18 位的值,也就是校验码
- Wi:第 i 位上的加权因子,
Wi = Math.pow(3, i - 1) % 31
;PS:这里的 i 从 1 开始计数,以下是各位置序号上的加权因子:
例如,当C18 = 30
时,根据对应关系,正确的社会信用代码的第 18 位必定为 Y,当C18 = 31
时,正确的社会信用代码的第 18 位必定为 0;
JS/TS 实现
/**
* @description 根据GB 32100—2015法人和其他组织统一社会信用代码编码规则,校验该字符串是否为正确的统一社会信用代码
* @param {string} usci 统一社会信用代码
* @returns {boolean} boolean 验证结果,是否为符合规则的统一社会信用代码
*/
export function isValidUSCI(usci) {
if (typeof usci !== 'string') return false;
usci = usci.trim();
// 标准规定的要18位
if (usci.length !== 18) return false;
/** 代码-值对应,以此得出C_i */
const C_i_map = new Map();
C_i_map.set('0', 0);
C_i_map.set('1', 1);
C_i_map.set('2', 2);
C_i_map.set('3', 3);
C_i_map.set('4', 4);
C_i_map.set('5', 5);
C_i_map.set('6', 6);
C_i_map.set('7', 7);
C_i_map.set('8', 8);
C_i_map.set('9', 9);
C_i_map.set('A', 10);
C_i_map.set('B', 11);
C_i_map.set('C', 12);
C_i_map.set('D', 13);
C_i_map.set('E', 14);
C_i_map.set('F', 15);
C_i_map.set('G', 16);
C_i_map.set('H', 17);
C_i_map.set('J', 18);
C_i_map.set('K', 19);
C_i_map.set('L', 20);
C_i_map.set('M', 21);
C_i_map.set('N', 22);
C_i_map.set('P', 23);
C_i_map.set('Q', 24);
C_i_map.set('R', 25);
C_i_map.set('T', 26);
C_i_map.set('U', 27);
C_i_map.set('W', 28);
C_i_map.set('X', 29);
C_i_map.set('Y', 30);
/** 根据公式计算Ci * Wi的值的列表,总共17个值,以此来计算第18个值 */
const list = new Array(17).fill(0);
for (let i = 0; i < 17; i++) {
const C = C_i_map.get(usci[i]);
// 不在序列里的话,直接返回false
if (isNaN(C)) return false;
const W = Math.pow(3, i) % 31;
list[i] = C * W;
}
// 计算级数之和
const sum = list.reduce((prev, curr) => prev + curr);
// 求出校验码字符值
let C_18 = 31 - (sum % 31);
if (C_18 === 31) {
C_18 = 0;
}
// 根据校验码字符值,根据map,得到第18位的字符
let val_18 = null;
for (let [key, value] of C_i_map.entries()) {
if (value === C_18) {
val_18 = key;
}
}
return val_18 === usci[17];
}