package jnpf.base.model.ocr.model;
|
|
import com.alibaba.fastjson.JSONObject;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
import jnpf.base.model.ocr.OcrConstant;
|
import jnpf.base.model.ocr.OcrModel;
|
import jnpf.util.DateUtil;
|
import jnpf.util.StringUtil;
|
import lombok.Data;
|
import lombok.EqualsAndHashCode;
|
import lombok.NoArgsConstructor;
|
import lombok.extern.slf4j.Slf4j;
|
|
import java.text.ParseException;
|
import java.text.SimpleDateFormat;
|
import java.util.Objects;
|
import java.util.regex.Matcher;
|
import java.util.regex.Pattern;
|
|
/**
|
* ocr解析身份证模型
|
*
|
* @author JNPF开发平台组
|
* @version v6.1.0
|
* @copyright 引迈信息技术有限公司
|
* @date 2025/7/29 16:58:47
|
*/
|
@EqualsAndHashCode(callSuper = true)
|
@Data
|
@Schema(description = "ocr解析身份证模型")
|
@NoArgsConstructor
|
@Slf4j
|
public class IdCardModel extends OcrModel {
|
@Schema(description = "姓名")
|
private String name;
|
@Schema(description = "性别")
|
private String sex;
|
@Schema(description = "民族")
|
private String nation;
|
@Schema(description = "出生日期")
|
private Long birth;
|
@Schema(description = "地址")
|
private String address;
|
@Schema(description = "身份证号")
|
private String idNum;
|
//国徽面
|
@Schema(description = "发证机关")
|
private String authority;
|
@Schema(description = "证件有效期")
|
private String validDate;
|
@Schema(description = "证件有效期起始日期")
|
private Long startDate;
|
@Schema(description = "证件有效期结束日期")
|
private Long endDate;
|
|
@Override
|
public void extract(String url, JSONObject body) {
|
super.extract(url, body);
|
if (Objects.equals(this.getType(), OcrConstant.IDCARD_FRONT)) {
|
for (String str : this.getStrList()) {
|
predictName(str);
|
national(str);
|
cardNumber(str);
|
}
|
address();
|
if (StringUtil.isNotEmpty(idNum)) {
|
sex();
|
birthday();
|
}
|
//以下是补充识别
|
fullName();
|
national2();
|
} else {
|
for (String str : this.getStrList()) {
|
qianFaJiGuan(str);
|
youXiaoQiXian(str);
|
}
|
if (StringUtil.isNotEmpty(validDate)) {
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd");
|
try {
|
// 拆分字符串
|
String[] dates = validDate.split("-");
|
startDate = sdf.parse(dates[0]) != null ? sdf.parse(dates[0]).getTime() : null;
|
// 解析结束日期
|
endDate = sdf.parse(dates[1]) != null ? sdf.parse(dates[1]).getTime() : null;
|
} catch (ParseException e) {
|
log.error("日期解析失败: " + e.getMessage());
|
}
|
}
|
}
|
this.setContent(null);
|
this.setStrList(null);
|
}
|
|
/**
|
* 获取身份证姓名
|
*/
|
private void predictName(String str) {
|
if (str.contains("姓名") || str.contains("名")) {
|
String pattern = ".*名[一-龥]{1,4}";
|
Pattern r = Pattern.compile(pattern);
|
Matcher m = r.matcher(str);
|
if (m.matches()) {
|
name = str.substring(str.indexOf("名") + 1);
|
}
|
}
|
}
|
|
/**
|
* 为了防止第一次得到的名字为空,以后是遇到什么情况就解决什么情况就行了
|
*/
|
private void fullName() {
|
if (StringUtil.isEmpty(name)) {
|
String result = this.getContent();
|
if (result.contains("性") || result.contains("性别")) {
|
String str = result.substring(0, result.lastIndexOf("性"));
|
String pattern = ".*名[一-龥]{1,4}";
|
Pattern r = Pattern.compile(pattern);
|
Matcher m = r.matcher(str);
|
if (m.matches()) {
|
name = str.substring(str.indexOf("名") + 1);
|
}
|
}
|
}
|
}
|
|
/**
|
* 获取民族
|
*/
|
private void national(String str) {
|
String pattern = ".*民族[一-龥]{1,4}";
|
Pattern r = Pattern.compile(pattern);
|
Matcher m = r.matcher(str);
|
if (m.matches()) {
|
nation = str.substring(str.indexOf("族") + 1);
|
}
|
}
|
|
private void national2() {
|
if (StringUtil.isEmpty(nation) && this.getContent().contains("出生")) {
|
String front = this.getContent().split("出生")[0];
|
nation = front.substring(front.indexOf("民") + 2);
|
}
|
}
|
|
/**
|
* 获取身份证地址
|
*/
|
private void address() {
|
StringBuilder addressJoin = new StringBuilder();
|
for (String str : this.getStrList()) {
|
if (str.contains("住址") || str.contains("址") || str.contains("省") || str.contains("市")
|
|| str.contains("县") || str.contains("街") || str.contains("乡") || str.contains("村")
|
|| str.contains("镇") || str.contains("区") || str.contains("城") || str.contains("组")
|
|| str.contains("号") || str.contains("幢") || str.contains("室")
|
) {
|
addressJoin.append(str);
|
}
|
}
|
String s = addressJoin.toString();
|
if (s.contains("省") || s.contains("县") || s.contains("住址") || s.contains("址") || s.contains("公民身份")) {
|
address = s.substring(s.indexOf("址") + 1, s.indexOf("公民身份"));
|
} else {
|
address = s;
|
}
|
}
|
|
/**
|
* 获取身份证号
|
*/
|
private void cardNumber(String str) {
|
// 18位身份证正则表达式
|
Pattern r = Pattern.compile("[1-9]\\d{13,16}[a-zA-Z0-9]");
|
Matcher m = r.matcher(str);
|
if (m.find()) {
|
idNum = m.group();
|
}
|
}
|
|
/**
|
* 二代身份证18位
|
*/
|
private void sex() {
|
// 取倒身份证倒数第二位的数字的奇偶性判断性别,二代身份证18位
|
String substring = idNum.substring(idNum.length() - 2, idNum.length() - 1);
|
int parseInt = Integer.parseInt(substring);
|
if (parseInt % 2 == 0) {
|
sex = "女";
|
} else {
|
sex = "男";
|
}
|
}
|
|
/**
|
* 从身份证中获取出生信息
|
*/
|
private void birthday() {
|
String dateStr = idNum.substring(6, 14);
|
String year = dateStr.substring(0, 4);
|
String month = dateStr.substring(4, 6);
|
String day = dateStr.substring(6, 8);
|
String bd = year + "-" + month + "-" + day;
|
birth = DateUtil.stringToDates(bd).getTime();
|
}
|
|
/**
|
* 获取身份证反面信息的签发机关
|
*/
|
private void qianFaJiGuan(String str) {
|
if (str.contains("公安局")) {
|
if (str.contains("签发机关")) {
|
str = str.replace("签发机关", "");
|
}
|
String pattern = ".*局";
|
Pattern r = Pattern.compile(pattern);
|
Matcher m = r.matcher(str);
|
if (m.matches()) {
|
authority = str;
|
}
|
}
|
}
|
|
/**
|
* 身份证反面有效期识别
|
*/
|
private void youXiaoQiXian(String str) {
|
if (str.contains("有效期限")) {
|
// String为引用类型
|
str = str.replace("有效期限", "");
|
}
|
String pattern = "\\d{4}[-/.]\\d{1,2}[-/.]\\d{1,2}-\\d{4}[-/.]\\d{1,2}[-/.]\\d{1,2}";
|
Pattern r = Pattern.compile(pattern);
|
Matcher m = r.matcher(str);
|
if (m.matches()) {
|
validDate = str;
|
}
|
}
|
}
|