diff --git a/yanzhu-common/yanzhu-common-core/src/main/java/com/yanzhu/common/core/utils/IdcardUtils.java b/yanzhu-common/yanzhu-common-core/src/main/java/com/yanzhu/common/core/utils/IdcardUtils.java new file mode 100644 index 00000000..60d85138 --- /dev/null +++ b/yanzhu-common/yanzhu-common-core/src/main/java/com/yanzhu/common/core/utils/IdcardUtils.java @@ -0,0 +1,635 @@ +package com.yanzhu.common.core.utils; + +/** + * Copyright (C) 2009-2010 Yichuan, Fuchun All rights reserved. + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * @(#) IdcardUtils.java Date: 2010-06-17 + */ + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 身份证工具类 + * + * @author June + * @version 1.0, 2010-06-17 + */ +public class IdcardUtils extends StringUtils { + + /** 中国公民身份证号码最小长度。 */ + public static final int CHINA_ID_MIN_LENGTH = 15; + + /** 中国公民身份证号码最大长度。 */ + public static final int CHINA_ID_MAX_LENGTH = 18; + + /** 省、直辖市代码表 */ + public static final String cityCode[] = { + "11", "12", "13", "14", "15", "21", "22", "23", "31", "32", "33", "34", "35", "36", "37", "41", + "42", "43", "44", "45", "46", "50", "51", "52", "53", "54", "61", "62", "63", "64", "65", "71", + "81", "82", "91" + }; + + /** 每位加权因子 */ + public static final int power[] = { + 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 + }; + + /** 第18位校检码 */ + public static final String verifyCode[] = { + "1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2" + }; + /** 最低年限 */ + public static final int MIN = 1930; + public static Map cityCodes = new HashMap(); + /** 台湾身份首字母对应数字 */ + public static Map twFirstCode = new HashMap(); + /** 香港身份首字母对应数字 */ + public static Map hkFirstCode = new HashMap(); + static { + cityCodes.put("11", "北京市"); + cityCodes.put("12", "天津市"); + cityCodes.put("13", "河北省"); + cityCodes.put("14", "山西省"); + cityCodes.put("15", "内蒙古省"); + cityCodes.put("21", "辽宁省"); + cityCodes.put("22", "吉林省"); + cityCodes.put("23", "黑龙江省"); + cityCodes.put("31", "上海市"); + cityCodes.put("32", "江苏省"); + cityCodes.put("33", "浙江省"); + cityCodes.put("34", "安徽省"); + cityCodes.put("35", "福建省"); + cityCodes.put("36", "江西省"); + cityCodes.put("37", "山东省"); + cityCodes.put("41", "河南省"); + cityCodes.put("42", "湖北省"); + cityCodes.put("43", "湖南省"); + cityCodes.put("44", "广东省"); + cityCodes.put("45", "广西省"); + cityCodes.put("46", "海南省"); + cityCodes.put("50", "重庆市"); + cityCodes.put("51", "四川省"); + cityCodes.put("52", "贵州省"); + cityCodes.put("53", "云南省"); + cityCodes.put("54", "西藏省"); + cityCodes.put("61", "陕西省"); + cityCodes.put("62", "甘肃省"); + cityCodes.put("63", "青海省"); + cityCodes.put("64", "宁夏省"); + cityCodes.put("65", "新疆省"); + cityCodes.put("71", "台湾自治区"); + cityCodes.put("81", "香港自治区"); + cityCodes.put("82", "澳门自治区"); + cityCodes.put("91", "国外居住证"); + twFirstCode.put("A", 10); + twFirstCode.put("B", 11); + twFirstCode.put("C", 12); + twFirstCode.put("D", 13); + twFirstCode.put("E", 14); + twFirstCode.put("F", 15); + twFirstCode.put("G", 16); + twFirstCode.put("H", 17); + twFirstCode.put("J", 18); + twFirstCode.put("K", 19); + twFirstCode.put("L", 20); + twFirstCode.put("M", 21); + twFirstCode.put("N", 22); + twFirstCode.put("P", 23); + twFirstCode.put("Q", 24); + twFirstCode.put("R", 25); + twFirstCode.put("S", 26); + twFirstCode.put("T", 27); + twFirstCode.put("U", 28); + twFirstCode.put("V", 29); + twFirstCode.put("X", 30); + twFirstCode.put("Y", 31); + twFirstCode.put("W", 32); + twFirstCode.put("Z", 33); + twFirstCode.put("I", 34); + twFirstCode.put("O", 35); + hkFirstCode.put("A", 1); + hkFirstCode.put("B", 2); + hkFirstCode.put("C", 3); + hkFirstCode.put("R", 18); + hkFirstCode.put("U", 21); + hkFirstCode.put("Z", 26); + hkFirstCode.put("X", 24); + hkFirstCode.put("W", 23); + hkFirstCode.put("O", 15); + hkFirstCode.put("N", 14); + } + + /** + * 将15位身份证号码转换为18位 + * + * @param idCard + * 15位身份编码 + * @return 18位身份编码 + */ + public static String conver15CardTo18(String idCard) { + String idCard18 = ""; + if (idCard.length() != CHINA_ID_MIN_LENGTH) { + return null; + } + if (isNum(idCard)) { + // 获取出生年月日 + String birthday = idCard.substring(6, 12); + Date birthDate = null; + try { + birthDate = new SimpleDateFormat("yyMMdd").parse(birthday); + } catch (ParseException e) { + e.printStackTrace(); + } + Calendar cal = Calendar.getInstance(); + if (birthDate != null) + cal.setTime(birthDate); + // 获取出生年(完全表现形式,如:2010) + String sYear = String.valueOf(cal.get(Calendar.YEAR)); + idCard18 = idCard.substring(0, 6) + sYear + idCard.substring(8); + // 转换字符数组 + char[] cArr = idCard18.toCharArray(); + if (cArr != null) { + int[] iCard = converCharToInt(cArr); + int iSum17 = getPowerSum(iCard); + // 获取校验位 + String sVal = getCheckCode18(iSum17); + if (sVal.length() > 0) { + idCard18 += sVal; + } else { + return null; + } + } + } else { + return null; + } + return idCard18; + } + + /** + * 验证身份证是否合法 + */ + public static boolean validateCard(String idCard) { + String card = idCard.trim(); + if (validateIdCard18(card)) { + return true; + } + if (validateIdCard15(card)) { + return true; + } + String[] cardval = validateIdCard10(card); + if (cardval != null) { + if (cardval[2].equals("true")) { + return true; + } + } + return false; + } + + /** + * 验证18位身份编码是否合法 + * + * @param idCard 身份编码 + * @return 是否合法 + */ + public static boolean validateIdCard18(String idCard) { + boolean bTrue = false; + if (idCard.length() == CHINA_ID_MAX_LENGTH) { + // 前17位 + String code17 = idCard.substring(0, 17); + // 第18位 + String code18 = idCard.substring(17, CHINA_ID_MAX_LENGTH); + if (isNum(code17)) { + char[] cArr = code17.toCharArray(); + if (cArr != null) { + int[] iCard = converCharToInt(cArr); + int iSum17 = getPowerSum(iCard); + // 获取校验位 + String val = getCheckCode18(iSum17); + if (val.length() > 0) { + if (val.equalsIgnoreCase(code18)) { + bTrue = true; + } + } + } + } + } + return bTrue; + } + + /** + * 验证15位身份编码是否合法 + * + * @param idCard + * 身份编码 + * @return 是否合法 + */ + public static boolean validateIdCard15(String idCard) { + if (idCard.length() != CHINA_ID_MIN_LENGTH) { + return false; + } + if (isNum(idCard)) { + String proCode = idCard.substring(0, 2); + if (cityCodes.get(proCode) == null) { + return false; + } + String birthCode = idCard.substring(6, 12); + Date birthDate = null; + try { + birthDate = new SimpleDateFormat("yy").parse(birthCode.substring(0, 2)); + } catch (ParseException e) { + e.printStackTrace(); + } + Calendar cal = Calendar.getInstance(); + if (birthDate != null) + cal.setTime(birthDate); + if (!valiDate(cal.get(Calendar.YEAR), Integer.valueOf(birthCode.substring(2, 4)), + Integer.valueOf(birthCode.substring(4, 6)))) { + return false; + } + } else { + return false; + } + return true; + } + + /** + * 验证10位身份编码是否合法 + * + * @param idCard 身份编码 + * @return 身份证信息数组 + *

+ * [0] - 台湾、澳门、香港 [1] - 性别(男M,女F,未知N) [2] - 是否合法(合法true,不合法false) + * 若不是身份证件号码则返回null + *

+ */ + public static String[] validateIdCard10(String idCard) { + String[] info = new String[3]; + String card = idCard.replaceAll("[\\(|\\)]", ""); + if (card.length() != 8 && card.length() != 9 && idCard.length() != 10) { + return null; + } + if (idCard.matches("^[a-zA-Z][0-9]{9}$")) { // 台湾 + info[0] = "台湾"; + System.out.println("11111"); + String char2 = idCard.substring(1, 2); + if (char2.equals("1")) { + info[1] = "M"; + System.out.println("MMMMMMM"); + } else if (char2.equals("2")) { + info[1] = "F"; + System.out.println("FFFFFFF"); + } else { + info[1] = "N"; + info[2] = "false"; + System.out.println("NNNN"); + return info; + } + info[2] = validateTWCard(idCard) ? "true" : "false"; + } else if (idCard.matches("^[1|5|7][0-9]{6}\\(?[0-9A-Z]\\)?$")) { // 澳门 + info[0] = "澳门"; + info[1] = "N"; + // TODO + } else if (idCard.matches("^[A-Z]{1,2}[0-9]{6}\\(?[0-9A]\\)?$")) { // 香港 + info[0] = "香港"; + info[1] = "N"; + info[2] = validateHKCard(idCard) ? "true" : "false"; + } else { + return null; + } + return info; + } + + /** + * 验证台湾身份证号码 + * + * @param idCard + * 身份证号码 + * @return 验证码是否符合 + */ + public static boolean validateTWCard(String idCard) { + String start = idCard.substring(0, 1); + String mid = idCard.substring(1, 9); + String end = idCard.substring(9, 10); + Integer iStart = twFirstCode.get(start); + Integer sum = iStart / 10 + (iStart % 10) * 9; + char[] chars = mid.toCharArray(); + Integer iflag = 8; + for (char c : chars) { + sum = sum + Integer.valueOf(c + "") * iflag; + iflag--; + } + return (sum % 10 == 0 ? 0 : (10 - sum % 10)) == Integer.valueOf(end) ? true : false; + } + + /** + * 验证香港身份证号码(存在Bug,部份特殊身份证无法检查) + *

+ * 身份证前2位为英文字符,如果只出现一个英文字符则表示第一位是空格,对应数字58 前2位英文字符A-Z分别对应数字10-35 + * 最后一位校验码为0-9的数字加上字符"A","A"代表10 + *

+ *

+ * 将身份证号码全部转换为数字,分别对应乘9-1相加的总和,整除11则证件号码有效 + *

+ * + * @param idCard 身份证号码 + * @return 验证码是否符合 + */ + public static boolean validateHKCard(String idCard) { + String card = idCard.replaceAll("[\\(|\\)]", ""); + Integer sum = 0; + if (card.length() == 9) { + sum = (Integer.valueOf(card.substring(0, 1).toUpperCase().toCharArray()[0]) - 55) * 9 + + (Integer.valueOf(card.substring(1, 2).toUpperCase().toCharArray()[0]) - 55) * 8; + card = card.substring(1, 9); + } else { + sum = 522 + (Integer.valueOf(card.substring(0, 1).toUpperCase().toCharArray()[0]) - 55) * 8; + } + String mid = card.substring(1, 7); + String end = card.substring(7, 8); + char[] chars = mid.toCharArray(); + Integer iflag = 7; + for (char c : chars) { + sum = sum + Integer.valueOf(c + "") * iflag; + iflag--; + } + if (end.toUpperCase().equals("A")) { + sum = sum + 10; + } else { + sum = sum + Integer.valueOf(end); + } + return (sum % 11 == 0) ? true : false; + } + + /** + * 将字符数组转换成数字数组 + * + * @param ca + * 字符数组 + * @return 数字数组 + */ + public static int[] converCharToInt(char[] ca) { + int len = ca.length; + int[] iArr = new int[len]; + try { + for (int i = 0; i < len; i++) { + iArr[i] = Integer.parseInt(String.valueOf(ca[i])); + } + } catch (NumberFormatException e) { + e.printStackTrace(); + } + return iArr; + } + + /** + * 将身份证的每位和对应位的加权因子相乘之后,再得到和值 + * + * @param iArr + * @return 身份证编码。 + */ + public static int getPowerSum(int[] iArr) { + int iSum = 0; + if (power.length == iArr.length) { + for (int i = 0; i < iArr.length; i++) { + for (int j = 0; j < power.length; j++) { + if (i == j) { + iSum = iSum + iArr[i] * power[j]; + } + } + } + } + return iSum; + } + + /** + * 将power和值与11取模获得余数进行校验码判断 + * + * @param iSum + * @return 校验位 + */ + public static String getCheckCode18(int iSum) { + String sCode = ""; + switch (iSum % 11) { + case 10: + sCode = "2"; + break; + case 9: + sCode = "3"; + break; + case 8: + sCode = "4"; + break; + case 7: + sCode = "5"; + break; + case 6: + sCode = "6"; + break; + case 5: + sCode = "7"; + break; + case 4: + sCode = "8"; + break; + case 3: + sCode = "9"; + break; + case 2: + sCode = "x"; + break; + case 1: + sCode = "0"; + break; + case 0: + sCode = "1"; + break; + } + return sCode; + } + + /** + * 根据身份编号获取年龄 + * + * @param idCard + * 身份编号 + * @return 年龄 + */ + public static int getAgeByIdCard(String idCard) { + int iAge = 0; + if (idCard.length() == CHINA_ID_MIN_LENGTH) { + idCard = conver15CardTo18(idCard); + } + String year = idCard.substring(6, 10); + Calendar cal = Calendar.getInstance(); + int iCurrYear = cal.get(Calendar.YEAR); + iAge = iCurrYear - Integer.valueOf(year); + return iAge; + } + + /** + * 根据身份编号获取生日 + * + * @param idCard 身份编号 + * @return 生日(yyyyMMdd) + */ + public static String getBirthByIdCard(String idCard) { + Integer len = idCard.length(); + if (len < CHINA_ID_MIN_LENGTH) { + return null; + } else if (len == CHINA_ID_MIN_LENGTH) { + idCard = conver15CardTo18(idCard); + } + return idCard.substring(6, 14); + } + + /** + * 根据身份编号获取生日年 + * + * @param idCard 身份编号 + * @return 生日(yyyy) + */ + public static Short getYearByIdCard(String idCard) { + Integer len = idCard.length(); + if (len < CHINA_ID_MIN_LENGTH) { + return null; + } else if (len == CHINA_ID_MIN_LENGTH) { + idCard = conver15CardTo18(idCard); + } + return Short.valueOf(idCard.substring(6, 10)); + } + + /** + * 根据身份编号获取生日月 + * + * @param idCard + * 身份编号 + * @return 生日(MM) + */ + public static Short getMonthByIdCard(String idCard) { + Integer len = idCard.length(); + if (len < CHINA_ID_MIN_LENGTH) { + return null; + } else if (len == CHINA_ID_MIN_LENGTH) { + idCard = conver15CardTo18(idCard); + } + return Short.valueOf(idCard.substring(10, 12)); + } + + /** + * 根据身份编号获取生日天 + * + * @param idCard + * 身份编号 + * @return 生日(dd) + */ + public static Short getDateByIdCard(String idCard) { + Integer len = idCard.length(); + if (len < CHINA_ID_MIN_LENGTH) { + return null; + } else if (len == CHINA_ID_MIN_LENGTH) { + idCard = conver15CardTo18(idCard); + } + return Short.valueOf(idCard.substring(12, 14)); + } + + /** + * 根据身份编号获取性别 + * + * @param idCard 身份编号 + * @return 性别(M-男,F-女,N-未知) + */ + public static String getGenderByIdCard(String idCard) { + String sGender = "N"; + if (idCard.length() == CHINA_ID_MIN_LENGTH) { + idCard = conver15CardTo18(idCard); + } + String sCardNum = idCard.substring(16, 17); + if (Integer.parseInt(sCardNum) % 2 != 0) { + sGender = "M"; + } else { + sGender = "F"; + } + return sGender; + } + + /** + * 根据身份编号获取户籍省份 + * + * @param idCard 身份编码 + * @return 省级编码。 + */ + public static String getProvinceByIdCard(String idCard) { + int len = idCard.length(); + String sProvince = null; + String sProvinNum = ""; + if (len == CHINA_ID_MIN_LENGTH || len == CHINA_ID_MAX_LENGTH) { + sProvinNum = idCard.substring(0, 2); + } + sProvince = cityCodes.get(sProvinNum); + return sProvince; + } + + /** + * 数字验证 + * + * @param val + * @return 提取的数字。 + */ + public static boolean isNum(String val) { + return val == null || "".equals(val) ? false : val.matches("^[0-9]*$"); + } + + /** + * 验证小于当前日期 是否有效 + * + * @param iYear + * 待验证日期(年) + * @param iMonth + * 待验证日期(月 1-12) + * @param iDate + * 待验证日期(日) + * @return 是否有效 + */ + public static boolean valiDate(int iYear, int iMonth, int iDate) { + Calendar cal = Calendar.getInstance(); + int year = cal.get(Calendar.YEAR); + int datePerMonth; + if (iYear < MIN || iYear >= year) { + return false; + } + if (iMonth < 1 || iMonth > 12) { + return false; + } + switch (iMonth) { + case 4: + case 6: + case 9: + case 11: + datePerMonth = 30; + break; + case 2: + boolean dm = ((iYear % 4 == 0 && iYear % 100 != 0) || (iYear % 400 == 0)) + && (iYear > MIN && iYear < year); + datePerMonth = dm ? 29 : 28; + break; + default: + datePerMonth = 31; + } + return (iDate >= 1) && (iDate <= datePerMonth); + } +} diff --git a/yanzhu-common/yanzhu-common-core/src/main/java/com/yanzhu/common/core/utils/OcrService.java b/yanzhu-common/yanzhu-common-core/src/main/java/com/yanzhu/common/core/utils/OcrService.java index 4fc3a16d..c89a757a 100644 --- a/yanzhu-common/yanzhu-common-core/src/main/java/com/yanzhu/common/core/utils/OcrService.java +++ b/yanzhu-common/yanzhu-common-core/src/main/java/com/yanzhu/common/core/utils/OcrService.java @@ -81,6 +81,9 @@ public class OcrService { map.put("address",address); map.put("cardId",cardId); map.put("birthDay",birthDay); + if(StringUtils.isNotEmpty(cardId)){ + map.put("native",IdcardUtils.getProvinceByIdCard(cardId)); + } map.put("sex",sex); return map; } diff --git a/yanzhu-modules/yanzhu-manage/src/main/java/com/yanzhu/manage/controller/wechat/WxController.java b/yanzhu-modules/yanzhu-manage/src/main/java/com/yanzhu/manage/controller/wechat/WxController.java index 0732eb24..f1cde0c8 100644 --- a/yanzhu-modules/yanzhu-manage/src/main/java/com/yanzhu/manage/controller/wechat/WxController.java +++ b/yanzhu-modules/yanzhu-manage/src/main/java/com/yanzhu/manage/controller/wechat/WxController.java @@ -1,5 +1,6 @@ package com.yanzhu.manage.controller.wechat; +import com.yanzhu.common.core.utils.OcrService; import com.yanzhu.common.core.web.controller.BaseController; import com.yanzhu.common.core.web.domain.AjaxResult; import com.yanzhu.manage.domain.BusExamUser; @@ -198,4 +199,19 @@ public class WxController extends BaseController { proProjectInfoSubdeptsUsersService.startFlowableProcess(); return success(); } + + /** + * + * @param url + * @return + */ + @GetMapping("/cardOCR/front") + public AjaxResult getFront(String url) { + return AjaxResult.success(OcrService.getFront(url)); + } + + @GetMapping("/cardOCR/back") + public AjaxResult getBack(String url) { + return AjaxResult.success(OcrService.getBack(url)); + } }