增加百度云api的人脸和OCR

This commit is contained in:
2025-07-18 18:37:27 +08:00
parent 3dc6d94d60
commit aade077977
18 changed files with 987 additions and 1 deletions

View File

@ -319,3 +319,10 @@ warm-flow:
- 255,205,23 - 255,205,23
## 已办理 ## 已办理
- 157,255,0 - 157,255,0
--- # 百度云配置
baidu:
client:
id: zSB7KdLgY7a1tIEx3eTy65TE
secret: 5nabjclW5BWGV8UwEueDgBDmOveRVkmD

View File

@ -17,7 +17,12 @@
<dependencies> <dependencies>
<!-- JSON解析FastJSON -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.32</version>
</dependency>
<!-- word转pdf转图片 --> <!-- word转pdf转图片 -->
<!-- docx4j 核心 --> <!-- docx4j 核心 -->
<dependency> <dependency>
@ -189,6 +194,22 @@
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>ruoyi-workflow</artifactId> <artifactId>ruoyi-workflow</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>2.13.9</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna-platform</artifactId>
<version>5.15.0</version>
<scope>compile</scope>
</dependency>
</dependencies> </dependencies>

View File

@ -0,0 +1,9 @@
package org.dromara.common.constant;
/**
* @Author 铁憨憨
* @Date 2025/7/18 10:38
* @Version 1.0
*/public interface businessConstant {
String REDIS_BAIDU_KEY = "baidu:access_token"; //百度token存储在redis中的key
}

View File

@ -0,0 +1,24 @@
package org.dromara.common.utils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.net.http.HttpClient;
import java.time.Duration;
/**
* @Author 铁憨憨
* @Date 2025/7/18 10:16
* @Version 1.0
*
* HttpClient 设计为可重用、线程安全的组件,其内部维护了连接池等资源,适合在多个接口调用中共享使用,所以交给Spring Bean 管理
*/
@Configuration
public class HttpClientConfig {
@Bean
public HttpClient httpClient() {
return HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
}
}

View File

@ -0,0 +1,101 @@
package org.dromara.common.utils.baiduUtil;
import com.google.gson.Gson;
import org.dromara.common.utils.baiduUtil.entity.AccessTokenResponse;
import org.glassfish.jaxb.runtime.v2.runtime.reflect.opt.Const;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Value;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.concurrent.TimeUnit;
import static org.dromara.common.constant.businessConstant.REDIS_BAIDU_KEY;
/**
* @Author 铁憨憨
* @Date 2025/7/18 9:46
* @Version 1.0
*
* 获取百度AccessToken
*/
@Service
public class BaiDuCommon {
@Autowired
private HttpClient httpClient;
@Autowired
private StringRedisTemplate redisTemplate;
@Value("${baidu.client.id}")
private String clientId;
@Value("${baidu.client.secret}")
private String clientSecret;
private static final String TOKEN_URL = "https://aip.baidubce.com/oauth/2.0/token?client_id=%s&client_secret=%s&grant_type=client_credentials";
private static final Gson gson = new Gson();
/**
* 获取百度AccessToken优先从Redis缓存中获取
*/
public String getAccessToken() {
// 1. 尝试从Redis中获取
String cachedToken = redisTemplate.opsForValue().get(REDIS_BAIDU_KEY);
if (cachedToken != null) {
return cachedToken;
}
// 2. Redis中没有加锁防止并发请求刷新Token
synchronized (this) {
// 再次检查,避免其他线程已经刷新
cachedToken = redisTemplate.opsForValue().get(REDIS_BAIDU_KEY);
if (cachedToken != null) {
return cachedToken;
}
// 3. 真正调用API获取新Token
String newToken = fetchAccessTokenFromBaidu();
if (newToken != null) {
// 4. 将新Token存入Redis并设置过期时间比实际有效期短一些
redisTemplate.opsForValue().set(REDIS_BAIDU_KEY, newToken, 29, TimeUnit.DAYS);
return newToken;
}
}
return null; // 获取失败
}
/**
* 从百度API获取AccessToken
*/
private String fetchAccessTokenFromBaidu() {
String url = String.format(TOKEN_URL, clientId, clientSecret);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("Content-Type", "application/json")
.header("Accept", "application/json")
.POST(HttpRequest.BodyPublishers.noBody())
.build();
try {
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == 200) {
AccessTokenResponse tokenResponse = gson.fromJson(response.body(), AccessTokenResponse.class);
return tokenResponse.getAccessToken();
} else {
throw new IOException("获取AccessToken失败状态码: " + response.statusCode());
}
} catch (IOException | InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("调用百度API获取AccessToken失败", e);
}
}
}

View File

@ -0,0 +1,205 @@
package org.dromara.common.utils.baiduUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.dromara.common.utils.baiduUtil.entity.face.ComparisonRes;
import org.dromara.common.utils.baiduUtil.entity.face.HumanFaceReq;
import org.dromara.common.utils.baiduUtil.entity.face.HumanFaceRes;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.core.JsonProcessingException;
import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
* @Author 铁憨憨
* @Date 2025/7/18 17:09
* @Version 1.0
* 处理百度人脸相关逻辑:人脸检测、人脸对比
*/
@Service
public class BaiDuFace {
@Autowired
private BaiDuCommon baiDuCommon;
@Autowired
private HttpClient httpClient;
@Autowired
private ObjectMapper objectMapper;
// 人脸识别API地址
private static final String FACE_DETECT_URL = "https://aip.baidubce.com/rest/2.0/face/v3/detect?access_token=%s";
// 人脸对比API地址
private static final String FACE_COMPARE_URL = "https://aip.baidubce.com/rest/2.0/face/v3/match?access_token=%s";
/**
* 人脸识别+人脸检测
* @param request 人脸请求参数
* @throws RuntimeException 检测失败时抛出异常由Spring统一处理
*/
public void humanFace(HumanFaceReq request) {
// 1. 获取AccessToken
String accessToken = baiDuCommon.getAccessToken();
if (accessToken == null || accessToken.trim().isEmpty()) {
throw new RuntimeException("获取访问令牌失败token为空");
}
// 2. 构建请求URL
String requestUrl = String.format(FACE_DETECT_URL, URLEncoder.encode(accessToken, StandardCharsets.UTF_8));
try {
// 3. 序列化请求参数
String requestBody = objectMapper.writeValueAsString(request);
// 4. 构建HTTP请求
HttpRequest httpRequest = HttpRequest.newBuilder()
.uri(URI.create(requestUrl))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
// 5. 发送请求并获取响应
HttpResponse<String> response = httpClient.send(
httpRequest,
HttpResponse.BodyHandlers.ofString()
);
// 6. 解析响应结果
HumanFaceRes faceRep = objectMapper.readValue(response.body(), HumanFaceRes.class);
// 7. 处理API返回错误
if (faceRep.getErrorCode() != 0) {
throw new RuntimeException("错误码说明:" + faceRep.getErrorMsg());
}
// 8. 验证人脸信息(无人脸时抛出异常)
if (faceRep.getResult() == null
|| faceRep.getResult().getFaceList() == null
|| faceRep.getResult().getFaceList().isEmpty()) {
throw new RuntimeException("未检测到有效人脸信息");
}
// 9. 人脸质量校验
HumanFaceRes.FaceList faceInfo = faceRep.getResult().getFaceList().get(0);
// 9.1 人脸置信度校验必须为1.0
if (faceInfo.getFaceProbability() != 1.0) {
throw new RuntimeException("人脸置信度过低,请重新拍摄!");
}
// 9.2 真实人脸校验(排除卡通人脸)
HumanFaceRes.FaceType faceType = faceInfo.getFaceType();
if (faceType == null) {
throw new RuntimeException("无法判断人脸类型");
}
if ("cartoon".equals(faceType.getType())) {
throw new RuntimeException("请传入真实人脸,勿使用卡通图像");
}
// 校验真实人脸置信度需≥0.8
if (faceType.getProbability() < 0.8) {
throw new RuntimeException("人脸识别不清晰,请换个角度拍摄");
}
// 9.3 人脸模糊度校验模糊度≥0.1视为模糊)
HumanFaceRes.Quality quality = faceInfo.getQuality();
if (quality == null) {
throw new RuntimeException("无法获取人脸质量信息");
}
if (quality.getBlur() != 0) {
throw new RuntimeException("人脸过于模糊,请保持镜头稳定");
}
// 9.4 光线校验(光线值<100视为过暗
if (quality.getIllumination() < 100.0) {
throw new RuntimeException("光线太暗,请在光线充足的环境下拍摄");
}
// 9.5 人脸完整性校验1为完整0为不完整
if (quality.getCompleteness() != 1) {
throw new RuntimeException("人脸未完全显示,请确保面部在框内");
}
} catch (JsonProcessingException e) {
throw new RuntimeException("请求参数序列化失败:" + e.getMessage(), e);
} catch (IOException e) {
throw new RuntimeException("IO处理异常" + e.getMessage(), e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复中断状态
throw new RuntimeException("请求被中断:" + e.getMessage(), e);
}
}
/**
* 人脸对比(计算人脸相似度)
* @param requestList 人脸请求参数列表至少包含2个人脸
* @return 人脸相似度得分
* @throws RuntimeException 对比失败时抛出异常
*/
public double comparison(List<HumanFaceReq> requestList) {
// 1. 校验请求参数
if (requestList == null || requestList.size() < 2) {
throw new RuntimeException("人脸对比至少需要2张人脸图片");
}
// 2. 获取AccessToken
String accessToken = baiDuCommon.getAccessToken();
if (accessToken == null || accessToken.trim().isEmpty()) {
throw new RuntimeException("获取访问令牌失败token为空");
}
// 3. 构建请求URL
String requestUrl = String.format(FACE_COMPARE_URL, URLEncoder.encode(accessToken, StandardCharsets.UTF_8));
try {
// 4. 序列化请求参数
String requestBody = objectMapper.writeValueAsString(requestList);
// 5. 构建HTTP请求
HttpRequest httpRequest = HttpRequest.newBuilder()
.uri(URI.create(requestUrl))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
// 6. 发送请求并获取响应
HttpResponse<String> response = httpClient.send(
httpRequest,
HttpResponse.BodyHandlers.ofString()
);
// 7. 解析响应结果
ComparisonRes comparisonRep = objectMapper.readValue(response.body(), ComparisonRes.class);
// 8. 处理API返回错误
if (comparisonRep.getErrorCode() != 0) {
throw new RuntimeException("人脸对比失败:" + comparisonRep.getErrorMsg()
+ "(错误码:" + comparisonRep.getErrorCode() + "");
}
// 9. 校验对比结果
if (comparisonRep.getResult() == null) {
throw new RuntimeException("人脸对比结果为空");
}
return comparisonRep.getResult().getScore();
} catch (JsonProcessingException e) {
throw new RuntimeException("请求参数序列化失败:" + e.getMessage(), e);
} catch (IOException e) {
throw new RuntimeException("IO处理异常" + e.getMessage(), e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("请求被中断:" + e.getMessage(), e);
}
}
}

View File

@ -0,0 +1,204 @@
package org.dromara.common.utils.baiduUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.dromara.common.utils.baiduUtil.entity.ocr.IDCardInfo;
import org.dromara.common.utils.baiduUtil.entity.ocr.OcrReq;
import org.dromara.common.utils.baiduUtil.entity.ocr.Result;
import org.dromara.common.utils.baiduUtil.entity.ocr.WordsResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
/**
* @Author 铁憨憨
* @Date 2025/7/18 10:45
* @Version 1.0
*
* 处理百度OCR相关逻辑身份证识别、银行卡识别
*/
@Service
public class BaiDuOCR {
@Autowired
private BaiDuCommon baiDuCommon;
@Autowired
private HttpClient httpClient;
@Autowired
private ObjectMapper objectMapper; //ObjectMapper 是 Java 处理 JSON 的核心工具,项目中使用它进行 JSON 与 Java 对象的相互转换。
String baseUrlTemplate = "https://aip.baidubce.com/rest/2.0/ocr/v1/bankcard?access_token=%s";
String BASE_URL = "https://aip.baidubce.com/rest/2.0/ocr/v1/idcard?access_token=%s";
/**
* @description 银行卡OCR识别
* @author 铁憨憨
* @date 2025/7/18 11:50
* @param vr 请求参数
* @return 识别结果Result
**/
public Result bankCardOCRRecognition(OcrReq vr) {
// 先从缓存里面捞取token若为空直接抛出异常
String atStr = baiDuCommon.getAccessToken();
if (atStr == null || atStr.trim().isEmpty()) {
throw new RuntimeException("获取访问令牌失败token为空");
}
try {
// 构建请求URL包含token参数
String requestUrl = String.format(baseUrlTemplate, URLEncoder.encode(atStr, StandardCharsets.UTF_8));
// 准备请求体将请求参数转为JSON
String requestBody = objectMapper.writeValueAsString(vr);
if (requestBody == null) {
throw new RuntimeException("请求参数序列化失败");
}
// 构建HTTP请求
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(requestUrl))
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
// 发送请求并获取响应
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
// 处理响应状态码非200直接抛出异常
if (response.statusCode() != 200) {
throw new RuntimeException("接口请求失败,状态码:" + response.statusCode() + ",响应信息:" + response.body());
}
// 解析响应结果(若解析失败抛出异常)
Result result = objectMapper.readValue(response.body(), Result.class);
if (result == null) {
throw new RuntimeException("响应结果解析失败");
}
// // 若接口返回错误码如百度API的error_code抛出异常
// if (result.getErrorCode() != null && result.getErrorCode() != 0) {
// throw new RuntimeException("接口返回错误:" + result.getErrorMessage() + "(错误码:" + result.getErrorCode() + "");
// }
return result;
} catch (IOException e) {
// IO异常如序列化失败、网络IO错误
throw new RuntimeException("IO处理异常" + e.getMessage(), e);
} catch (InterruptedException e) {
// 线程中断异常
Thread.currentThread().interrupt(); // 恢复中断状态
throw new RuntimeException("请求被中断:" + e.getMessage(), e);
}
}
/**
* @description 身份证OCR识别
* @author 铁憨憨
* @date 2025/7/18 16:53
* @param request 请求参数
* @return 识别结果WordsResult
**/
public WordsResult idCardOCR(OcrReq request) {
// 获取AccessToken为空直接抛异常
String accessToken = baiDuCommon.getAccessToken();
if (accessToken == null || accessToken.trim().isEmpty()) {
throw new RuntimeException("获取访问令牌失败token为空");
}
// 构建请求URL
String requestUrl = String.format(BASE_URL, accessToken);
try {
// 准备请求体(序列化失败抛异常)
String requestBody = objectMapper.writeValueAsString(request);
if (requestBody == null) {
throw new RuntimeException("请求参数序列化失败");
}
// 构建HTTP请求
HttpRequest httpRequest = HttpRequest.newBuilder()
.uri(URI.create(requestUrl))
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
// 发送请求并获取响应
HttpResponse<String> response = httpClient.send(
httpRequest,
HttpResponse.BodyHandlers.ofString()
);
// 处理响应状态码非200直接抛异常
if (response.statusCode() != 200) {
throw new RuntimeException("接口请求失败,状态码:" + response.statusCode() + ",响应信息:" + response.body());
}
// 处理响应内容(去除空格并解析)
String responseBody = response.body().replaceAll("\\s+", "");
IDCardInfo idCardInfo = objectMapper.readValue(responseBody, IDCardInfo.class);
if (idCardInfo == null) {
throw new RuntimeException("响应结果解析失败");
}
// 检查身份证状态状态异常由validateImageStatus抛异常
validateImageStatus(idCardInfo.getImageStatus());
// 提取识别结果(结果为空抛异常)
WordsResult wordsResult = idCardInfo.getWordsResult();
if (wordsResult == null) {
throw new RuntimeException("未识别到身份证信息");
}
return wordsResult;
} catch (IOException e) {
// IO异常序列化、网络IO等
throw new RuntimeException("IO处理异常" + e.getMessage(), e);
} catch (InterruptedException e) {
// 线程中断异常
Thread.currentThread().interrupt(); // 恢复中断状态
throw new RuntimeException("请求被中断:" + e.getMessage(), e);
}
}
/**
* @description 验证身份证图片状态
* @author 铁憨憨
* @date 2025/7/18 17:08
* @param imageStatus 图片状态
* @throws RuntimeException 当状态不正常时抛出运行时异常
**/
private void validateImageStatus(String imageStatus) {
if (imageStatus == null || "normal".equals(imageStatus)) {
return; // 正常状态,直接返回
}
// 根据不同状态码抛出不同的异常信息
String errorMessage = switch (imageStatus) {
case "reversed_side" -> "身份证正反面颠倒,请调整后重新上传";
case "non_idcard" -> "上传的图片中不包含身份证,请重新上传";
case "blurred" -> "身份证图片模糊,请重新上传清晰的图片";
case "other_type_card" -> "上传的不是身份证,请上传有效的身份证图片";
case "over_exposure" -> "身份证关键字段反光或过曝,请调整光线后重新上传";
case "over_dark" -> "身份证图片欠曝(亮度过低),请调整光线后重新上传";
default -> "未知错误:" + imageStatus;
};
throw new RuntimeException(errorMessage);
}
}

View File

@ -0,0 +1,19 @@
package org.dromara.common.utils.baiduUtil.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* @Author 铁憨憨
* @Date 2025/7/18 9:56
* @Version 1.0
*/
@Data
@Accessors(chain = true)
public class AccessTokenResponse {
private String accessToken;
private int expiresIn;
private String scope;
}

View File

@ -0,0 +1,49 @@
package org.dromara.common.utils.baiduUtil.entity.face;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
/**
* @Author 铁憨憨
* @Date 2025/7/18 17:15
* @Version 1.0
*
* 人脸对比响应结果
*/
@Data
public class ComparisonRes {
@JsonProperty("error_code")
private int errorCode;
@JsonProperty("error_msg")
private String errorMsg;
@JsonProperty("log_id")
private long logId;
@JsonProperty("timestamp")
private long timestamp;
@JsonProperty("cached")
private int cached;
@JsonProperty("result")
private CompareResult result;
@Data
public static class CompareResult {
@JsonProperty("score")
private double score; // 人脸相似度得分
@JsonProperty("face_list")
private List<FaceToken> faceList;
}
@Data
public static class FaceToken {
@JsonProperty("face_token")
private String faceToken;
}
}

View File

@ -0,0 +1,23 @@
package org.dromara.common.utils.baiduUtil.entity.face;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* @Author 铁憨憨
* @Date 2025/7/18 17:12
* @Version 1.0
*
* 人脸识别请求参数
*/
@Data
public class HumanFaceReq {
@JsonProperty("image")
private String image; // 图片base64字符串
@JsonProperty("image_type")
private String imageType = "BASE64"; // 图片类型固定为BASE64
@JsonProperty("face_field")
private String faceField = "face_type,quality"; // 需要返回的人脸字段
}

View File

@ -0,0 +1,142 @@
package org.dromara.common.utils.baiduUtil.entity.face;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
/**
* @Author 铁憨憨
* @Date 2025/7/18 17:13
* @Version 1.0
*
* 人脸识别响应结果
*/
@Data
public class HumanFaceRes {
@JsonProperty("error_code")
private int errorCode;
@JsonProperty("error_msg")
private String errorMsg;
@JsonProperty("log_id")
private long logId;
@JsonProperty("timestamp")
private long timestamp;
@JsonProperty("cached")
private int cached;
@JsonProperty("result")
private Result result;
@Data
public static class Result {
@JsonProperty("face_num")
private int faceNum;
@JsonProperty("face_list")
private List<FaceList> faceList;
}
@Data
public static class FaceList {
@JsonProperty("face_token")
private String faceToken;
@JsonProperty("location")
private Location location;
@JsonProperty("face_probability")
private double faceProbability;
@JsonProperty("angle")
private Angle angle;
@JsonProperty("face_type")
private FaceType faceType;
@JsonProperty("quality")
private Quality quality;
}
@Data
public static class Location {
@JsonProperty("left")
private double left;
@JsonProperty("top")
private double top;
@JsonProperty("width")
private int width;
@JsonProperty("height")
private int height;
@JsonProperty("rotation")
private int rotation;
}
@Data
public static class Angle {
@JsonProperty("yaw")
private double yaw;
@JsonProperty("pitch")
private double pitch;
@JsonProperty("roll")
private double roll;
}
@Data
public static class FaceType {
@JsonProperty("type")
private String type;
@JsonProperty("probability")
private double probability;
}
@Data
public static class Quality {
@JsonProperty("occlusion")
private Occlusion occlusion;
@JsonProperty("blur")
private double blur;
@JsonProperty("illumination")
private double illumination;
@JsonProperty("completeness")
private long completeness;
}
@Data
public static class Occlusion {
@JsonProperty("left_eye")
private double leftEye;
@JsonProperty("right_eye")
private double rightEye;
@JsonProperty("nose")
private double nose;
@JsonProperty("mouth")
private double mouth;
@JsonProperty("left_cheek")
private double leftCheek;
@JsonProperty("right_cheek")
private double rightCheek;
@JsonProperty("chin_contour")
private double chinContour;
}
}

View File

@ -0,0 +1,39 @@
package org.dromara.common.utils.baiduUtil.entity.ocr;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
/**
* @Author 铁憨憨
* @Date 2025/7/18 10:58
* @Version 1.0
*
* 银行卡具体信息
*/
@Data
//public class BankData {
// private String valid_date; // 有效期
// private String bank_card_number; // 卡号
// private String bank_name; // 银行名称
// private int bank_card_type; // 卡类型0未知1借记卡2贷记卡等
// private String holder_name; // 持卡人姓名(可能为空)
//}
public class BankData {
@JSONField(name = "bank_card_number",label = "银行卡卡号")
private String bankCardNumber;
@JSONField(name = "valid_date",label = "有效期")
private String validDate;
@JSONField(name = "bank_card_type",label = "银行卡类型0不能识别; 1借记卡; 2贷记卡原信用卡大部分为贷记卡; 3准贷记卡; 4预付费卡")
private int bankCardType;
@JSONField(name = "bank_name",label = "银行名,不能识别时为空")
private String bankName;
@JSONField(name = "holder_name",label = "持卡人姓名,不能识别时为空")
private String holderName;
}

View File

@ -0,0 +1,16 @@
package org.dromara.common.utils.baiduUtil.entity.ocr;
import lombok.Data;
/**
* @Author 铁憨憨
* @Date 2025/7/18 10:55
* @Version 1.0
*
* 单个识别字段(含文字和位置)
*/
@Data
public class Field {
private Location location; // 位置信息
private String words; // 识别文字
}

View File

@ -0,0 +1,30 @@
package org.dromara.common.utils.baiduUtil.entity.ocr;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
/**
* @Author 铁憨憨
* @Date 2025/7/18 10:50
* @Version 1.0
*
* 身份证识别结果
*/
@Data
public class IDCardInfo {
@JSONField(name = "words_result")
private WordsResult wordsResult; // 识别字段集合(姓名、身份证号等)
@JSONField(name = "words_result_num")
private int wordsResultNum; // 识别字段数量
@JSONField(name = "idcard_number_type")
private int idCardNumberType; // 身份证类型如18位/15位
private String imageStatus; // 图像状态(如是否颠倒)
@JSONField(name = "log_id")
private long logId; // 请求唯一标识日志ID
private String photo; // 身份证照片base64若检测
}

View File

@ -0,0 +1,18 @@
package org.dromara.common.utils.baiduUtil.entity.ocr;
import lombok.Data;
/**
* @Author 铁憨憨
* @Date 2025/7/18 10:56
* @Version 1.0
*
* 位置信息(坐标)
*/
@Data
public class Location {
private int top; // 顶部坐标
private int left; // 左侧坐标
private int width; // 宽度
private int height; // 高度
}

View File

@ -0,0 +1,24 @@
package org.dromara.common.utils.baiduUtil.entity.ocr;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
/**
* @Author 铁憨憨
* @Date 2025/7/18 10:47
* @Version 1.0
*
* OCR请求参数身份证/银行卡通用)
*/
@Data
public class OcrReq {
private String image; // 图像base64与url二选一
private String url; // 图像URL与image二选一
@JSONField(name = "id_card_side")
private String idCardSide; // 身份证正反面front/back银行卡无需
@JSONField(name = "detect_photo")
private boolean detectPhoto;// 是是否开启银行卡质量类型(清晰模糊、边框/四角不完整检测功能默认不开启false。
}

View File

@ -0,0 +1,22 @@
package org.dromara.common.utils.baiduUtil.entity.ocr;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
/**
* @Author 铁憨憨
* @Date 2025/7/18 11:20
* @Version 1.0
*
* 银行卡识别的完整返回结果
*/
@Data
public class Result {
@JSONField(name = "result")
private BankData res;
private int direction;
@JSONField(name = "log_id")
private long logId;
}

View File

@ -0,0 +1,33 @@
package org.dromara.common.utils.baiduUtil.entity.ocr;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
/**
* @Author 铁憨憨
* @Date 2025/7/18 10:50
* @Version 1.0
*
* 身份证识别字段集合
*/
@Data
public class WordsResult {
@JSONField(name = "姓名")
private Field name; // 姓名
@JSONField(name = "民族")
private Field nation; // 民族
@JSONField(name = "住址")
private Field address; // 住址
@JSONField(name = "公民身份号码")
private Field citizenIdentification; // 身份证号
@JSONField(name = "出生")
private Field birth; // 出生日期
@JSONField(name = "性别")
private Field gender; // 性别
@JSONField(name = "失效日期")
private Field expirationDate; // 失效日期
@JSONField(name = "签发机关")
private Field issuingAuthority; // 签发机关
@JSONField(name = "签发日期")
private Field issueDate; // 签发日期
}