validator/isSocialCreditCode.js

// 基础规则,由18位数字和大写字母组成,不使用I、O、Z、S、V。
const baseReg = /^[\dA-HJ-NPQRTUWXY]{2}\d{6}[\dA-HJ-NPQRTUWXY]{10}/;

// 基础字符组成
const baseCodeArr = '0123456789ABCDEFGHJKLMNPQRTUWXY'.split('');

//加权因子
const weightFactor = [1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10, 30, 28];

/**
 * 获取字符位置
 * 
 * @private
 * @param {String} code 字符
 * @returns {Number} 字符所在基础字符的位置
 */
function getBaseCodeIndex(code) {
  let ret;

  baseCodeArr.some((item, index) => {
    if (item === code) {
      ret = index;
      return true;
    }
    return false;
  });

  return ret;
}

/**
 * 计算校验码
 * 
 * @private
 * @since 1.1.0
 * @param {String} preCode 统一代码前17位
 * @returns {String} 校验码
 */
function sumCheckCode(preCode) {
  // const preCodeArr = preCode.split('');

  let total = 0;

  // 计算字符位置对应序号和加权因子的乘积,总和
  for (let i = 0; i < 17; i++) {
    // 字符位置对应的基础编码序号
    const index = getBaseCodeIndex(preCode[i]);
    // 加权因子
    const wf = weightFactor[i];
    // 计算序号和加权因子的乘积,并计算级数之和
    total += index * wf;
  }

  // 计算整数求余函数MOD
  const remainder = total % 31;
  // 校验码字符值序号
  const checkCodeIndex = 31 - remainder;

  return baseCodeArr[checkCodeIndex];
}

/**
 * 检测值是否为统一社会信用代码,也叫三证合一组织代码。由18位数字和大写字母组成,不使用I、O、Z、S、V。
 * 
 * @static
 * @alias module:Validator.isSocialCreditCode
 * @see {@link https://zh.wikisource.org/zh-hans/GB_32100-2015_法人和其他组织统一社会信用代码编码规则|GB 32100-2015 法人和其他组织统一社会信用代码编码规则}
 * @since 1.1.0
 * @param {String} value 要检测的值
 * @param {Object} [options] 配置项
 * @param {Boolean} [options.loose=false] 宽松模式。如果为true,不校验校验位。
 * @returns {Boolean} 值是否为统一社会信用代码
 * @example
 * 
 * isSocialCreditCode('91350100M000100Y4A3');
 * // => true
 *
 * isSocialCreditCode('91350100M000100Y4AB');
 * // => false
 *
 * // 宽松模式,不校验校验位。所以也可以通过
 * isSocialCreditCode('91350100M000100Y4AB', {loose: true});
 * // => true
 * 
 */
function isSocialCreditCode(value, {
  loose = false
} = {}) {
  const passBaseRule = baseReg.test(value);

  // 宽松模式 或 基础规则不通过直接返回
  if (loose || !passBaseRule) {
    return passBaseRule;
  }

  // 前17位
  const preCode = value.substr(0, 17);
  // 校验码
  const lastCode = value.substr(-1);
  // 计算校验码
  const checkCode = sumCheckCode(preCode);

  return lastCode === checkCode;
}

export default isSocialCreditCode;