根据 GB32100-2015 的算法校验企业统一社会信用代码

332 阅读2分钟

根据 GB32100-2015 的算法校验企业统一社会信用代码

需求:对用户输入的企业统一社会信用代码进行校验,必须输入正确的信用代码才能提交

参照标准,根据国标的算法进行校验

“企业统一社会信用代码”的介绍,参见c.gb688.cn/bzgk/gb/sho…

从官方文档中,可以得到第 18 位(校验码)的计算公式:

6.png

其中:

  • MOD(n, m),表示取模,对应 JS 中的“%”运算
  • i:表示在 18 位企业统一社会信用代码中的序号,PS:从 1 开始
  • Ci:表示第 i 位置上的代码字符的值,字符对应的值为:

8.png

  • C18:第 18 位的值,也就是校验码
  • Wi:第 i 位上的加权因子,Wi = Math.pow(3, i - 1) % 31PS:这里的 i 从 1 开始计数,以下是各位置序号上的加权因子: 7.png

例如,当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];
}