From 88bfc16289c9aef43a7e7387a4312d033dc4af0a Mon Sep 17 00:00:00 2001 From: quyixiao <2621048238@qq.com> Date: Thu, 4 Jun 2026 01:40:28 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../car/RecognizeDriverLicenseController.java | 518 +++++++++++++++++- 1 file changed, 503 insertions(+), 15 deletions(-) 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 02b6c42..313dfd1 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,42 +1,530 @@ package com.heyu.api.controller.car; -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.controller.ocr.BaiduOcrResult; import com.heyu.api.data.annotation.EbAuthentication; import com.heyu.api.data.constants.ApiConstants; +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 org.springframework.beans.factory.annotation.Autowired; +import com.heyu.api.resp.car.RecognizeDriverLicenseBackResp; +import com.heyu.api.resp.car.RecognizeDriverLicenseFaceResp; +import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.HashMap; +import java.util.Map; + +/** + * 驾驶证识别控制器 + *

+ * 对机动车驾驶证正页、副页及电子驾驶证正页进行结构化识别。 + * 校验、请求组装、结果封装及异常处理均在 Controller 层完成。 + *

+ *

+ * 接口路径:POST /driver/license/recognize + *

+ *

+ * 返回约定:进入平台识别链路(组装请求体之后)一律 {@code R.ok()};若有异常则将详细提示写入 {@code msg}。 + * 仅入参校验失败时返回 {@code R.error()}(未调用平台识别、不计费)。 + *

+ * + * @author heyu + * @since 1.0.0 + */ +@Slf4j @RestController @RequestMapping("/driver/license") public class RecognizeDriverLicenseController extends BaseController { - @Autowired - private BDriverLicenseHandle bDriverLicenseHandle; + private static final String DRIVING_LICENSE_URI = "/rest/2.0/ocr/v1/driving_license"; + + /** 平台错误码 → 对外原因说明(每条语义独立,便于客服判读) */ + private static final Map PLATFORM_ERROR_HINTS = new HashMap<>(); + + static { + PLATFORM_ERROR_HINTS.put("17", "本接口当日累计调用次数已触达平台日配额上限,本次识别请求被拒绝"); + PLATFORM_ERROR_HINTS.put("18", "单位时间请求过于密集,已触发平台 QPS 限流,请拉长调用间隔后重试"); + PLATFORM_ERROR_HINTS.put("19", "平台侧总调用配额已耗尽,需运营侧扩容或次日再试"); + PLATFORM_ERROR_HINTS.put("100", "提交的表单字段组合不符合驾驶证识别接口契约,请对照文档核对字段名与取值"); + PLATFORM_ERROR_HINTS.put("110", "平台鉴权凭证校验未通过,属服务端配置异常,需运维刷新令牌"); + PLATFORM_ERROR_HINTS.put("111", "平台鉴权凭证已超过有效期,属服务端配置异常,需运维重新签发"); + PLATFORM_ERROR_HINTS.put("216100", "存在非法或与接口不匹配的参数项"); + PLATFORM_ERROR_HINTS.put("216101", "影像入参缺失:未提供可用的 image 或 url 字段"); + PLATFORM_ERROR_HINTS.put("216103", "单个参数字段长度超限(常见于 url 过长或 base64 体积过大)"); + PLATFORM_ERROR_HINTS.put("216110", "当前应用未开通驾驶证识别能力或授权范围不包含本接口"); + PLATFORM_ERROR_HINTS.put("216200", "解码后的图片数据为空,上传环节可能未完成或内容损坏"); + PLATFORM_ERROR_HINTS.put("216201", "图片编码格式不在允许列表,仅支持 jpg、jpeg、png、bmp"); + PLATFORM_ERROR_HINTS.put("216202", "像素尺寸或编码后体积不满足规范(边长 15~4096px,编码后≤4M)"); + PLATFORM_ERROR_HINTS.put("216203", "引擎无法从当前画面中稳定提取驾驶证文本,多见于模糊、过曝或非证件照"); + PLATFORM_ERROR_HINTS.put("216630", "证件版面识别失败,建议重拍并保证四角完整、无遮挡"); + PLATFORM_ERROR_HINTS.put("282000", "平台识别引擎内部异常,属短暂性故障,可间隔数秒后重试"); + } @EbAuthentication(tencent = ApiConstants.TENCENT_AUTH) @PostMapping("/recognize") public R recognize(DriverLicenseRecognizeRequest request) { - return BaiduOcrResult.raw(bDriverLicenseHandle.handle(toBaiduRequest(request))); + long start = System.currentTimeMillis(); + try { + String validateError = validateRequest(request); + if (validateError != null) { + log.info("[驾驶证识别] 阶段=入参校验 结论=拦截 说明=请求未进入识别链路(不计费) " + + "入参摘要={} 对外提示摘要={}", + buildInputLogContext(request), abbreviate(validateError, 120)); + return R.error(validateError); + } + + String drivingLicenseSide = resolveDrivingLicenseSide(request); + String imageMode = resolveImageMode(request); + String inputContext = buildInputLogContext(request); + + String content = buildRequestContent(request, drivingLicenseSide); + if (isBlank(content)) { + log.error("[驾驶证识别] 阶段=报文组装 结论=失败 目标页面={} 页面标签={} 影像模式={} " + + "请求体字节长度=0 入参摘要={} 原因=未写入有效image或url节点", + drivingLicenseSide, sideLabel(drivingLicenseSide), imageMode, inputContext); + return okResult(drivingLicenseSide, formatHint( + "报文组装异常", + "识别请求体在序列化后未包含任何影像载荷,平台侧无法受理本次识别", + "请核对 imageBase64 是否已 urlencode、imageUrl 是否非空字符串;" + + "请求头须为 application/x-www-form-urlencoded", + "目标页面=" + sideLabel(drivingLicenseSide) + ",影像模式=" + imageMode + ), null); + } + + int requestBodyLength = content.length(); + log.info("[驾驶证识别] 阶段=平台调用 开始 目标页面={} 页面标签={} 影像模式={} " + + "请求体字节长度={} 入参摘要={}", + drivingLicenseSide, sideLabel(drivingLicenseSide), imageMode, requestBodyLength, inputContext); + Map platformResult = requestBaidu(DRIVING_LICENSE_URI, content); + if (platformResult == null) { + log.error("[驾驶证识别] 阶段=平台调用 结论=无回执 目标页面={} 页面标签={} 影像模式={} " + + "请求体字节长度={} 入参摘要={} 可能原因=链路超时/鉴权失败/响应不可解析", + drivingLicenseSide, sideLabel(drivingLicenseSide), imageMode, requestBodyLength, inputContext); + return okResult(drivingLicenseSide, formatHint( + "服务无回执", + "识别指令已下发,但在约定时间内未收到平台可解析的 JSON 回执", + "建议间隔 3~5 秒重试;若连续失败,请记录 traceId、调用时刻与 side 并联系技术支持排查链路", + "目标页面=" + sideLabel(drivingLicenseSide) + ",影像模式=" + imageMode + ), null); + } + + String hint = resolvePlatformHint(platformResult, drivingLicenseSide, inputContext, imageMode); + Object data = ApiConstants.back.equals(drivingLicenseSide) + ? buildBackResp(platformResult) + : buildFaceResp(platformResult); + + if (hint == null && isRecognizeDataEmpty(data)) { + log.info("[驾驶证识别] 阶段=字段映射 结论=全空 目标页面={} 页面标签={} 影像模式={} " + + "入参摘要={} 平台回执摘要={} 已映射字段数=0 说明=回执结构正常但未抽取到任何业务字段", + drivingLicenseSide, sideLabel(drivingLicenseSide), imageMode, + inputContext, buildPlatformReceiptSummary(platformResult)); + hint = formatHint( + "字段映射为空", + "平台回执已通过结构校验,但证号、姓名、准驾车型等结构化字段均未命中", + ApiConstants.back.equals(drivingLicenseSide) + ? "副页识别请确认 side=back,且画面包含「记录」「档案编号」等区域;避免裁剪或反光" + : "正页识别请确认 side=face/front,画面须包含完整证面;电子驾驶证需保证截图清晰", + "目标页面=" + sideLabel(drivingLicenseSide) + ",已映射字段数=0" + ); + } + + long cost = System.currentTimeMillis() - start; + int mappedFieldCount = countMappedFields(data); + if (StringUtils.isNotBlank(hint)) { + log.info("[驾驶证识别] 阶段=请求结束 结论=成功(含业务提示) 目标页面={} 页面标签={} 影像模式={} " + + "耗时={}ms 已映射字段数={} 入参摘要={} 平台回执摘要={} 对外提示摘要={}", + drivingLicenseSide, sideLabel(drivingLicenseSide), imageMode, cost, mappedFieldCount, + inputContext, buildPlatformReceiptSummary(platformResult), abbreviate(hint, 120)); + } else { + log.info("[驾驶证识别] 阶段=请求结束 结论=成功 目标页面={} 页面标签={} 影像模式={} " + + "耗时={}ms 已映射字段数={} 入参摘要={} 平台回执摘要={}", + drivingLicenseSide, sideLabel(drivingLicenseSide), imageMode, cost, mappedFieldCount, + inputContext, buildPlatformReceiptSummary(platformResult)); + } + return okResult(drivingLicenseSide, hint, data); + + } catch (Exception e) { + long cost = System.currentTimeMillis() - start; + String side = request != null ? resolveDrivingLicenseSide(request) : ApiConstants.front; + log.error("[驾驶证识别] 阶段=运行时 结论=未捕获异常 耗时={}ms 目标页面={} 页面标签={} 影像模式={} " + + "入参摘要={} 异常类型={} 异常信息={}", + cost, side, sideLabel(side), + request != null ? resolveImageMode(request) : "未知", + buildInputLogContext(request), + e.getClass().getName(), + e.getMessage() != null ? e.getMessage() : "无", e); + return okResult(side, formatHint( + "运行时故障", + "服务端在处理识别流程时抛出未预期异常,识别结果不可用", + "请勿重复高频重试;请保存 traceId、异常发生时间及 side,由技术支持结合堆栈进一步定位", + "异常类型=" + e.getClass().getSimpleName() + + (e.getMessage() != null ? ",摘要=" + e.getMessage() : "") + ), null); + } } - private BDriverLicenseRequest toBaiduRequest(DriverLicenseRecognizeRequest request) { - BDriverLicenseRequest bRequest = new BDriverLicenseRequest(); + private String validateRequest(DriverLicenseRecognizeRequest request) { if (request == null) { - return bRequest; + log.info("[驾驶证识别] 阶段=入参校验 结论=拒绝 原因=Spring未绑定到请求Bean " + + "入参摘要=request=null 计费标记=未调用平台"); + return formatHint( + "入参绑定失败", + "控制器未接收到可绑定的表单对象,所有业务字段均为空", + "请确认使用 POST 提交;Content-Type 为 application/x-www-form-urlencoded;" + + "字段名与接口文档一致(imageBase64 / imageUrl / side)", + "绑定结果=DriverLicenseRecognizeRequest 为 null" + ); } - bRequest.setImageBase64(request.getImageBase64()); - bRequest.setImageUrl(request.getImageUrl()); - if (StringUtils.isNotBlank(request.getSide())) { - bRequest.setDrivingLicenseSide(ApiConstants.face.equals(request.getSide()) ? ApiConstants.front : request.getSide()); + if (isBlank(request.getImageBase64()) && isBlank(request.getImageUrl())) { + log.info("[驾驶证识别] 阶段=入参校验 结论=拒绝 原因=影像双字段皆空 入参摘要={} 计费标记=未调用平台", + buildInputLogContext(request)); + return formatHint( + "影像源缺失", + "imageBase64 与 imageUrl 均未提供,识别引擎没有可处理的图像输入", + "任选其一:① imageBase64 传 jpg/jpeg/png/bmp 的 base64(urlencode 后≤4M);" + + "② imageUrl 传可公网直连的 HTTPS/HTTP 地址(≤1024 字符,关闭防盗链)", + "side=" + (isBlank(request.getSide()) ? "未传(将按正页处理)" : request.getSide().trim()) + ); } - return bRequest; + if (StringUtils.isNotBlank(request.getImageBase64()) && StringUtils.isNotBlank(request.getImageUrl())) { + log.info("[驾驶证识别] 阶段=入参兼容 说明=Base64与URL同时存在,按规范丢弃URL仅保留Base64流 入参摘要={}", + buildInputLogContext(request)); + } + if (resolveDrivingLicenseSide(request) == null) { + log.info("[驾驶证识别] 阶段=入参校验 结论=拒绝 原因=side非法 入参摘要={} 计费标记=未调用平台", + buildInputLogContext(request)); + return formatHint( + "页面标识无效", + "参数 side 的取值无法映射到驾驶证正页或副页识别模式", + "正页(含纸质正页、电子驾驶证正页)请传 face 或 front;" + + "副页(含实习记录等)请传 back", + "当前 side=" + request.getSide() + ",合法取值=face|front|back" + ); + } + return null; + } + + private String resolveImageMode(DriverLicenseRecognizeRequest request) { + if (request == null) { + return "未知"; + } + if (StringUtils.isNotBlank(request.getImageBase64())) { + return StringUtils.isNotBlank(request.getImageUrl()) ? "Base64优先(已忽略URL)" : "Base64"; + } + if (StringUtils.isNotBlank(request.getImageUrl())) { + return "URL"; + } + return "无"; + } + + private String resolveDrivingLicenseSide(DriverLicenseRecognizeRequest request) { + if (request == null || isBlank(request.getSide())) { + return ApiConstants.front; + } + String side = request.getSide().trim(); + if (ApiConstants.face.equals(side) || ApiConstants.front.equals(side)) { + return ApiConstants.front; + } + if (ApiConstants.back.equals(side)) { + return ApiConstants.back; + } + return null; + } + + private String sideLabel(String drivingLicenseSide) { + return ApiConstants.back.equals(drivingLicenseSide) ? "副页(back)" : "正页(front/face)"; + } + + private String buildRequestContent(DriverLicenseRecognizeRequest request, String drivingLicenseSide) { + StringBuilder sb = new StringBuilder(); + if (StringUtils.isNotBlank(request.getImageBase64())) { + sb.append("&image=").append(request.getImageBase64()); + } else if (StringUtils.isNotBlank(request.getImageUrl())) { + sb.append("&url=").append(request.getImageUrl()); + } + sb.append("&detect_direction=false"); + sb.append("&driving_license_side=").append(drivingLicenseSide); + sb.append("&unified_valid_period=false"); + sb.append("&quality_warn=false"); + sb.append("&risk_warn=false"); + return sb.toString(); + } + + private R okResult(String drivingLicenseSide, String hint, Object data) { + if (data == null) { + data = ApiConstants.back.equals(drivingLicenseSide) + ? new RecognizeDriverLicenseBackResp() + : new RecognizeDriverLicenseFaceResp(); + } + R result = StringUtils.isNotBlank(hint) ? R.ok(hint) : R.ok(); + return result.setData(data); + } + + private String resolvePlatformHint(Map platformResult, String drivingLicenseSide, + String inputContext, String imageMode) { + Object errorCodeObj = platformResult.get("error_code"); + if (errorCodeObj != null) { + String errorCode = String.valueOf(errorCodeObj); + Object errorMsgObj = platformResult.get("error_msg"); + String errorMsg = errorMsgObj != null ? errorMsgObj.toString() : "平台未返回文字描述"; + String knownHint = PLATFORM_ERROR_HINTS.get(errorCode); + String category = resolveErrorCategory(errorCode); + log.error("[驾驶证识别] 阶段=平台回执 结论=业务拒绝 目标页面={} 页面标签={} 影像模式={} " + + "错误分类={} 错误码={} 错误描述={} 是否命中本地错误码说明={} " + + "入参摘要={} 平台回执摘要={}", + drivingLicenseSide, sideLabel(drivingLicenseSide), imageMode, + category, errorCode, errorMsg, knownHint != null, + inputContext, buildPlatformReceiptSummary(platformResult)); + return formatHint( + category, + knownHint != null ? knownHint : "平台返回了未在本地维护的错误码,需结合错误描述人工判读", + buildPlatformErrorSuggestion(errorCode, drivingLicenseSide), + "错误码=" + errorCode + ",错误描述=" + errorMsg + ",目标页面=" + sideLabel(drivingLicenseSide) + ); + } + if (isWordsResultEmpty(platformResult)) { + log.info("[驾驶证识别] 阶段=结果解析 结论=空集 目标页面={} 页面标签={} 影像模式={} " + + "入参摘要={} 平台回执摘要={} 说明=回执中不存在可遍历的结构化结果节点", + drivingLicenseSide, sideLabel(drivingLicenseSide), imageMode, + inputContext, buildPlatformReceiptSummary(platformResult)); + return formatHint( + "结构化结果缺失", + "平台回执未包含可解析的识别结果集合,无法进入字段映射环节", + "优先排查:① 上传内容是否为驾驶证对应页面;② side 是否与实物一致;" + + "③ 使用 URL 时确保平台抓取节点可访问且无 403/302 拦截", + "目标页面=" + sideLabel(drivingLicenseSide) + ",解析状态=结果集为空" + ); + } + return null; + } + + private String resolveErrorCategory(String errorCode) { + if ("17".equals(errorCode)) { + return "日配额耗尽"; + } + if ("18".equals(errorCode)) { + return "并发限流"; + } + if ("19".equals(errorCode)) { + return "总配额耗尽"; + } + if ("110".equals(errorCode) || "111".equals(errorCode)) { + return "鉴权失效"; + } + if (errorCode.startsWith("2162")) { + return "影像不合规"; + } + if ("282000".equals(errorCode)) { + return "引擎内部错误"; + } + return "平台业务拒绝"; + } + + private String buildPlatformErrorSuggestion(String errorCode, String drivingLicenseSide) { + if ("17".equals(errorCode)) { + return "请安排次日再试,或联系运营提升日调用配额"; + } + if ("18".equals(errorCode)) { + return "请在客户端增加限流/退避策略,避免瞬时并发过高"; + } + if ("19".equals(errorCode)) { + return "请联系运营确认平台总配额与续费状态"; + } + if ("110".equals(errorCode)) { + return "属服务端鉴权配置问题,需技术支持检查平台密钥与令牌缓存"; + } + if ("111".equals(errorCode)) { + return "属服务端令牌过期,需技术支持重新获取并刷新缓存"; + } + if ("216200".equals(errorCode)) { + return "请重新上传图片,确认 base64 未截断、未混入换行或非法字符"; + } + if ("216201".equals(errorCode)) { + return "请将图片转为 jpg/jpeg/png/bmp 之一后重新编码上传"; + } + if ("216202".equals(errorCode)) { + return "请压缩或裁剪图片,保证最长边≤4096px、最短边≥15px,且 urlencode 后≤4M"; + } + if ("216203".equals(errorCode)) { + return "请在自然光下重拍,确保证面占画面主体且文字可辨;side=" + sideLabel(drivingLicenseSide); + } + if ("216630".equals(errorCode)) { + return "请平铺证件拍摄,避免手指遮挡关键字段;副页识别务必传 side=back"; + } + if ("282000".equals(errorCode)) { + return "短暂性故障,建议 5~10 秒后单次重试,不宜连续轰炸接口"; + } + return "请依据错误描述调整入参或影像后重试;持续失败请附带 traceId 联系技术支持"; + } + + @SuppressWarnings("unchecked") + private boolean isWordsResultEmpty(Map platformResult) { + Object wordsResult = platformResult.get("words_result"); + if (wordsResult == null) { + return true; + } + if (wordsResult instanceof Map) { + return ((Map) wordsResult).isEmpty(); + } + return false; + } + + private boolean isRecognizeDataEmpty(Object data) { + if (data instanceof RecognizeDriverLicenseFaceResp) { + RecognizeDriverLicenseFaceResp r = (RecognizeDriverLicenseFaceResp) data; + return isBlank(r.getLicenseNumber()) && isBlank(r.getName()) && isBlank(r.getGender()) + && isBlank(r.getNationality()) && isBlank(r.getBirthDate()) && isBlank(r.getIssueDate()) + && isBlank(r.getVehicleType()) && isBlank(r.getAddress()) && isBlank(r.getIssueUnit()) + && isBlank(r.getStartDate()) && isBlank(r.getEndDate()) && isBlank(r.getAccumulatedPoints()) + && isBlank(r.getStatus()) && isBlank(r.getArchiveNumber()) && isBlank(r.getGenerateTime()) + && isBlank(r.getCurrentTime()) && isBlank(r.getBarcodeNumber()); + } + if (data instanceof RecognizeDriverLicenseBackResp) { + RecognizeDriverLicenseBackResp r = (RecognizeDriverLicenseBackResp) data; + return isBlank(r.getName()) && isBlank(r.getRecord()) && isBlank(r.getCardNumber()) + && isBlank(r.getArchiveNumber()); + } + return true; + } + + private String formatHint(String category, String reason, String suggestion, String detail) { + StringBuilder sb = new StringBuilder(); + sb.append("【驾驶证识别·").append(category).append("】").append(reason); + if (StringUtils.isNotBlank(detail)) { + sb.append("|定位信息:").append(detail); + } + sb.append("|处置指引:").append(suggestion); + return sb.toString(); + } + + private String abbreviate(String text, int maxLen) { + if (text == null || text.length() <= maxLen) { + return text; + } + return text.substring(0, maxLen) + "..."; + } + + /** + * 入参日志摘要(不打印 base64 正文,仅长度与 url 长度) + */ + private String buildInputLogContext(DriverLicenseRecognizeRequest request) { + if (request == null) { + return "request=null"; + } + String sideParam = isBlank(request.getSide()) ? "未传" : request.getSide().trim(); + String resolvedSide = resolveDrivingLicenseSide(request); + String targetPage = resolvedSide == null ? "无法解析" : sideLabel(resolvedSide); + return "side入参=" + sideParam + + ",目标页面=" + targetPage + + ",影像模式=" + resolveImageMode(request) + + ",base64长度=" + textLength(request.getImageBase64()) + + ",url长度=" + textLength(request.getImageUrl()) + + ",url是否为空=" + isBlank(request.getImageUrl()); + } + + /** + * 平台回执日志摘要(便于失败排查) + */ + private String buildPlatformReceiptSummary(Map platformResult) { + if (platformResult == null) { + return "platformResult=null"; + } + StringBuilder sb = new StringBuilder(); + sb.append("error_code=").append(platformResult.get("error_code")); + sb.append(",error_msg=").append(platformResult.get("error_msg")); + sb.append(",log_id=").append(platformResult.get("log_id")); + sb.append(",words_result_num=").append(platformResult.get("words_result_num")); + sb.append(",direction=").append(platformResult.get("direction")); + Object wordsResult = platformResult.get("words_result"); + if (wordsResult instanceof Map) { + sb.append(",结果字段数=").append(((Map) wordsResult).size()); + } else { + sb.append(",结果字段数=不可解析"); + } + return sb.toString(); + } + + private int textLength(String value) { + return value == null ? 0 : value.length(); + } + + private int countMappedFields(Object data) { + if (data instanceof RecognizeDriverLicenseFaceResp) { + RecognizeDriverLicenseFaceResp r = (RecognizeDriverLicenseFaceResp) data; + int count = 0; + if (StringUtils.isNotBlank(r.getLicenseNumber())) count++; + if (StringUtils.isNotBlank(r.getName())) count++; + if (StringUtils.isNotBlank(r.getGender())) count++; + if (StringUtils.isNotBlank(r.getNationality())) count++; + if (StringUtils.isNotBlank(r.getBirthDate())) count++; + if (StringUtils.isNotBlank(r.getIssueDate())) count++; + if (StringUtils.isNotBlank(r.getVehicleType())) count++; + if (StringUtils.isNotBlank(r.getAddress())) count++; + if (StringUtils.isNotBlank(r.getIssueUnit())) count++; + if (StringUtils.isNotBlank(r.getStartDate())) count++; + if (StringUtils.isNotBlank(r.getEndDate())) count++; + if (StringUtils.isNotBlank(r.getAccumulatedPoints())) count++; + if (StringUtils.isNotBlank(r.getStatus())) count++; + if (StringUtils.isNotBlank(r.getArchiveNumber())) count++; + if (StringUtils.isNotBlank(r.getGenerateTime())) count++; + if (StringUtils.isNotBlank(r.getCurrentTime())) count++; + if (StringUtils.isNotBlank(r.getBarcodeNumber())) count++; + return count; + } + if (data instanceof RecognizeDriverLicenseBackResp) { + RecognizeDriverLicenseBackResp r = (RecognizeDriverLicenseBackResp) data; + int count = 0; + if (StringUtils.isNotBlank(r.getName())) count++; + if (StringUtils.isNotBlank(r.getRecord())) count++; + if (StringUtils.isNotBlank(r.getCardNumber())) count++; + if (StringUtils.isNotBlank(r.getArchiveNumber())) count++; + return count; + } + return 0; + } + + private RecognizeDriverLicenseFaceResp buildFaceResp(Map data) { + RecognizeDriverLicenseFaceResp resp = new RecognizeDriverLicenseFaceResp(); + resp.setLicenseNumber(getWords(data, "证号")); + resp.setName(getWords(data, "姓名")); + resp.setGender(getWords(data, "性别")); + resp.setNationality(getWords(data, "国籍")); + resp.setBirthDate(getWords(data, "出生日期")); + resp.setIssueDate(getWords(data, "初次领证日期")); + resp.setVehicleType(getWords(data, "准驾车型")); + resp.setAddress(getWords(data, "住址")); + resp.setIssueUnit(getWords(data, "发证单位")); + resp.setStartDate(firstNonBlank(getWords(data, "有效起始日期"), getWords(data, "有效期限"))); + resp.setEndDate(firstNonBlank(getWords(data, "失效日期"), getWords(data, "至"))); + resp.setAccumulatedPoints(getWords(data, "累积记分")); + resp.setStatus(getWords(data, "状态")); + resp.setArchiveNumber(getWords(data, "档案编号")); + resp.setGenerateTime(getWords(data, "生成时间")); + resp.setCurrentTime(getWords(data, "当前时间")); + resp.setBarcodeNumber(getWords(data, "条形码下编号")); + return resp; + } + + private RecognizeDriverLicenseBackResp buildBackResp(Map data) { + RecognizeDriverLicenseBackResp resp = new RecognizeDriverLicenseBackResp(); + resp.setName(getWords(data, "姓名")); + resp.setRecord(getWords(data, "记录")); + resp.setCardNumber(getWords(data, "证号")); + resp.setArchiveNumber(getWords(data, "档案编号")); + return resp; + } + + private String getWords(Map data, String field) { + return MapUtils.getByExpr(data, "words_result." + field + ".words"); + } + + private String firstNonBlank(String first, String second) { + if (StringUtils.isNotBlank(first)) { + return first; + } + return second; } }