1.修改请求报错

2.base64格式测试没有通过
This commit is contained in:
jiangtd 2026-06-05 00:54:26 +08:00
parent c272282d55
commit 1af445bcd8

View File

@ -11,6 +11,7 @@ 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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@ -72,10 +73,11 @@ public class RecognizeDriverLicenseController extends BaseController {
*/
@EbAuthentication(tencent = ApiConstants.TENCENT_AUTH)
@PostMapping("/recognize")
public R recognize(DriverLicenseRecognizeRequest request) {
public R recognize(@RequestBody DriverLicenseRecognizeRequest request) {
long start = System.currentTimeMillis();
RecognizeContext ctx = null;
try {
// ---------- 步骤参数校验不调百度不扣费 ----------
String validateError = validateRequest(request);
if (validateError != null) {
log.info("驾驶证识别:参数检查没通过,直接返回错误(还没调识别、不扣费)。{} 返回给客户:{}",
@ -83,11 +85,13 @@ public class RecognizeDriverLicenseController extends BaseController {
return R.error(validateError);
}
// ---------- 步骤构建上下文side图片模式入参摘要 ----------
ctx = new RecognizeContext(
resolveDrivingLicenseSide(request),
ImageMode.of(request),
buildInputLogContext(request));
// ---------- 步骤组装百度 API 请求体 ----------
String content = buildRequestContent(request, ctx.side);
if (isBlank(content)) {
log.error("驾驶证识别:组装请求失败,请求里没带有效图片。识别{}{}。{}",
@ -101,6 +105,7 @@ public class RecognizeDriverLicenseController extends BaseController {
), null);
}
// ---------- 步骤调用百度平台识别 ----------
Map<String, Object> platformResult = callPlatform(content, ctx);
if (platformResult == null) {
return okResult(ctx.side, formatHint(
@ -111,11 +116,13 @@ public class RecognizeDriverLicenseController extends BaseController {
), null);
}
// ---------- 步骤解析平台结果 根据 side 构建正页/副页响应 ----------
Object data = ApiConstants.back.equals(ctx.side)
? buildBackResp(platformResult)
: buildFaceResp(platformResult);
String hint = resolvePlatformHint(platformResult, ctx, data);
// ---------- 步骤日志记录 & 返回 ----------
logRecognizeResult(ctx, platformResult, data, hint, start);
return okResult(ctx.side, hint, data);
@ -141,6 +148,14 @@ public class RecognizeDriverLicenseController extends BaseController {
// ===================== 流程拆分方法 =====================
/**
* 调用百度云驾驶证识别接口记录调用日志并返回平台原始响应
*
* @param content 已拼装好的 POST 请求体
* @param ctx 上下文side图片模式入参摘要
* @return 平台返回的 JSON 解析后的 Map超时/鉴权/解析失败时返回 null
* @see #requestBaidu(String, String)
*/
private Map<String, Object> callPlatform(String content, RecognizeContext ctx) {
int len = content.length();
log.info("驾驶证识别:开始调用平台识别。识别{}{},请求大小约 {} 字节。{}",
@ -154,6 +169,21 @@ public class RecognizeDriverLicenseController extends BaseController {
return result;
}
/**
* 解析平台返回结果判断是否需要向客户返回提示信息
* <p>判断顺序</p>
* <ol>
* <li>error_code 存在 平台业务拒绝配额/鉴权/影像不合规</li>
* <li>words_result 为空 平台未识别出内容</li>
* <li>响应对象所有字段均为空 平台有返回但字段映射后全为空</li>
* <li>以上均不命中 返回 null识别正常无需额外提示</li>
* </ol>
*
* @param platformResult 百度平台原始返回
* @param ctx 识别上下文
* @param data 映射后的响应对象
* @return 提示文案正常识别返回 null
*/
private String resolvePlatformHint(Map<String, Object> platformResult, RecognizeContext ctx, Object data) {
Object errorCodeObj = platformResult.get("error_code");
if (errorCodeObj != null) {
@ -201,6 +231,16 @@ public class RecognizeDriverLicenseController extends BaseController {
return null;
}
/**
* 记录本次识别的最终结果日志含耗时识别字段数量
* 区分"带提示""完全成功"两种日志级别方便后续检索
*
* @param ctx 识别上下文
* @param platformResult 平台原始返回用于提取回执摘要
* @param data 映射后的响应对象
* @param hint 提示文案可为 null
* @param start 起始时间戳 ms用于计算总耗时
*/
private void logRecognizeResult(RecognizeContext ctx, Map<String, Object> platformResult,
Object data, String hint, long start) {
long cost = System.currentTimeMillis() - start;
@ -259,6 +299,18 @@ public class RecognizeDriverLicenseController extends BaseController {
return null;
}
/**
* 解析 side 参数为标准化的正页/副页标识
* <ul>
* <li>/未传 默认正页 ({@link ApiConstants#front})</li>
* <li>face front 正页</li>
* <li>back 副页</li>
* <li>其他 {@code null}表示无效</li>
* </ul>
*
* @param request 入参对象
* @return {@link ApiConstants#front}{@link ApiConstants#back} {@code null}
*/
private String resolveDrivingLicenseSide(DriverLicenseRecognizeRequest request) {
if (request == null || isBlank(request.getSide())) {
return ApiConstants.front;
@ -273,16 +325,39 @@ public class RecognizeDriverLicenseController extends BaseController {
return null;
}
/**
* 获取 side 对应的用户可读标签带取值说明用于日志与提示文案
*
* @param side 标准化后的 sidefront / back
* @return "副页(back)" "正页(front/face)"
*/
private String sideLabel(String side) {
return ApiConstants.back.equals(side) ? "副页(back)" : "正页(front/face)";
}
/**
* 获取 side 对应的中文描述用于日志输出
*
* @param side 标准化后的 sidefront / back
* @return "驾驶证副页" "驾驶证正页"
*/
private String sideDesc(String side) {
return ApiConstants.back.equals(side) ? "驾驶证副页" : "驾驶证正页";
}
// ===================== 请求/响应构造 =====================
/**
* 构造调用百度驾驶证的 POST 请求体application/x-www-form-urlencoded 格式
* <ul>
* <li>优先使用 Base64image 参数备选 URLurl 参数</li>
* <li>始终携带固定的百度参数detect_directiondriving_license_sideunified_valid_period </li>
* </ul>
*
* @param request 客户入参
* @param drivingLicenseSide 标准化后的 sidefront / back
* @return 请求体字符串若既无 Base64 也无 URL 则返回空字符串
*/
private String buildRequestContent(DriverLicenseRecognizeRequest request, String drivingLicenseSide) {
StringBuilder sb = new StringBuilder();
if (StringUtils.isNotBlank(request.getImageBase64())) {
@ -298,6 +373,18 @@ public class RecognizeDriverLicenseController extends BaseController {
return sb.toString();
}
/**
* 统一构造接口返回体
* <ul>
* <li> data null 根据 side 自动创建对应类型的空响应对象保证字段不为 null</li>
* <li>hint 非空时追加到 R.ok() msg 为空则使用默认成功消息</li>
* </ul>
*
* @param drivingLicenseSide 标准化 side决定空响应类型
* @param hint 提示文案成功时可为 null
* @param data 识别对象可为 null
* @return 统一响应体
*/
private R okResult(String drivingLicenseSide, String hint, Object data) {
if (data == null) {
data = ApiConstants.back.equals(drivingLicenseSide)
@ -308,6 +395,14 @@ public class RecognizeDriverLicenseController extends BaseController {
return result.setData(data);
}
/**
* 将平台返回的 words_result 映射为驾驶证正页响应对象 {@link RecognizeDriverLicenseFaceResp}
* 逐字段从嵌套 Map 中提取words_result.{字段名}.words
*
* @param data 百度平台返回的原始结果
* @return 正页响应对象
* @see #getWords(Map, String)
*/
private RecognizeDriverLicenseFaceResp buildFaceResp(Map<String, Object> data) {
RecognizeDriverLicenseFaceResp resp = new RecognizeDriverLicenseFaceResp();
resp.setLicenseNumber(getWords(data, "证号"));
@ -330,6 +425,13 @@ public class RecognizeDriverLicenseController extends BaseController {
return resp;
}
/**
* 将平台返回的 words_result 映射为驾驶证副页响应对象 {@link RecognizeDriverLicenseBackResp}
* 副页仅包含 姓名记录证号档案编号 四个字段
*
* @param data 百度平台返回的原始结果
* @return 副页响应对象
*/
private RecognizeDriverLicenseBackResp buildBackResp(Map<String, Object> data) {
RecognizeDriverLicenseBackResp resp = new RecognizeDriverLicenseBackResp();
resp.setName(getWords(data, "姓名"));
@ -339,10 +441,26 @@ public class RecognizeDriverLicenseController extends BaseController {
return resp;
}
/**
* Baidu 返回结果的 words_result 中提取指定字段的文本
* <p>访问路径words_result.{field}.words</p>
*
* @param data Baidu 返回的原始 Map
* @param field 字段中文名 "证号""姓名"
* @return 识别的文字内容未命中或为空时返回 null
*/
private String getWords(Map<String, Object> data, String field) {
return MapUtils.getByExpr(data, "words_result." + field + ".words");
}
/**
* 取第一个非空字符串均空时返回 null
* 用于处理百度返回中字段名不一致的场景如有效期限可能叫"有效起始日期"也可能叫"有效期限"
*
* @param first 优先值
* @param second 备选值
* @return 非空字符串或 null
*/
private String firstNonBlank(String first, String second) {
return StringUtils.isNotBlank(first) ? first : second;
}
@ -384,10 +502,31 @@ public class RecognizeDriverLicenseController extends BaseController {
return count;
}
/**
* 判断响应对象的所有 String 字段是否均为空
* 快速检查是否完全没有识出任何字段用于决定是否追加"字段映射为空"提示
*
* @param data 正页或副页响应对象
* @return 所有 String 字段均空则返回 true
* @see #countNonBlankStringFields(Object)
*/
private static boolean isAllStringFieldsBlank(Object data) {
return countNonBlankStringFields(data) == 0;
}
/**
* 格式化为统一的提示文案由四部分拼接而成
* <pre>
* 驾驶证识别·{category}{reason}定位信息{detail}处置指引{suggestion}
* </pre>
* detail 可空空时跳过"定位信息"部分
*
* @param category 问题分类"影像不合规""鉴权失效"
* @param reason 问题原因描述
* @param suggestion 处置建议
* @param detail 定位辅助信息可为空
* @return 格式化的提示字符串
*/
private String formatHint(String category, String reason, String suggestion, String detail) {
StringBuilder sb = new StringBuilder();
sb.append("【驾驶证识别·").append(category).append("").append(reason);
@ -398,6 +537,14 @@ public class RecognizeDriverLicenseController extends BaseController {
return sb.toString();
}
/**
* 截断字符串至指定长度超出部分以 "..." 替代
* 防止日志或提示文案中出现超长文本 Base64 片段详细堆栈
*
* @param text 原始字符串
* @param maxLen 最大保留字符数
* @return 截断后的字符串null 或未超长则返回原值
*/
private String abbreviate(String text, int maxLen) {
if (text == null || text.length() <= maxLen) {
return text;
@ -442,6 +589,13 @@ public class RecognizeDriverLicenseController extends BaseController {
logId != null ? logId : "");
}
/**
* 获取字符串长度null 安全
* 用于 buildInputLogContext 中简述客户传入的 Base64 / URL 大小避免打印全文
*
* @param value 字符串
* @return 长度null 返回 0
*/
private int textLength(String value) {
return value == null ? 0 : value.length();
}