🐛 修复道路运输证OCR恒失败:上游不得替它补默认正反面

OcrTypeMapping.normalizeSide 对空值恒返回 front,控制器把这个 front 传给下游,
而道路运输证没有正副面参数(supportsSide 为 false),下游于是判
「传了 side 但该证件不支持」并直接抛错——小程序不传 side、传 front、
传空串三种方式实测全部失败,该证件 OCR 从来没能用过。

改为未指定时返回 null,把「默认取正面」交给知晓证件类型的一侧决定,
并补测试钉住这条契约。
This commit is contained in:
2026-10-03 15:35:22 +08:00
parent cc997c1c0c
commit 214ff38ef8
3 changed files with 37 additions and 8 deletions
@@ -143,6 +143,9 @@ public class DriverAuthController extends BladeController {
public R<BaiduOcrResultVO> ocr(@RequestBody OcrRecognizeRequest request) {
// 两侧对「驾驶证 / 行驶证」的用词正好相反,故必须经显式映射,不能把入参直接透传下游
BaiduOcrType ocrType = OcrTypeMapping.toBaiduType(request.getType());
// 未指定时传 null,不要在这里补 front:道路运输证没有正副面参数,
// 补了会变成「传了 side 但该证件不支持」,让这条链路恒失败。
// 「默认取正面」由知晓证件类型的一侧(OCR 服务)决定。
String side = OcrTypeMapping.normalizeSide(request.getSide());
return R.data(baiduOcrService.recognizeUrl(ocrType, side, request.getImageUrl()));
}
@@ -110,14 +110,23 @@ public final class OcrTypeMapping {
/**
* 规范化正反面参数
* <p>
* 小程序目前不传该参数,按正面处理(身份证、行驶证、驾驶证的正页是常用场景)。
* 小程序不传该参数,按正面处理(身份证、行驶证、驾驶证的正页是常用场景)。
* <p>
* ⚠️ 调用方**必须原样把返回值交给下游,不要在传参前自行补默认值**:
* 这里的「默认正面」只对**支持正副面**的证件成立。道路运输证没有正副面参数,
* 若在调用方先把空值填成 {@code front} 再传下去,下游会看到「传了 side 但该证件
* 不支持」而直接报错——本方法返回 null 才是「调用方没指定」,下游据此才分得清
* 「默认取正面」和「这个证件根本不该带这个参数」。
*
* @param side 正反面:front / back
* @return 规范化结果,恒为 front 或 back
* @return 规范化结果 front / back;调用方未指定时返回 null
*/
public static String normalizeSide(String side) {
// 归一化结果是大写,故此处比对 "BACK"
return "BACK".equals(normalize(side)) ? SIDE_BACK : SIDE_FRONT;
String normalized = normalize(side);
if (normalized.isEmpty()) {
return null;
}
return SIDE_BACK.equals(normalized.toLowerCase(Locale.ROOT)) ? SIDE_BACK : SIDE_FRONT;
}
/**
@@ -5,6 +5,8 @@ import org.junit.jupiter.api.Test;
import org.springblade.transport.ocr.constant.BaiduOcrType;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -93,13 +95,28 @@ class OcrTypeMappingTest {
}
@Test
@DisplayName("正反面:小程序不传 side 时按正面处理")
void defaultSide() {
assertEquals("front", OcrTypeMapping.normalizeSide(null));
assertEquals("front", OcrTypeMapping.normalizeSide(""));
@DisplayName("正反面:小程序不传 side 时返回 null,交由知晓证件类型的一侧决定默认值")
void unsetSideStaysNull() {
// 这里恒返回 front 会让道路运输证恒失败:它没有正副面参数,
// 上游先填成 front,下游就变成「传了 side 但该证件不支持」——
// 小程序不传 side 也会中招(实测三种传法全部报错)。
assertNull(OcrTypeMapping.normalizeSide(null));
assertNull(OcrTypeMapping.normalizeSide(""));
assertNull(OcrTypeMapping.normalizeSide(" "));
assertEquals("front", OcrTypeMapping.normalizeSide("front"));
assertEquals("back", OcrTypeMapping.normalizeSide("back"));
assertEquals("back", OcrTypeMapping.normalizeSide("BACK"));
}
@Test
@DisplayName("正反面:道路运输证不带正副面参数,其余证件默认正面")
void roadTransportHasNoSide() {
// 下游据此区分「默认取正面」与「该证件不该带这个参数」,故这条契约必须有测试守
assertFalse(OcrTypeMapping.toBaiduType("road_transport").supportsSide(),
"道路运输证没有正副面参数,上游不得替它补 front");
assertTrue(OcrTypeMapping.toBaiduType("vehicle").supportsSide());
assertTrue(OcrTypeMapping.toBaiduType("driver").supportsSide());
assertTrue(OcrTypeMapping.toBaiduType("idcard").supportsSide());
}
}