✨ 小程序证件 OCR 薄适配层,含类型映射
小程序按既有契约 POST {imageUrl, type, side?} 到 /driver/auth/ocr,
后端转调已有的百度 OCR 能力(recognizeUrl)。
映射的必要性在于两侧命名对「行驶证」和「驾驶证」的用词正好相反:
驾驶证 —— 小程序传 driver,后端 OCR 用 driving_license
行驶证 —— 小程序传 vehicle,后端 OCR 用 vehicle_license
把入参直接透传下游,调用方不会收到任何错误,只会拿到另一种证件的字段,
问题要到人工核对时才发现。故用显式白名单逐一对应。
采用白名单而非「透传 + 兜底为通用识别」,是因为从业资格证没有对应的百度接口:
静默降级成通用文字识别会让前端以为识别成功,实际拿到一堆通用文本。
该类型明确报错并提示手动填写。
正反面:小程序目前不传 side,按 front 处理(正页是常用场景)。
10 个映射用例覆盖五个小程序类型、后端既有命名、大小写/分隔符变体、
未知类型、空值、不支持类型与 side 归一化。
全模块 58 个测试通过:mvn -o test -pl blade-service/blade-transport -am
This commit is contained in:
+20
@@ -31,10 +31,15 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.boot.ctrl.BladeController;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.transport.ocr.constant.BaiduOcrType;
|
||||
import org.springblade.transport.ocr.service.IBaiduOcrService;
|
||||
import org.springblade.transport.pojo.dto.OcrRecognizeRequest;
|
||||
import org.springblade.transport.pojo.dto.PersonCertSubmitRequest;
|
||||
import org.springblade.transport.pojo.dto.VehicleCertSubmitRequest;
|
||||
import org.springblade.transport.pojo.vo.BaiduOcrResultVO;
|
||||
import org.springblade.transport.pojo.vo.TransportCertRecordVO;
|
||||
import org.springblade.transport.service.IDriverCertService;
|
||||
import org.springblade.transport.support.OcrTypeMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
@@ -61,6 +66,7 @@ import java.util.List;
|
||||
public class DriverAuthController extends BladeController {
|
||||
|
||||
private final IDriverCertService driverCertService;
|
||||
private final IBaiduOcrService baiduOcrService;
|
||||
|
||||
// ------------------------------------------------------------ 个人认证
|
||||
|
||||
@@ -118,4 +124,18 @@ public class DriverAuthController extends BladeController {
|
||||
return R.data(driverCertService.getRecord(id));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------ OCR
|
||||
|
||||
@PostMapping("/ocr")
|
||||
@ApiOperationSupport(order = 8)
|
||||
@Operation(summary = "证件OCR识别",
|
||||
description = "按小程序既有契约收 {imageUrl, type, side?},转调百度OCR。"
|
||||
+ "type 用小程序命名:idcard-身份证,driver-驾驶证,vehicle-行驶证,road_transport-道路运输证")
|
||||
public R<BaiduOcrResultVO> ocr(@RequestBody OcrRecognizeRequest request) {
|
||||
// 两侧对「驾驶证 / 行驶证」的用词正好相反,故必须经显式映射,不能把入参直接透传下游
|
||||
BaiduOcrType ocrType = OcrTypeMapping.toBaiduType(request.getType());
|
||||
String side = OcrTypeMapping.normalizeSide(request.getSide());
|
||||
return R.data(baiduOcrService.recognizeUrl(ocrType, side, request.getImageUrl()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.transport.support;
|
||||
|
||||
import org.springblade.transport.ocr.constant.BaiduOcrType;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 证件类型映射:小程序命名 → 百度 OCR 证件类型
|
||||
* <p>
|
||||
* ⚠️ 两侧命名对「行驶证」和「驾驶证」的用词正好相反,是本类存在的主要理由:
|
||||
* <table border="1">
|
||||
* <caption>命名对照</caption>
|
||||
* <tr><th>证件</th><th>小程序传</th><th>后端 OCR 用</th></tr>
|
||||
* <tr><td>驾驶证</td><td>{@code driver}</td><td>{@code driving_license}</td></tr>
|
||||
* <tr><td>行驶证</td><td>{@code vehicle}</td><td>{@code vehicle_license}</td></tr>
|
||||
* </table>
|
||||
* 若把 {@code vehicle} 直接透传给百度按 {@code driving_license} 处理,或反之,
|
||||
* 调用方不会收到任何错误——拿到的是另一种证件的字段,问题只在人工核对时才发现。
|
||||
* 故此处用显式白名单逐一对应,而不是把请求参数直接丢给下游。
|
||||
* <p>
|
||||
* 采用白名单而非「透传 + 兜底为通用识别」还有一个原因:
|
||||
* 从业资格证没有对应的百度接口,静默降级成通用文字识别会让前端以为识别成功。
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public final class OcrTypeMapping {
|
||||
|
||||
/**
|
||||
* 允许的证件类型:小程序命名与后端既有命名都收,其余一律拒绝
|
||||
*/
|
||||
private static final Map<String, BaiduOcrType> TYPE_ALIASES = Map.ofEntries(
|
||||
// 身份证
|
||||
Map.entry("IDCARD", BaiduOcrType.ID_CARD),
|
||||
Map.entry("ID_CARD", BaiduOcrType.ID_CARD),
|
||||
// 驾驶证:小程序叫 driver
|
||||
Map.entry("DRIVER", BaiduOcrType.DRIVING_LICENSE),
|
||||
Map.entry("DRIVING_LICENSE", BaiduOcrType.DRIVING_LICENSE),
|
||||
// 行驶证:小程序叫 vehicle
|
||||
Map.entry("VEHICLE", BaiduOcrType.VEHICLE_LICENSE),
|
||||
Map.entry("VEHICLE_LICENSE", BaiduOcrType.VEHICLE_LICENSE),
|
||||
// 道路运输证
|
||||
Map.entry("ROAD_TRANSPORT", BaiduOcrType.ROAD_TRANSPORT_CERTIFICATE),
|
||||
Map.entry("ROAD_TRANSPORT_CERTIFICATE", BaiduOcrType.ROAD_TRANSPORT_CERTIFICATE)
|
||||
);
|
||||
|
||||
/**
|
||||
* 明确知道「没有对应 OCR 能力」的类型,单独给出可读提示
|
||||
*/
|
||||
private static final Map<String, String> KNOWN_UNSUPPORTED = Map.of(
|
||||
"QUALIFICATION", "从业资格证"
|
||||
);
|
||||
|
||||
private static final String SIDE_FRONT = "front";
|
||||
private static final String SIDE_BACK = "back";
|
||||
|
||||
private OcrTypeMapping() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 把调用方传来的证件类型转成百度 OCR 证件类型
|
||||
*
|
||||
* @param type 证件类型:小程序传 idcard / driver / vehicle / road_transport / qualification,
|
||||
* 也接受后端既有命名 id_card / driving_license / vehicle_license / road_transport_certificate
|
||||
* @return 百度 OCR 证件类型
|
||||
* @throws IllegalArgumentException 类型为空、未知,或该证件不支持 OCR
|
||||
*/
|
||||
public static BaiduOcrType toBaiduType(String type) {
|
||||
String normalized = normalize(type);
|
||||
if (normalized.isEmpty()) {
|
||||
throw new IllegalArgumentException("OCR证件类型不能为空");
|
||||
}
|
||||
BaiduOcrType baiduOcrType = TYPE_ALIASES.get(normalized);
|
||||
if (baiduOcrType != null) {
|
||||
return baiduOcrType;
|
||||
}
|
||||
String unsupportedName = KNOWN_UNSUPPORTED.get(normalized);
|
||||
if (unsupportedName != null) {
|
||||
// 不降级为通用识别:否则前端会拿到一堆通用文本却以为识别成功
|
||||
throw new IllegalArgumentException(unsupportedName + "暂不支持自动识别,请手动填写");
|
||||
}
|
||||
throw new IllegalArgumentException("不支持的OCR证件类型:" + type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 规范化正反面参数
|
||||
* <p>
|
||||
* 小程序目前不传该参数,按正面处理(身份证、行驶证、驾驶证的正页是常用场景)。
|
||||
*
|
||||
* @param side 正反面:front / back
|
||||
* @return 规范化结果,恒为 front 或 back
|
||||
*/
|
||||
public static String normalizeSide(String side) {
|
||||
// 归一化结果是大写,故此处比对 "BACK"
|
||||
return "BACK".equals(normalize(side)) ? SIDE_BACK : SIDE_FRONT;
|
||||
}
|
||||
|
||||
/**
|
||||
* 归一化:去首尾空白、去分隔符差异、统一大写
|
||||
*
|
||||
* @param value 原始值
|
||||
* @return 归一化结果;空值返回空串
|
||||
*/
|
||||
private static String normalize(String value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
return value.trim()
|
||||
.replace('-', '_')
|
||||
.replace(' ', '_')
|
||||
.toUpperCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user