diff --git a/api-third/src/main/java/com/heyu/api/baidu/handle/traffic/BDriverLicenseHandle.java b/api-third/src/main/java/com/heyu/api/baidu/handle/traffic/BDriverLicenseHandle.java new file mode 100644 index 0000000..004fcae --- /dev/null +++ b/api-third/src/main/java/com/heyu/api/baidu/handle/traffic/BDriverLicenseHandle.java @@ -0,0 +1,80 @@ +package com.heyu.api.baidu.handle.traffic; + +import com.heyu.api.baidu.BaiduBaseHandle; +import com.heyu.api.baidu.request.traffic.BDriverLicenseRequest; +import com.heyu.api.data.annotation.CustomPath; +import com.heyu.api.data.constants.ApiConstants; +import com.heyu.api.data.utils.StringUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * https://console.bce.baidu.com/support/#/api?product=AI&project=文字识别&parent=交通场景OCR&api=rest%2F2.0%2Focr%2Fv1%2Fdriving_license&method=post + *

+ *

+ * 驾驶证识别 - 百度OCR + * + * 支持对机动车驾驶证正页及副页所有字段进行结构化识别。 + * 驾驶证正页:证号、姓名、性别、国籍、出生日期、初次领证日期、准驾车型、有效起始日期、失效日期、住址、发证单位 11个字段 + * 驾驶证副页:姓名、记录、证号、档案编号 4个字段 + * 同时支持识别交管12123 APP 发放的电子驾驶证正页,包括证号、姓名、性别、国籍、出生日期、初次领证日期、准驾车型、有效起始日期、失效日期、累积记分、状态、档案编号、生成时间、当前时间、条形码下编号 15个字段 + */ +@Component +@Slf4j +@CustomPath("driverLicense") +public class BDriverLicenseHandle extends BaiduBaseHandle { + + @Override + public String getUri() { + return "/rest/2.0/ocr/v1/driving_license"; + } + + @Override + public String check(BDriverLicenseRequest request) { + if (checkNotTrueFalse(request.getDetectDirection())) { + return "detectDirection 必须传 " + ApiConstants.trueOrFalse + + ",false:默认值,不检测朝向, true:检测朝向"; + } + if (checkNotFrontBack(request.getDrivingLicenseSide())) { + return "drivingLicenseSide 必须传 " + ApiConstants.frontOrback + + ",front:识别驾驶证正页、电子驾驶证正页, back:识别驾驶证副页"; + } + if (checkNotTrueFalse(request.getUnifiedValidPeriod())) { + return "unifiedValidPeriod 必须传 " + ApiConstants.trueOrFalse + + ",false:默认值,不进行归一化处理, true:归一化格式输出"; + } + if (checkNotTrueFalse(request.getQualityWarn())) { + return "qualityWarn 必须传 " + ApiConstants.trueOrFalse + + ",false:不输出质量告警信息, true:输出遮挡、不完整质量告警信息"; + } + if (checkNotTrueFalse(request.getRiskWarn())) { + return "riskWarn 必须传 " + ApiConstants.trueOrFalse + + ",false:不输出风险告警信息, true:开启,输出驾驶正复印、翻拍、PS等告警信息"; + } + return checkImageUri(request); + } + + @Override + public String getContent(BDriverLicenseRequest request) { + StringBuffer sb = getImageContent(request); + if (StringUtils.isNotBlank(request.getDetectDirection())) { + sb.append("&detect_direction=").append(request.getDetectDirection()); + } + if (StringUtils.isNotBlank(request.getDrivingLicenseSide())) { + sb.append("&driving_license_side=").append(request.getDrivingLicenseSide()); + } + if (StringUtils.isNotBlank(request.getUnifiedValidPeriod())) { + sb.append("&unified_valid_period=").append(request.getUnifiedValidPeriod()); + } + if (StringUtils.isNotBlank(request.getQualityWarn())) { + sb.append("&quality_warn=").append(request.getQualityWarn()); + } + if (StringUtils.isNotBlank(request.getRiskWarn())) { + sb.append("&risk_warn=").append(request.getRiskWarn()); + } + return sb.toString(); + } + +} diff --git a/api-third/src/main/java/com/heyu/api/baidu/request/traffic/BDriverLicenseRequest.java b/api-third/src/main/java/com/heyu/api/baidu/request/traffic/BDriverLicenseRequest.java new file mode 100644 index 0000000..5132212 --- /dev/null +++ b/api-third/src/main/java/com/heyu/api/baidu/request/traffic/BDriverLicenseRequest.java @@ -0,0 +1,52 @@ +package com.heyu.api.baidu.request.traffic; + +import com.heyu.api.baidu.request.BaiduImageUrlRequest; +import lombok.Data; + +/** + * 驾驶证识别 - 百度OCR API请求 + * https://console.bce.baidu.com/support/#/api?product=AI&project=文字识别&parent=交通场景OCR&api=rest%2F2.0%2Focr%2Fv1%2Fdriving_license&method=post + * + * + * 百度驾驶证识别:/rest/2.0/ocr/v1/driving_license + * + * 驾驶证正页:证号、姓名、性别、国籍、出生日期、初次领证日期、准驾车型、有效起始日期、失效日期、住址、发证单位 11个字段 + * 电子驾驶证正页:证号、姓名、性别、国籍、出生日期、初次领证日期、准驾车型、有效起始日期、失效日期、累积记分、状态、档案编号、生成时间、当前时间、条形码下编号 15个字段 + * 驾驶证副页:姓名、记录、证号、档案编号 4个字段 + */ +@Data +public class BDriverLicenseRequest extends BaiduImageUrlRequest { + + /** + * - false:默认值,不检测朝向,朝向是指输入图像是正常方向、逆时针旋转90/180/270度 + * - true:检测朝向 + */ + private String detectDirection = "false"; + + /** + * - front:默认值,识别驾驶证正页、电子驾驶证正页 + * - back:识别驾驶证副页 + */ + private String drivingLicenseSide = "front"; + + /** + * - false: 默认值,不进行归一化处理 + * - true: 归一化格式输出,将驾驶证正页的「有效起始日期」+「有效期限」及「有效期限」+「至」,归一化为「有效起始日期」+「失效日期」格式输出 + */ + private String unifiedValidPeriod = "false"; + + /** + * 是否开启质量检测功能,仅在驾驶证正页识别时生效, + * - false:默认值,不输出质量告警信息 + * - true: 输出驾驶证遮挡、不完整质量告警信息 + */ + private String qualityWarn = "false"; + + /** + * 是否开启风险检测功能,仅在驾驶证正页识别时生效, + * - false:默认值,不输出风险告警信息 + * - true:开启,输出驾驶证复印、翻拍、PS等告警信息 + */ + private String riskWarn = "false"; + +} diff --git a/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeDriverLicenseController.java b/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeDriverLicenseController.java index 5ac58c0..34129a1 100644 --- a/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeDriverLicenseController.java +++ b/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeDriverLicenseController.java @@ -1,14 +1,15 @@ package com.heyu.api.controller.car; -import com.aliyun.ocr20191230.models.RecognizeDriverLicenseResponse; -import com.aliyun.ocr20191230.models.RecognizeDriverLicenseResponseBody; -import com.heyu.api.alibaba.handle.common.text.ARecognizeDriverLicenseHandle; -import com.heyu.api.alibaba.request.common.text.ARecognizeDriverLicenseRequest; +import com.heyu.api.baidu.handle.traffic.BDriverLicenseHandle; +import com.heyu.api.baidu.request.traffic.BDriverLicenseRequest; import com.heyu.api.controller.BaseController; import com.heyu.api.data.annotation.EbAuthentication; import com.heyu.api.data.constants.ApiConstants; import com.heyu.api.data.utils.ApiR; +import com.heyu.api.data.utils.MapUtils; import com.heyu.api.data.utils.R; +import com.heyu.api.data.utils.StringUtils; +import com.heyu.api.request.car.DriverLicenseRecognizeRequest; import com.heyu.api.resp.car.RecognizeDriverLicenseBackResp; import com.heyu.api.resp.car.RecognizeDriverLicenseFaceResp; import lombok.extern.slf4j.Slf4j; @@ -18,96 +19,128 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.Map; + /** - * https://next.api.aliyun.com/api/ocr/2019-12-30/RecognizeDriverLicense?tab=DEMO&lang=JAVA - *

- * 驾驶证识别 - *

- * RecognizeDriverLicense - * - * 驾驶证识别能力可以识别驾驶证首页和副页关键字段内容,包括:档案编号、姓名、有效期时长、性别、发证日期、驾驶证号、驾驶证准驾车型、有效期开始时间、地址,共 9 个关键字段信息。 - * - * - * - * - * https://console.bce.baidu.com/support/?_=1740219852952×tamp=1740325062124#/api?product=AI&project=%E6%96%87%E5%AD%97%E8%AF%86%E5%88%AB&parent=%E4%BA%A4%E9%80%9A%E5%9C%BA%E6%99%AFOCR&api=rest%2F2.0%2Focr%2Fv1%2Fdriving_license&method=post - * - * - * 驾驶证识别 - * - * - * + * 驾驶证识别(百度OCR) * + * 支持对机动车驾驶证正页及副页所有字段进行结构化识别。 + * 驾驶证正页:证号、姓名、性别、国籍、出生日期、初次领证日期、准驾车型、有效起始日期、失效日期、住址、发证单位 11个字段 + * 驾驶证副页:姓名、记录、证号、档案编号 4个字段 + * 同时支持识别交管12123 APP 发放的电子驾驶证正页,包括证号、姓名、性别、国籍、出生日期、初次领证日期、准驾车型、有效起始日期、失效日期、累积记分、状态、档案编号、生成时间、当前时间、条形码下编号 15个字段 * + * 百度文档:https://console.bce.baidu.com/support/#/api?product=AI&project=文字识别&parent=交通场景OCR&api=rest%2F2.0%2Focr%2Fv1%2Fdriving_license&method=post */ @Slf4j @RestController @RequestMapping("/driver/license") +@SuppressWarnings("unchecked") public class RecognizeDriverLicenseController extends BaseController { @Autowired - private ARecognizeDriverLicenseHandle aRecognizeDriverLicenseHandle; + private BDriverLicenseHandle bDriverLicenseHandle; - // http://localhost:8888/driver/license/recognize?side=face&imageBase64=/9j/4AAQSkZJRgABA @EbAuthentication(tencent = ApiConstants.TENCENT_AUTH) @PostMapping("/recognize") - public R recognize(@RequestBody ARecognizeDriverLicenseRequest request) { - if (!hasImage(request)) { - return R.error("imageUrl和imageBase64不能同时为空"); + public R recognize(@RequestBody DriverLicenseRecognizeRequest request) { + long start = System.currentTimeMillis(); + + if (request == null) { + return R.error("请求参数不能为空"); + } + if (isBlank(request.getImageBase64()) && isBlank(request.getImageUrl())) { + return R.error("imageBase64和imageUrl不能同时为空"); + } + log.info("驾驶证识别-参数校验耗时:{}ms", System.currentTimeMillis() - start); + + long t1 = System.currentTimeMillis(); + BDriverLicenseRequest bRequest = toBaiduRequest(request); + log.info("驾驶证识别-构建百度请求耗时:{}ms", System.currentTimeMillis() - t1); + + long t2 = System.currentTimeMillis(); + ApiR apiR = bDriverLicenseHandle.handle(bRequest); + long baiduCost = System.currentTimeMillis() - t2; + log.info("驾驶证识别-调用百度OCR接口耗时:{}ms", baiduCost); + + if (apiR == null || !apiR.isSuccess() || apiR.getData() == null) { + return R.error(thirdError(apiR)); } - ApiR aR = aRecognizeDriverLicenseHandle.handle(request); - if (!isValidAliResponse(aR)) { - return R.error(thirdError(aR)); - } - - RecognizeDriverLicenseResponseBody.RecognizeDriverLicenseResponseBodyData data = - aR.getData().getBody().getData(); + long t3 = System.currentTimeMillis(); + Map data = apiR.getData(); + Object resp; if (ApiConstants.face.equals(request.getSide())) { - if (data.getFaceResult() == null) { - return R.error("未识别到驾驶证正页信息"); + resp = toFaceResp(data); + } else { + resp = toBackResp(data); + } + log.info("驾驶证识别-结果映射耗时:{}ms, side:{}", System.currentTimeMillis() - t3, request.getSide()); + + log.info("驾驶证识别-总耗时:{}ms, 其中第三方(百度OCR):{}ms", System.currentTimeMillis() - start, baiduCost); + return R.ok().setData(resp); + } + + private BDriverLicenseRequest toBaiduRequest(DriverLicenseRecognizeRequest request) { + BDriverLicenseRequest bRequest = new BDriverLicenseRequest(); + bRequest.setImageBase64(request.getImageBase64()); + bRequest.setImageUrl(request.getImageUrl()); + + // 将 "face" 映射为百度API的 "front" + if (StringUtils.isNotBlank(request.getSide())) { + if (ApiConstants.face.equals(request.getSide())) { + bRequest.setDrivingLicenseSide("front"); + } else { + bRequest.setDrivingLicenseSide(request.getSide()); } - return R.ok().setData(toFaceResp(data.getFaceResult())); } - if (data.getBackResult() == null) { - return R.error("未识别到驾驶证副页信息"); + return bRequest; + } + + private RecognizeDriverLicenseFaceResp toFaceResp(Map data) { + RecognizeDriverLicenseFaceResp resp = new RecognizeDriverLicenseFaceResp(); + + // 驾驶证正页 11个字段 + resp.setLicenseNumber(MapUtils.getByExpr(data, "words_result.证号.words")); + resp.setName(MapUtils.getByExpr(data, "words_result.姓名.words")); + resp.setGender(MapUtils.getByExpr(data, "words_result.性别.words")); + resp.setNationality(MapUtils.getByExpr(data, "words_result.国籍.words")); + resp.setBirthDate(MapUtils.getByExpr(data, "words_result.出生日期.words")); + resp.setIssueDate(MapUtils.getByExpr(data, "words_result.初次领证日期.words")); + resp.setVehicleType(MapUtils.getByExpr(data, "words_result.准驾车型.words")); + // 电子/归一化模式返回 "有效起始日期",非归一化返回 "有效期限" + String startDate = MapUtils.getByExpr(data, "words_result.有效起始日期.words"); + if (startDate == null) { + startDate = MapUtils.getByExpr(data, "words_result.有效期限.words"); } - return R.ok().setData(toBackResp(data.getBackResult())); + resp.setStartDate(startDate); + + // 电子/归一化模式返回 "失效日期",非归一化返回 "至" + String endDate = MapUtils.getByExpr(data, "words_result.失效日期.words"); + if (endDate == null) { + endDate = MapUtils.getByExpr(data, "words_result.至.words"); + } + resp.setEndDate(endDate); + resp.setAddress(MapUtils.getByExpr(data, "words_result.住址.words")); + resp.setIssueUnit(MapUtils.getByExpr(data, "words_result.发证单位.words")); + + // 电子驾驶证正页 额外字段 + resp.setAccumulatedPoints(MapUtils.getByExpr(data, "words_result.累积记分.words")); + resp.setStatus(MapUtils.getByExpr(data, "words_result.状态.words")); + resp.setArchiveNumber(MapUtils.getByExpr(data, "words_result.档案编号.words")); + resp.setGenerateTime(MapUtils.getByExpr(data, "words_result.生成时间.words")); + resp.setCurrentTime(MapUtils.getByExpr(data, "words_result.当前时间.words")); + resp.setBarcodeNumber(MapUtils.getByExpr(data, "words_result.条形码下编号.words")); + + return resp; } - private boolean isValidAliResponse(ApiR apiR) { - return apiR != null - && apiR.isSuccess() - && apiR.getData() != null - && isSuccessStatusCode(apiR.getData().getStatusCode()) - && apiR.getData().getBody() != null - && apiR.getData().getBody().getData() != null; + private RecognizeDriverLicenseBackResp toBackResp(Map data) { + RecognizeDriverLicenseBackResp resp = new RecognizeDriverLicenseBackResp(); + resp.setName(MapUtils.getByExpr(data, "words_result.姓名.words")); + resp.setRecord(MapUtils.getByExpr(data, "words_result.记录.words")); + resp.setCardNumber(MapUtils.getByExpr(data, "words_result.证号.words")); + resp.setArchiveNumber(MapUtils.getByExpr(data, "words_result.档案编号.words")); + return resp; } - private RecognizeDriverLicenseFaceResp toFaceResp( - RecognizeDriverLicenseResponseBody.RecognizeDriverLicenseResponseBodyDataFaceResult faceResult) { - RecognizeDriverLicenseFaceResp faceResp = new RecognizeDriverLicenseFaceResp(); - faceResp.setVehicleType(faceResult.getVehicleType()); - faceResp.setIssueDate(faceResult.getIssueDate()); - faceResp.setEndDate(faceResult.getEndDate()); - faceResp.setGender(faceResult.getGender()); - faceResp.setAddress(faceResult.getAddress()); - faceResp.setStartDate(faceResult.getStartDate()); - faceResp.setLicenseNumber(faceResult.getLicenseNumber()); - faceResp.setName(faceResult.getName()); - faceResp.setIssueUnit(faceResult.getIssueUnit()); - faceResp.setNationality(faceResult.getNationality()); - faceResp.setBirthDate(faceResult.getBirthDate()); - return faceResp; - } - - private RecognizeDriverLicenseBackResp toBackResp( - RecognizeDriverLicenseResponseBody.RecognizeDriverLicenseResponseBodyDataBackResult backResult) { - RecognizeDriverLicenseBackResp backResp = new RecognizeDriverLicenseBackResp(); - backResp.setArchiveNumber(backResult.getArchiveNumber()); - backResp.setName(backResult.getName()); - backResp.setCardNumber(backResult.getCardNumber()); - backResp.setRecord(backResult.getRecord()); - return backResp; - } } diff --git a/api-web/api-interface/src/main/java/com/heyu/api/request/car/DriverLicenseRecognizeRequest.java b/api-web/api-interface/src/main/java/com/heyu/api/request/car/DriverLicenseRecognizeRequest.java new file mode 100644 index 0000000..a857e42 --- /dev/null +++ b/api-web/api-interface/src/main/java/com/heyu/api/request/car/DriverLicenseRecognizeRequest.java @@ -0,0 +1,32 @@ +package com.heyu.api.request.car; + +import lombok.Data; + +/** + * 驾驶证识别请求参数 + * + * 对外提供简洁接口,支持识别机动车驾驶证正页及副页所有字段 + */ +@Data +public class DriverLicenseRecognizeRequest { + + /** + * 图像数据,base64编码后进行urlencode,要求base64编码和urlencode后大小不超过4M + * 支持jpg/jpeg/png/bmp格式 + * 和url二选一 + */ + private String imageBase64; + + /** + * 图片完整URL,URL长度不超过1024字节 + * 和imageBase64二选一 + */ + private String imageUrl; + + /** + * face:识别驾驶证正页、电子驾驶证正页(默认) + * back:识别驾驶证副页 + */ + private String side = "face"; + +} diff --git a/api-web/api-interface/src/main/java/com/heyu/api/resp/car/RecognizeDriverLicenseFaceResp.java b/api-web/api-interface/src/main/java/com/heyu/api/resp/car/RecognizeDriverLicenseFaceResp.java index a95cdcb..b6c55d4 100644 --- a/api-web/api-interface/src/main/java/com/heyu/api/resp/car/RecognizeDriverLicenseFaceResp.java +++ b/api-web/api-interface/src/main/java/com/heyu/api/resp/car/RecognizeDriverLicenseFaceResp.java @@ -2,111 +2,65 @@ package com.heyu.api.resp.car; import lombok.Data; -/*** - * https://next.api.aliyun.com/api/ocr/2019-12-30/RecognizeDriverLicense?tab=DOC&lang=JAVA +/** + * 驾驶证识别 - 正页/电子驾驶证正页 响应 + * + * 支持识别:驾驶证正页(11字段)、电子驾驶证正页(15字段) */ @Data public class RecognizeDriverLicenseFaceResp { - - /*** - * 驾驶证准驾车型。 - * - * 示例值: - * C1 - */ - private String vehicleType; - - - /*** - * 初次发证日期。格式:YYYYMMDD,例如 19800101,即 1980 年 01 月 01 日。 - * - * 示例值: - * 20130208 - * - * - */ - private String issueDate; - - /*** - * - */ - private String endDate; - - /*** - * 性别。 - * - * 示例值: - * 男der - */ - private String gender; - - - /*** - * 地址。 - * - * 示例值: - * 江苏省徐州市铜山区棠张镇田河村1队129号 - */ - private String address; - - /*** - * - */ - private String startDate; - - - /*** - * 证号。 - * - * 示例值: - * 210288898898898888 - */ + /** 证号 */ private String licenseNumber; - - /*** - * 姓名。 - * - * 示例值: - * 张三 - */ + /** 姓名 */ private String name; + /** 性别 */ + private String gender; - /*** - * 发证单位。 - * - * 示例值: - * 江苏省徐州市公安局交通巡逻警察支队 - */ - private String issueUnit; - - - /*** - * 国籍。 - * - * 示例值: - * 中国 - */ + /** 国籍 */ private String nationality; - - /*** - * 出生日期。 - * - * 示例值: - * 1992-05-20 - */ + /** 出生日期,格式:YYYYMMDD */ private String birthDate; + /** 初次领证日期,格式:YYYYMMDD */ + private String issueDate; + /** 准驾车型,如 C1 */ + private String vehicleType; + /** 有效起始日期,格式:YYYYMMDD */ + private String startDate; + /** 失效日期,格式:YYYYMMDD */ + private String endDate; + /** 住址 */ + private String address; + /** 发证单位 */ + private String issueUnit; + // ======== 电子驾驶证正页 额外字段 ======== + /** 累积记分,如 "0分",仅电子驾驶证正页返回 */ + private String accumulatedPoints; + /** 状态,如 "正常",仅电子驾驶证正页返回 */ + private String status; + + /** 档案编号,仅电子驾驶证正页返回 */ + private String archiveNumber; + + /** 生成时间,如 "2021年09月04日",仅电子驾驶证正页返回 */ + private String generateTime; + + /** 当前时间,如 "2022年04月16日14:09:39",仅电子驾驶证正页返回 */ + private String currentTime; + + /** 条形码下编号,如 "*4360028416376*",仅电子驾驶证正页返回 */ + private String barcodeNumber; }