idCardValidator.ts 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. /**
  2. * 身份证号验证工具
  3. *
  4. * 提供严格的18位身份证号校验:
  5. * - 格式校验
  6. * - 出生日期校验
  7. * - 校验码验证(ISO 7064:1983.MOD 11-2)
  8. */
  9. // 加权因子
  10. const WEIGHT_FACTORS = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
  11. // 校验码对应值
  12. const CHECK_CODES = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
  13. /**
  14. * 验证身份证号格式
  15. */
  16. function validateFormat(idCard: string): { valid: boolean; error: string } {
  17. // 必须是18位
  18. if (idCard.length !== 18) {
  19. return { valid: false, error: '身份证号必须为18位' };
  20. }
  21. // 前17位必须是数字,最后一位是数字或X
  22. if (!/^\d{17}[\dXx]$/.test(idCard)) {
  23. return { valid: false, error: '身份证号格式不正确' };
  24. }
  25. return { valid: true, error: '' };
  26. }
  27. /**
  28. * 验证出生日期
  29. */
  30. function validateBirthDate(idCard: string): { valid: boolean; error: string } {
  31. // 提取出生日期(第7-14位)
  32. const year = idCard.substring(6, 10);
  33. const month = idCard.substring(10, 12);
  34. const day = idCard.substring(12, 14);
  35. // 验证年份(1900-当前年份)
  36. const yearInt = parseInt(year, 10);
  37. const currentYear = new Date().getFullYear();
  38. if (yearInt < 1900 || yearInt > currentYear) {
  39. return { valid: false, error: `出生年份不合法(应在1900-${currentYear}之间)` };
  40. }
  41. // 验证月份(01-12)
  42. const monthInt = parseInt(month, 10);
  43. if (monthInt < 1 || monthInt > 12) {
  44. return { valid: false, error: '出生月份不合法(应在01-12之间)' };
  45. }
  46. // 验证日期
  47. const dayInt = parseInt(day, 10);
  48. const birthDate = new Date(yearInt, monthInt - 1, dayInt);
  49. // 检查日期是否有效
  50. if (
  51. birthDate.getFullYear() !== yearInt ||
  52. birthDate.getMonth() !== monthInt - 1 ||
  53. birthDate.getDate() !== dayInt
  54. ) {
  55. return { valid: false, error: '出生日期不合法' };
  56. }
  57. // 不能是未来日期
  58. if (birthDate > new Date()) {
  59. return { valid: false, error: '出生日期不能是未来日期' };
  60. }
  61. return { valid: true, error: '' };
  62. }
  63. /**
  64. * 验证校验码(ISO 7064:1983.MOD 11-2)
  65. */
  66. function validateCheckCode(idCard: string): { valid: boolean; error: string } {
  67. // 计算校验码
  68. let sum = 0;
  69. for (let i = 0; i < 17; i++) {
  70. sum += parseInt(idCard[i], 10) * WEIGHT_FACTORS[i];
  71. }
  72. // 取模得到校验码索引
  73. const checkIndex = sum % 11;
  74. const expectedCheckCode = CHECK_CODES[checkIndex];
  75. // 比较校验码(不区分大小写)
  76. const actualCheckCode = idCard[17].toUpperCase();
  77. if (actualCheckCode !== expectedCheckCode) {
  78. return { valid: false, error: '身份证号校验码错误' };
  79. }
  80. return { valid: true, error: '' };
  81. }
  82. /**
  83. * 完整验证身份证号
  84. */
  85. export function validateIDCard(idCard: string): { valid: boolean; error: string } {
  86. // 转大写并去除空格
  87. const cleanedIDCard = idCard.toUpperCase().trim();
  88. // 格式验证
  89. let result = validateFormat(cleanedIDCard);
  90. if (!result.valid) return result;
  91. // 出生日期验证
  92. result = validateBirthDate(cleanedIDCard);
  93. if (!result.valid) return result;
  94. // 校验码验证
  95. result = validateCheckCode(cleanedIDCard);
  96. if (!result.valid) return result;
  97. return { valid: true, error: '' };
  98. }
  99. /**
  100. * 验证真实姓名
  101. *
  102. * 规则:
  103. * - 纯汉字
  104. * - 无特殊符号、无空格
  105. * - 长度2-20位
  106. */
  107. export function validateRealName(name: string): { valid: boolean; error: string } {
  108. // 去除首尾空格
  109. const trimmedName = name.trim();
  110. // 长度验证(2-20位)
  111. if (trimmedName.length < 2 || trimmedName.length > 20) {
  112. return { valid: false, error: '姓名长度应为2-20个字符' };
  113. }
  114. // 纯汉字验证(不允许特殊符号、空格)
  115. if (!/^[\u4e00-\u9fa5]+$/.test(trimmedName)) {
  116. return { valid: false, error: '姓名只能包含汉字,不能有空格或特殊符号' };
  117. }
  118. return { valid: true, error: '' };
  119. }