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;
}
}