|
|
@@ -0,0 +1,206 @@
|
|
|
+package com.yc.ship.module.trade.utils;
|
|
|
+
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.Collections;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.HashSet;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Set;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 证件号码解析工具类 - 根据证件号前2位省份码解析省份名称
|
|
|
+ * <p>
|
|
|
+ * 中国大陆身份证号码前6位为行政区划码,其中前2位代表省份。
|
|
|
+ * 港澳台居民居住证同样采用18位编码格式,前2位为大陆居住地省份码。
|
|
|
+ * 护照、通行证、回乡证、台胞证、户口本等证件号码格式不同,无法解析省份。
|
|
|
+ * </p>
|
|
|
+ * <p>
|
|
|
+ * 支持解析的证件类型:
|
|
|
+ * 1 - 身份证(18位/15位)
|
|
|
+ * 11 - 港澳台居民居住证(18位,编码规则同身份证)
|
|
|
+ * </p>
|
|
|
+ * <p>
|
|
|
+ * 不支持解析的证件类型(返回空字符串):
|
|
|
+ * 3 - 港澳台通行证
|
|
|
+ * 4 - 护照
|
|
|
+ * 5 - 港澳通行证
|
|
|
+ * 6 - 台湾通行证
|
|
|
+ * 7 - 回乡证(港澳居民来往内地通行证)
|
|
|
+ * 8 - 台胞证(台湾居民来往大陆通行证)
|
|
|
+ * 10 - 户口本
|
|
|
+ * </p>
|
|
|
+ *
|
|
|
+ * @Author: AI
|
|
|
+ * @Date: 2026/05/06
|
|
|
+ */
|
|
|
+public class IdCardProvinceUtil {
|
|
|
+
|
|
|
+ // ==================== 证件类型常量 ====================
|
|
|
+ /** 身份证 */
|
|
|
+ public static final int CREDENTIAL_TYPE_ID_CARD = 0;
|
|
|
+ /** 港澳台通行证 */
|
|
|
+ public static final int CREDENTIAL_TYPE_HK_MACAO_TAIWAN_PASS = 3;
|
|
|
+ /** 护照 */
|
|
|
+ public static final int CREDENTIAL_TYPE_PASSPORT = 1;
|
|
|
+ /** 港澳通行证 */
|
|
|
+ public static final int CREDENTIAL_TYPE_HK_MACAO_PASS = 5;
|
|
|
+ /** 台湾通行证 */
|
|
|
+ public static final int CREDENTIAL_TYPE_TAIWAN_PASS = 6;
|
|
|
+ /** 回乡证(港澳居民来往内地通行证) */
|
|
|
+ public static final int CREDENTIAL_TYPE_HOME_RETURN_PERMIT = 7;
|
|
|
+ /** 台胞证(台湾居民来往大陆通行证) */
|
|
|
+ public static final int CREDENTIAL_TYPE_TAIWAN_COMPATRIOT_PERMIT = 8;
|
|
|
+ /** 户口本 */
|
|
|
+ public static final int CREDENTIAL_TYPE_HOUSEHOLD_REGISTER = 10;
|
|
|
+ /** 港澳台居民居住证 */
|
|
|
+ public static final int CREDENTIAL_TYPE_HK_MACAO_TAIWAN_RESIDENCE_PERMIT = 11;
|
|
|
+
|
|
|
+ // ==================== 校验常量 ====================
|
|
|
+ /** 身份证号码长度(18位) */
|
|
|
+ private static final int ID_CARD_LENGTH_18 = 18;
|
|
|
+ /** 旧身份证号码长度(15位) */
|
|
|
+ private static final int ID_CARD_LENGTH_15 = 15;
|
|
|
+ /** 省份码长度(前2位) */
|
|
|
+ private static final int PROVINCE_CODE_LENGTH = 2;
|
|
|
+ /** 18位身份证号末位校验位可能为X */
|
|
|
+ private static final char ID_CARD_X = 'X';
|
|
|
+ private static final char ID_CARD_X_LOWER = 'x';
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 可以解析省份的证件类型集合(号码格式与身份证相同,采用18位行政区划编码)
|
|
|
+ * 身份证(1) 和 港澳台居民居住证(11) 均采用GB 11643编码规则
|
|
|
+ */
|
|
|
+ private static final Set<Integer> PARSEABLE_CREDENTIAL_TYPES = Collections.unmodifiableSet(
|
|
|
+ new HashSet<>(Arrays.asList(CREDENTIAL_TYPE_ID_CARD, CREDENTIAL_TYPE_HK_MACAO_TAIWAN_RESIDENCE_PERMIT))
|
|
|
+ );
|
|
|
+
|
|
|
+ /** 省份码 -> 省份名称 映射表(不可变,基于GB/T 2260行政区划代码前2位) */
|
|
|
+ private static final Map<String, String> PROVINCE_MAP;
|
|
|
+
|
|
|
+ static {
|
|
|
+ Map<String, String> map = new HashMap<>(40);
|
|
|
+ map.put("11", "北京");
|
|
|
+ map.put("12", "天津");
|
|
|
+ map.put("13", "河北");
|
|
|
+ map.put("14", "山西");
|
|
|
+ map.put("15", "内蒙古");
|
|
|
+ map.put("21", "辽宁");
|
|
|
+ map.put("22", "吉林");
|
|
|
+ map.put("23", "黑龙江");
|
|
|
+ map.put("31", "上海");
|
|
|
+ map.put("32", "江苏");
|
|
|
+ map.put("33", "浙江");
|
|
|
+ map.put("34", "安徽");
|
|
|
+ map.put("35", "福建");
|
|
|
+ map.put("36", "江西");
|
|
|
+ map.put("37", "山东");
|
|
|
+ map.put("41", "河南");
|
|
|
+ map.put("42", "湖北");
|
|
|
+ map.put("43", "湖南");
|
|
|
+ map.put("44", "广东");
|
|
|
+ map.put("45", "广西");
|
|
|
+ map.put("46", "海南");
|
|
|
+ map.put("50", "重庆");
|
|
|
+ map.put("51", "四川");
|
|
|
+ map.put("52", "贵州");
|
|
|
+ map.put("53", "云南");
|
|
|
+ map.put("54", "西藏");
|
|
|
+ map.put("61", "陕西");
|
|
|
+ map.put("62", "甘肃");
|
|
|
+ map.put("63", "青海");
|
|
|
+ map.put("64", "宁夏");
|
|
|
+ map.put("65", "新疆");
|
|
|
+ map.put("71", "台湾");
|
|
|
+ map.put("81", "香港");
|
|
|
+ map.put("82", "澳门");
|
|
|
+ // 91开头的为国外身份证件,无对应省份
|
|
|
+ PROVINCE_MAP = Collections.unmodifiableMap(map);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据证件类型和证件号解析省份名称
|
|
|
+ * <p>
|
|
|
+ * 身份证(1)和港澳台居民居住证(11):通过号码前2位省份码解析;
|
|
|
+ * 台湾通行证(6)和台胞证(8):直接返回"台湾";
|
|
|
+ * 其他证件类型(护照、港澳通行证等):返回空字符串。
|
|
|
+ * </p>
|
|
|
+ *
|
|
|
+ * @param credentialType 证件类型
|
|
|
+ * @param credentialNo 证件号码
|
|
|
+ * @return 省份名称,无法解析时返回空字符串
|
|
|
+ */
|
|
|
+ public static String getProvinceName(Integer credentialType, String credentialNo, String nationalityName) {
|
|
|
+ if (credentialType == null) {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ // 台湾通行证、台胞证 → 直接返回"台湾"
|
|
|
+ if (credentialType == CREDENTIAL_TYPE_TAIWAN_PASS
|
|
|
+ || credentialType == CREDENTIAL_TYPE_TAIWAN_COMPATRIOT_PERMIT) {
|
|
|
+ return "台湾";
|
|
|
+ }
|
|
|
+
|
|
|
+ if (nationalityName.contains("香港")) {
|
|
|
+ return "香港";
|
|
|
+ }
|
|
|
+
|
|
|
+ if (nationalityName.contains("澳门")) {
|
|
|
+ return "澳门";
|
|
|
+ }
|
|
|
+ // 非可解析的证件类型,直接返回空
|
|
|
+ if (!PARSEABLE_CREDENTIAL_TYPES.contains(credentialType)) {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ // 证件号为空或长度不足,无法解析
|
|
|
+ if (credentialNo == null || credentialNo.trim().length() < PROVINCE_CODE_LENGTH) {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ String trimmedNo = credentialNo.trim();
|
|
|
+ // 校验号码格式
|
|
|
+ if (!isValidIdCardFormat(trimmedNo)) {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ // 提取省份码(前2位)并查表
|
|
|
+ String provinceCode = trimmedNo.substring(0, PROVINCE_CODE_LENGTH);
|
|
|
+ return PROVINCE_MAP.getOrDefault(provinceCode, "");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 校验号码是否符合身份证编码格式
|
|
|
+ * <p>
|
|
|
+ * 18位:前17位必须为数字,第18位可为数字或X/x
|
|
|
+ * 15位:全部必须为数字
|
|
|
+ * </p>
|
|
|
+ *
|
|
|
+ * @param idCardNo 证件号码(已trim)
|
|
|
+ * @return 格式是否合法
|
|
|
+ */
|
|
|
+ private static boolean isValidIdCardFormat(String idCardNo) {
|
|
|
+ int len = idCardNo.length();
|
|
|
+ if (len == ID_CARD_LENGTH_18) {
|
|
|
+ // 前17位必须为数字
|
|
|
+ String first17 = idCardNo.substring(0, 17);
|
|
|
+ if (!isNumeric(first17)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // 第18位可为数字或X/x
|
|
|
+ char lastChar = idCardNo.charAt(17);
|
|
|
+ return Character.isDigit(lastChar) || lastChar == ID_CARD_X || lastChar == ID_CARD_X_LOWER;
|
|
|
+ } else if (len == ID_CARD_LENGTH_15) {
|
|
|
+ // 15位旧身份证:全部为数字
|
|
|
+ return isNumeric(idCardNo);
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断字符串是否全部由数字组成
|
|
|
+ */
|
|
|
+ private static boolean isNumeric(String str) {
|
|
|
+ for (int i = 0, len = str.length(); i < len; i++) {
|
|
|
+ if (!Character.isDigit(str.charAt(i))) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+}
|