提交修改

This commit is contained in:
quyixiao 2026-06-24 01:13:08 +08:00
parent 965d206f5a
commit c4699ab363

View File

@ -35,7 +35,7 @@ import java.util.Map;
* <h3>本服务接口</h3> * <h3>本服务接口</h3>
* <ul> * <ul>
* <li>路径{@code POST /driver/license/recognize}</li> * <li>路径{@code POST /driver/license/recognize}</li>
* <li>Content-Type{@code application/x-www-form-urlencoded}</li> * <li>Content-Type{@code application/json}{@code @RequestBody}</li>
* <li>鉴权{@link EbAuthentication}Tencent 鉴权头见项目网关配置</li> * <li>鉴权{@link EbAuthentication}Tencent 鉴权头见项目网关配置</li>
* </ul> * </ul>
* *
@ -73,32 +73,26 @@ public class RecognizeDriverLicenseController extends AbstractRecognizeControlle
@PostMapping("/recognize") @PostMapping("/recognize")
public R recognize(@RequestBody DriverLicenseRecognizeRequest request) { public R recognize(@RequestBody DriverLicenseRecognizeRequest request) {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
RecognizeContext ctx = null; // 入口处一次性解析整个请求中 ImageInputUtils.validate / resolve 仅执行一次后续主流程与 catch 块复用 ctx
ImageInputUtils.ValidationResult imageValidation = request != null ? ImageInputUtils.validate(request.getImageUrlOrBase64()) : null;
ResolvedImageInput imageInput = imageValidation != null && imageValidation.isValid() ? imageValidation.getResolved() : null;
String resolvedSide = request != null ? resolveDrivingLicenseSide(request) : null;
RecognizeContext ctx = new RecognizeContext(
resolvedSide != null ? resolvedSide : ApiConstants.front,
imageInput,
buildInputLogContext(request, imageInput, resolvedSide));
try { try {
// ---------- 步骤参数校验不调百度不扣费 ---------- // ---------- 步骤参数校验不调百度不扣费 ----------
String validateError = validateRequest(request); String validateError = validateRequest(request, imageValidation, resolvedSide, ctx);
if (validateError != null) { if (validateError != null) {
String side = request != null ? resolveDrivingLicenseSide(request) : null;
if (side == null) {
side = ApiConstants.front;
}
ResolvedImageInput validateImage = request != null
? ImageInputUtils.resolve(request.getImageUrlOrBase64()) : null;
log.info("驾驶证识别:参数检查没通过,接口仍返回成功并附带提示(还没调识别、不扣费)。{} 返回给客户:{}", log.info("驾驶证识别:参数检查没通过,接口仍返回成功并附带提示(还没调识别、不扣费)。{} 返回给客户:{}",
buildInputLogContext(request, validateImage), ctx.inputLog, abbreviate(validateError, 120));
abbreviate(validateError, 120)); return okResult(ctx.side, validateError, null);
return okResult(side, validateError, null);
} }
// 校验通过后 ctx.imageInput ctx.side 必然有效下文可直接使用
// ---------- 步骤解析影像入参 & 构建上下文 ---------- // ---------- 步骤组装百度 API 请求体 ----------
ResolvedImageInput imageInput = ImageInputUtils.resolve(request.getImageUrlOrBase64()); String content = buildRequestContent(ctx.imageInput, ctx.side);
ctx = new RecognizeContext(
resolveDrivingLicenseSide(request),
imageInput,
buildInputLogContext(request, imageInput));
// ---------- 步骤组装百度 API 请求体 ----------
String content = buildRequestContent(imageInput, ctx.side);
if (isBlank(content)) { if (isBlank(content)) {
log.error("驾驶证识别:组装请求失败,请求里没带有效图片。识别{}{}。{}", log.error("驾驶证识别:组装请求失败,请求里没带有效图片。识别{}{}。{}",
sideDesc(ctx.side), ctx.imageInput.getType().getDesc(), ctx.inputLog); sideDesc(ctx.side), ctx.imageInput.getType().getDesc(), ctx.inputLog);
@ -106,45 +100,40 @@ public class RecognizeDriverLicenseController extends AbstractRecognizeControlle
"报文组装异常", "报文组装异常",
"识别请求体在序列化后未包含任何影像载荷,平台侧无法受理本次识别", "识别请求体在序列化后未包含任何影像载荷,平台侧无法受理本次识别",
"请核对 imageUrlOrBase64 是否非空且为有效 Base64 或 HTTP(S) 链接;" "请核对 imageUrlOrBase64 是否非空且为有效 Base64 或 HTTP(S) 链接;"
+ "请求头须为 application/x-www-form-urlencoded", + "请求头须为 application/json",
"目标页面=" + sideLabel(ctx.side) + ",影像模式=" + ctx.imageInput.getType().name() locator(ctx)
), null); ), null);
} }
// ---------- 步骤调用百度平台识别 ---------- // ---------- 步骤调用百度平台识别 ----------
Map<String, Object> platformResult = callPlatform(content, ctx); Map<String, Object> platformResult = callPlatform(content, ctx);
if (platformResult == null) { if (platformResult == null) {
return okResult(ctx.side, formatHint( return okResult(ctx.side, formatHint(
"服务无回执", "服务无回执",
"识别指令已下发,但在约定时间内未收到平台可解析的 JSON 回执", "识别指令已下发,但在约定时间内未收到平台可解析的 JSON 回执",
"建议间隔 3~5 秒重试;若连续失败,请记录 traceId、调用时刻与 side 并联系技术支持排查链路", "建议间隔 3~5 秒重试;若连续失败,请记录 traceId、调用时刻与 side 并联系技术支持排查链路",
"目标页面=" + sideLabel(ctx.side) + ",影像模式=" + ctx.imageInput.getType().name() locator(ctx)
), null); ), null);
} }
// ---------- 步骤解析平台结果 根据 side 构建正页/副页响应 ---------- // ---------- 步骤解析平台结果 根据 side 构建正页/副页响应 ----------
Object data = ApiConstants.back.equals(ctx.side) Object data = ApiConstants.back.equals(ctx.side)
? buildBackResp(platformResult) ? buildBackResp(platformResult)
: buildFaceResp(platformResult); : buildFaceResp(platformResult);
String hint = resolvePlatformHint(platformResult, ctx, data); String hint = resolvePlatformHint(platformResult, ctx, data);
// ---------- 步骤日志记录 & 返回 ---------- // ---------- 步骤日志记录 & 返回 ----------
logRecognizeResult(ctx, platformResult, data, hint, start); logRecognizeResult(ctx, platformResult, data, hint, start);
return okResult(ctx.side, hint, data); return okResult(ctx.side, hint, data);
} catch (Exception e) { } catch (Exception e) {
long cost = System.currentTimeMillis() - start; long cost = System.currentTimeMillis() - start;
String side = ctx != null ? ctx.side String modeDesc = ctx.imageInput != null ? ctx.imageInput.getType().getDesc() : "影像未解析";
: (request != null ? resolveDrivingLicenseSide(request) : ApiConstants.front);
ResolvedImageInput fallbackImage = request != null
? ImageInputUtils.resolve(request.getImageUrlOrBase64()) : null;
String mode = ctx != null ? ctx.imageInput.getType().getDesc()
: (fallbackImage != null ? fallbackImage.getType().getDesc() : "未知");
log.error("驾驶证识别:程序运行出错,耗时 {} ms。识别{}{}。客户传的:{}。异常:{} - {}", log.error("驾驶证识别:程序运行出错,耗时 {} ms。识别{}{}。客户传的:{}。异常:{} - {}",
cost, sideDesc(side), mode, buildInputLogContext(request, fallbackImage), cost, sideDesc(ctx.side), modeDesc, ctx.inputLog,
e.getClass().getSimpleName(), e.getClass().getSimpleName(),
e.getMessage() != null ? e.getMessage() : "无具体说明", e); e.getMessage() != null ? e.getMessage() : "无具体说明", e);
return okResult(side, formatHint( return okResult(ctx.side, formatHint(
"运行时故障", "运行时故障",
"服务端在处理识别流程时抛出未预期异常,识别结果不可用", "服务端在处理识别流程时抛出未预期异常,识别结果不可用",
"请勿重复高频重试;请保存 traceId、异常发生时间及 side由技术支持结合堆栈进一步定位", "请勿重复高频重试;请保存 traceId、异常发生时间及 side由技术支持结合堆栈进一步定位",
@ -271,24 +260,26 @@ public class RecognizeDriverLicenseController extends AbstractRecognizeControlle
// ===================== 校验与上下文 ===================== // ===================== 校验与上下文 =====================
private String validateRequest(DriverLicenseRecognizeRequest request) { /**
* 纯逻辑校验基于入口处已完成的 imageValidation / resolvedSide 做判断自身不再触发 IO
*/
private String validateRequest(DriverLicenseRecognizeRequest request,
ImageInputUtils.ValidationResult imageValidation,
String resolvedSide,
RecognizeContext ctx) {
if (request == null) { if (request == null) {
log.info("驾驶证识别:没收到任何请求参数(表单未绑定成功),直接拒绝,未调用识别、不扣费"); log.info("驾驶证识别:没收到任何请求参数(请求体未绑定成功),直接拒绝,未调用识别、不扣费");
return formatHint( return formatHint(
"入参绑定失败", "入参绑定失败",
"控制器未接收到可绑定的表单对象,所有业务字段均为空", "控制器未接收到可绑定的请求对象,所有业务字段均为空",
"请确认使用 POST 提交Content-Type 为 application/json 或 application/x-www-form-urlencoded" "请确认使用 POST 提交Content-Type 为 application/json"
+ "字段名与接口文档一致imageUrlOrBase64 / side", + "字段名与接口文档一致imageUrlOrBase64 / side",
"绑定结果=DriverLicenseRecognizeRequest 为 null" "绑定结果=DriverLicenseRecognizeRequest 为 null"
); );
} }
ImageInputUtils.ValidationResult imageValidation = if (imageValidation != null && !imageValidation.isValid()) {
ImageInputUtils.validate(request.getImageUrlOrBase64());
if (!imageValidation.isValid()) {
log.info("驾驶证识别imageUrlOrBase64 校验未通过({}),未调用识别、不扣费。原因:{}。{}", log.info("驾驶证识别imageUrlOrBase64 校验未通过({}),未调用识别、不扣费。原因:{}。{}",
imageValidation.getCategory(), imageValidation.getCategory(), imageValidation.getReason(), ctx.inputLog);
imageValidation.getReason(),
buildInputLogContext(request, null));
return formatHint( return formatHint(
imageValidation.getCategory(), imageValidation.getCategory(),
imageValidation.getReason(), imageValidation.getReason(),
@ -297,9 +288,9 @@ public class RecognizeDriverLicenseController extends AbstractRecognizeControlle
+ (isBlank(request.getSide()) ? SIDE_DEFAULT_HINT : request.getSide().trim()) + (isBlank(request.getSide()) ? SIDE_DEFAULT_HINT : request.getSide().trim())
); );
} }
if (resolveDrivingLicenseSide(request) == null) { if (resolvedSide == null) {
log.info("驾驶证识别side 参数写错了(只支持 face/front 正页、back 副页),直接拒绝,未调用识别、不扣费。{}", log.info("驾驶证识别side 参数写错了(只支持 face/front 正页、back 副页),直接拒绝,未调用识别、不扣费。{}",
buildInputLogContext(request, ImageInputUtils.resolve(request.getImageUrlOrBase64()))); ctx.inputLog);
return formatHint( return formatHint(
"页面标识无效", "页面标识无效",
"参数 side 的取值无法映射到驾驶证正页或副页识别模式", "参数 side 的取值无法映射到驾驶证正页或副页识别模式",
@ -318,7 +309,7 @@ public class RecognizeDriverLicenseController extends AbstractRecognizeControlle
if (request == null || isBlank(request.getSide())) { if (request == null || isBlank(request.getSide())) {
return ApiConstants.front; return ApiConstants.front;
} }
String side = request.getSide().trim(); String side = request.getSide().trim().toLowerCase();
if (ApiConstants.face.equals(side) || ApiConstants.front.equals(side)) { if (ApiConstants.face.equals(side) || ApiConstants.front.equals(side)) {
return ApiConstants.front; return ApiConstants.front;
} }
@ -411,13 +402,18 @@ public class RecognizeDriverLicenseController extends AbstractRecognizeControlle
return sb.toString(); return sb.toString();
} }
/** 入参日志说明(不打印影像正文,只说类型与长度) */ /**
private String buildInputLogContext(DriverLicenseRecognizeRequest request, ResolvedImageInput imageInput) { * 入参日志说明不打印影像正文只说类型与长度
*
* @param resolvedSide 已在调用方解析过的 side无效时为 null避免本方法内重复 resolve
*/
private String buildInputLogContext(DriverLicenseRecognizeRequest request,
ResolvedImageInput imageInput,
String resolvedSide) {
if (request == null) { if (request == null) {
return "未收到请求体"; return "未收到请求体";
} }
String sideParam = isBlank(request.getSide()) ? "未传(默认按正页)" : request.getSide().trim(); String sideParam = isBlank(request.getSide()) ? "未传(默认按正页)" : request.getSide().trim();
String resolvedSide = resolveDrivingLicenseSide(request);
String targetPage = resolvedSide == null ? "side 无法识别" : sideDesc(resolvedSide); String targetPage = resolvedSide == null ? "side 无法识别" : sideDesc(resolvedSide);
String modeDesc = imageInput != null ? imageInput.getType().getDesc() : "影像未解析"; String modeDesc = imageInput != null ? imageInput.getType().getDesc() : "影像未解析";
int rawLen = imageInput != null int rawLen = imageInput != null
@ -426,4 +422,10 @@ public class RecognizeDriverLicenseController extends AbstractRecognizeControlle
return String.format("识别%sside 参数=%s%s影像原始长度 %d 字符", return String.format("识别%sside 参数=%s%s影像原始长度 %d 字符",
targetPage, sideParam, modeDesc, rawLen); targetPage, sideParam, modeDesc, rawLen);
} }
/** "目标页面=xx影像模式=xx" 拼接formatHint 的 detail 字段共用此格式。 */
private String locator(RecognizeContext ctx) {
return "目标页面=" + sideLabel(ctx.side) + ",影像模式="
+ (ctx.imageInput != null ? ctx.imageInput.getType().name() : "未解析");
}
} }