From 1ff0df6cdbf43477d7f4ce5263f827b731eac78a Mon Sep 17 00:00:00 2001 From: quyixiao <2621048238@qq.com> Date: Sat, 6 Jun 2026 01:54:46 +0800 Subject: [PATCH] rjuwhnt --- .../car/RecognizeDriverLicense/TEST_REPORT.md | 600 ++++++++++++++++++ .../car/RecognizeDriverLicense/run_tests.py | 250 ++++++++ .../RecognizeDriverLicense/test_results.jsonl | 19 + 3 files changed, 869 insertions(+) create mode 100644 api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeDriverLicense/TEST_REPORT.md create mode 100644 api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeDriverLicense/run_tests.py create mode 100644 api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeDriverLicense/test_results.jsonl diff --git a/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeDriverLicense/TEST_REPORT.md b/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeDriverLicense/TEST_REPORT.md new file mode 100644 index 0000000..215ed48 --- /dev/null +++ b/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeDriverLicense/TEST_REPORT.md @@ -0,0 +1,600 @@ +# RecognizeDriverLicenseController 测试报告 + +## 测试环境 + +| 项 | 值 | +|---|---| +| 测试时间 | 2026-06-06 01:51:54 | +| 服务地址 | `http://localhost:8888` | +| 接口路径 | `POST /driver/license/recognize` | +| Content-Type | `application/json` | +| 鉴权 | macOS 本地环境跳过 Tencent 鉴权(LogAop 非 Linux 不拦截) | + +## 汇总 + +- 总用例:**19** +- 通过:**19** +- 失败:**0** +- 通过率:**100%** + +## 用例明细 + +| 编号 | 场景 | 类型 | HTTP | code | 结果 | 说明 | +|---|---|---|---|---|---|---| +| TC01 | 缺少 imageUrlOrBase64 | 入参校验 | 200 | 200 | ✅ 通过 | 入参校验拦截成功,返回 R.ok + 提示 | +| TC02 | imageUrlOrBase64 空字符串 | 入参校验 | 200 | 200 | ✅ 通过 | 入参校验拦截成功,返回 R.ok + 提示 | +| TC03 | imageUrlOrBase64 仅空白 | 入参校验 | 200 | 200 | ✅ 通过 | 入参校验拦截成功,返回 R.ok + 提示 | +| TC04 | side 非法值 | 入参校验 | 200 | 200 | ✅ 通过 | 入参校验拦截成功,返回 R.ok + 提示 | +| TC05 | www 缺少协议头 | 入参校验 | 200 | 200 | ✅ 通过 | 入参校验拦截成功,返回 R.ok + 提示 | +| TC06 | ftp 协议 | 入参校验 | 200 | 200 | ✅ 通过 | 入参校验拦截成功,返回 R.ok + 提示 | +| TC07 | Unix 本地路径 | 入参校验 | 200 | 200 | ✅ 通过 | 入参校验拦截成功,返回 R.ok + 提示 | +| TC08 | Windows 本地路径 | 入参校验 | 200 | 200 | ✅ 通过 | 入参校验拦截成功,返回 R.ok + 提示 | +| TC09 | 非 Base64 普通文本 | 入参校验 | 200 | 200 | ✅ 通过 | 入参校验拦截成功,返回 R.ok + 提示 | +| TC10 | data 前缀缺少 base64 段 | 入参校验 | 200 | 200 | ✅ 通过 | 入参校验拦截成功,返回 R.ok + 提示 | +| TC11 | Base64 过短 | 入参校验 | 200 | 200 | ✅ 通过 | 入参校验拦截成功,返回 R.ok + 提示 | +| TC12 | Base64 填充位非法 | 入参校验 | 200 | 200 | ✅ 通过 | 入参校验拦截成功,返回 R.ok + 提示 | +| TC13 | http 协议少斜杠 | 入参校验 | 200 | 200 | ✅ 通过 | 入参校验拦截成功,返回 R.ok + 提示 | +| TC14 | 双斜杠缺协议 | 入参校验 | 200 | 200 | ✅ 通过 | 入参校验拦截成功,返回 R.ok + 提示 | +| TC15 | file 协议 | 入参校验 | 200 | 200 | ✅ 通过 | 入参校验拦截成功,返回 R.ok + 提示 | +| TC16 | 有效 HTTPS 公网图片(正页) | 平台识别 | 200 | 200 | ✅ 通过 | 平台链路返回提示:平台业务拒绝 | +| TC17 | 有效 1x1 PNG Base64 | 平台识别 | 200 | 200 | ✅ 通过 | 平台链路返回提示:影像不合规 | +| TC18 | side=back 公网图片 | 平台识别 | 200 | 200 | ✅ 通过 | 平台链路返回提示:平台业务拒绝 | +| TC19 | side=front 别名 | 平台识别 | 200 | 200 | ✅ 通过 | 平台链路返回提示:并发限流 | + +## 详细记录 + +### TC01 缺少 imageUrlOrBase64 + +- **类型**:入参校验 +- **期望**:影像源缺失 +- **HTTP 状态码**:200 +- **业务 code**:`200` +- **结果**:通过 — 入参校验拦截成功,返回 R.ok + 提示 + +**请求体:** + +```json +{ + "side": "face" +} +``` + +**响应 msg:** + +``` +【驾驶证识别·影像源缺失】imageUrlOrBase64 未提供或仅包含空白字符|定位信息:入参长度=0,side=face|处置指引:请传 HTTP/HTTPS 图片链接,或 jpg/jpeg/png/bmp 的 Base64 字符串 +``` + +**响应 data(摘要):** + +```json +{} +``` + +### TC02 imageUrlOrBase64 空字符串 + +- **类型**:入参校验 +- **期望**:影像源缺失 +- **HTTP 状态码**:200 +- **业务 code**:`200` +- **结果**:通过 — 入参校验拦截成功,返回 R.ok + 提示 + +**请求体:** + +```json +{ + "imageUrlOrBase64": "", + "side": "face" +} +``` + +**响应 msg:** + +``` +【驾驶证识别·影像源缺失】imageUrlOrBase64 未提供或仅包含空白字符|定位信息:入参长度=0,side=face|处置指引:请传 HTTP/HTTPS 图片链接,或 jpg/jpeg/png/bmp 的 Base64 字符串 +``` + +**响应 data(摘要):** + +```json +{} +``` + +### TC03 imageUrlOrBase64 仅空白 + +- **类型**:入参校验 +- **期望**:影像源缺失 +- **HTTP 状态码**:200 +- **业务 code**:`200` +- **结果**:通过 — 入参校验拦截成功,返回 R.ok + 提示 + +**请求体:** + +```json +{ + "imageUrlOrBase64": " ", + "side": "face" +} +``` + +**响应 msg:** + +``` +【驾驶证识别·影像源缺失】imageUrlOrBase64 未提供或仅包含空白字符|定位信息:入参长度=0,side=face|处置指引:请传 HTTP/HTTPS 图片链接,或 jpg/jpeg/png/bmp 的 Base64 字符串 +``` + +**响应 data(摘要):** + +```json +{} +``` + +### TC04 side 非法值 + +- **类型**:入参校验 +- **期望**:页面标识无效 +- **HTTP 状态码**:200 +- **业务 code**:`200` +- **结果**:通过 — 入参校验拦截成功,返回 R.ok + 提示 + +**请求体:** + +```json +{ + "imageUrlOrBase64": "https://example.com/a.jpg", + "side": "invalid" +} +``` + +**响应 msg:** + +``` +【驾驶证识别·页面标识无效】参数 side 的取值无法映射到驾驶证正页或副页识别模式|定位信息:当前 side=invalid,合法取值=face|front|back|处置指引:正页(含纸质正页、电子驾驶证正页)请传 face 或 front;副页(含实习记录等)请传 back +``` + +**响应 data(摘要):** + +```json +{} +``` + +### TC05 www 缺少协议头 + +- **类型**:入参校验 +- **期望**:链接缺少协议头 +- **HTTP 状态码**:200 +- **业务 code**:`200` +- **结果**:通过 — 入参校验拦截成功,返回 R.ok + 提示 + +**请求体:** + +```json +{ + "imageUrlOrBase64": "www.example.com/a.jpg", + "side": "face" +} +``` + +**响应 msg:** + +``` +【驾驶证识别·链接缺少协议头】链接以 www. 开头但缺少协议头,无法识别为 HTTP(S) 地址|定位信息:入参前缀=www.example.com/a.jpg,side=face|处置指引:若以链接传图,请补全为 https:// 或 http:// 开头 +``` + +**响应 data(摘要):** + +```json +{} +``` + +### TC06 ftp 协议 + +- **类型**:入参校验 +- **期望**:链接协议无效 +- **HTTP 状态码**:200 +- **业务 code**:`200` +- **结果**:通过 — 入参校验拦截成功,返回 R.ok + 提示 + +**请求体:** + +```json +{ + "imageUrlOrBase64": "ftp://example.com/a.jpg", + "side": "face" +} +``` + +**响应 msg:** + +``` +【驾驶证识别·链接协议无效】检测到 ftp:// 协议,本接口仅支持 HTTP/HTTPS 图片链接|定位信息:入参前缀=ftp://example.com/a.jpg,side=face|处置指引:图片链接须以 http:// 或 https:// 开头,且可被公网访问 +``` + +**响应 data(摘要):** + +```json +{} +``` + +### TC07 Unix 本地路径 + +- **类型**:入参校验 +- **期望**:影像格式无效 +- **HTTP 状态码**:200 +- **业务 code**:`200` +- **结果**:通过 — 入参校验拦截成功,返回 R.ok + 提示 + +**请求体:** + +```json +{ + "imageUrlOrBase64": "/tmp/driver.jpg", + "side": "face" +} +``` + +**响应 msg:** + +``` +【驾驶证识别·影像格式无效】检测到以 / 开头的本地绝对路径,无法作为网络图片链接使用|定位信息:入参前缀=/tmp/driver.jpg,side=face|处置指引:请改为 HTTP/HTTPS 图片链接,或将图片转为 Base64 后传入 +``` + +**响应 data(摘要):** + +```json +{} +``` + +### TC08 Windows 本地路径 + +- **类型**:入参校验 +- **期望**:影像格式无效 +- **HTTP 状态码**:200 +- **业务 code**:`200` +- **结果**:通过 — 入参校验拦截成功,返回 R.ok + 提示 + +**请求体:** + +```json +{ + "imageUrlOrBase64": "C:\\images\\driver.jpg", + "side": "face" +} +``` + +**响应 msg:** + +``` +【驾驶证识别·影像格式无效】检测到 Windows 本地路径(如 C:\...),无法作为网络图片链接使用|定位信息:入参前缀=C:\images\driver.jpg,side=face|处置指引:请改为 HTTP/HTTPS 图片链接,或将图片转为 Base64 后传入 +``` + +**响应 data(摘要):** + +```json +{} +``` + +### TC09 非 Base64 普通文本 + +- **类型**:入参校验 +- **期望**:Base64 字符非法 +- **HTTP 状态码**:200 +- **业务 code**:`200` +- **结果**:通过 — 入参校验拦截成功,返回 R.ok + 提示 + +**请求体:** + +```json +{ + "imageUrlOrBase64": "not-a-valid-image-input", + "side": "face" +} +``` + +**响应 msg:** + +``` +【驾驶证识别·Base64 字符非法】内容包含非 Base64 合法字符(仅允许 A-Z、a-z、0-9、+、/、=)|定位信息:非法片段示例=not-a-va,side=face|处置指引:请检查是否误传了普通文本、JSON 字段名或文件路径;链接须以 http:// 或 https:// 开头 +``` + +**响应 data(摘要):** + +```json +{} +``` + +### TC10 data 前缀缺少 base64 段 + +- **类型**:入参校验 +- **期望**:Base64 前缀无效 +- **HTTP 状态码**:200 +- **业务 code**:`200` +- **结果**:通过 — 入参校验拦截成功,返回 R.ok + 提示 + +**请求体:** + +```json +{ + "imageUrlOrBase64": "data:image/jpeg;xxx,abc", + "side": "face" +} +``` + +**响应 msg:** + +``` +【驾驶证识别·Base64 前缀无效】data: 前缀格式不正确,缺少 base64, 段|定位信息:入参前缀=data:image/jpeg;xxx,abc,side=face|处置指引:请使用 data:image/jpeg;base64,{数据} 格式,或直接传纯 Base64 字符串 +``` + +**响应 data(摘要):** + +```json +{} +``` + +### TC11 Base64 过短 + +- **类型**:入参校验 +- **期望**:Base64 内容过短 +- **HTTP 状态码**:200 +- **业务 code**:`200` +- **结果**:通过 — 入参校验拦截成功,返回 R.ok + 提示 + +**请求体:** + +```json +{ + "imageUrlOrBase64": "abc", + "side": "face" +} +``` + +**响应 msg:** + +``` +【驾驶证识别·Base64 内容过短】剥离前缀后 Base64 仅 3 字符,不足以构成有效图片|定位信息:原始入参长度=3 字符,side=face|处置指引:请确认图片已完整编码,未截断 +``` + +**响应 data(摘要):** + +```json +{} +``` + +### TC12 Base64 填充位非法 + +- **类型**:入参校验 +- **期望**:Base64 字符非法 +- **HTTP 状态码**:200 +- **业务 code**:`200` +- **结果**:通过 — 入参校验拦截成功,返回 R.ok + 提示 + +**请求体:** + +```json +{ + "imageUrlOrBase64": "abcd====", + "side": "face" +} +``` + +**响应 msg:** + +``` +【驾驶证识别·Base64 字符非法】内容包含非 Base64 合法字符(仅允许 A-Z、a-z、0-9、+、/、=)|定位信息:非法片段示例=abcd====,side=face|处置指引:请检查是否误传了普通文本、JSON 字段名或文件路径;链接须以 http:// 或 https:// 开头 +``` + +**响应 data(摘要):** + +```json +{} +``` + +### TC13 http 协议少斜杠 + +- **类型**:入参校验 +- **期望**:链接缺少协议头 +- **HTTP 状态码**:200 +- **业务 code**:`200` +- **结果**:通过 — 入参校验拦截成功,返回 R.ok + 提示 + +**请求体:** + +```json +{ + "imageUrlOrBase64": "http:/example.com/a.jpg", + "side": "face" +} +``` + +**响应 msg:** + +``` +【驾驶证识别·链接缺少协议头】链接协议写法错误,应为 http:// 而非 http:/|定位信息:入参前缀=http:/example.com/a.jpg,side=face|处置指引:若以链接传图,请补全为 https:// 或 http:// 开头 +``` + +**响应 data(摘要):** + +```json +{} +``` + +### TC14 双斜杠缺协议 + +- **类型**:入参校验 +- **期望**:链接缺少协议头 +- **HTTP 状态码**:200 +- **业务 code**:`200` +- **结果**:通过 — 入参校验拦截成功,返回 R.ok + 提示 + +**请求体:** + +```json +{ + "imageUrlOrBase64": "//example.com/a.jpg", + "side": "face" +} +``` + +**响应 msg:** + +``` +【驾驶证识别·链接缺少协议头】链接以 // 开头但缺少协议头(如 https:),无法识别为完整 URL|定位信息:入参前缀=//example.com/a.jpg,side=face|处置指引:若以链接传图,请补全为 https:// 或 http:// 开头 +``` + +**响应 data(摘要):** + +```json +{} +``` + +### TC15 file 协议 + +- **类型**:入参校验 +- **期望**:链接协议无效 +- **HTTP 状态码**:200 +- **业务 code**:`200` +- **结果**:通过 — 入参校验拦截成功,返回 R.ok + 提示 + +**请求体:** + +```json +{ + "imageUrlOrBase64": "file:///tmp/a.jpg", + "side": "face" +} +``` + +**响应 msg:** + +``` +【驾驶证识别·链接协议无效】检测到 file:// 本地文件协议,不支持直接读取本地文件|定位信息:入参前缀=file:///tmp/a.jpg,side=face|处置指引:图片链接须以 http:// 或 https:// 开头,且可被公网访问 +``` + +**响应 data(摘要):** + +```json +{} +``` + +### TC16 有效 HTTPS 公网图片(正页) + +- **类型**:平台识别 +- **期望**:进入平台识别链路 +- **HTTP 状态码**:200 +- **业务 code**:`200` +- **结果**:通过 — 平台链路返回提示:平台业务拒绝 + +**请求体:** + +```json +{ + "imageUrlOrBase64": "https://www.opsky.com.cn/upload/20211224/KXfgvm2MFRAXKbPu5LK.png", + "side": "face" +} +``` + +**响应 msg:** + +``` +【驾驶证识别·平台业务拒绝】平台返回了未在本地维护的错误码,需结合错误描述人工判读|定位信息:错误码=282103,错误描述=recognize error, failed to match the template,目标页面=正页(front/face)|处置指引:请依据错误描述调整入参或影像后重试;持续失败请附带 traceId 联系技术支持 +``` + +**响应 data(摘要):** + +```json +{} +``` + +### TC17 有效 1x1 PNG Base64 + +- **类型**:平台识别 +- **期望**:进入平台识别链路 +- **HTTP 状态码**:200 +- **业务 code**:`200` +- **结果**:通过 — 平台链路返回提示:影像不合规 + +**请求体:** + +```json +{ + "imageUrlOrBase64": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==", + "side": "face" +} +``` + +**响应 msg:** + +``` +【驾驶证识别·影像不合规】像素尺寸或编码后体积不满足规范(边长 15~4096px,编码后≤4M)|定位信息:错误码=216202,错误描述=image size error,目标页面=正页(front/face)|处置指引:请压缩或裁剪图片,保证最长边≤4096px、最短边≥15px,且 urlencode 后≤4M +``` + +**响应 data(摘要):** + +```json +{} +``` + +### TC18 side=back 公网图片 + +- **类型**:平台识别 +- **期望**:进入平台识别链路 +- **HTTP 状态码**:200 +- **业务 code**:`200` +- **结果**:通过 — 平台链路返回提示:平台业务拒绝 + +**请求体:** + +```json +{ + "imageUrlOrBase64": "https://www.opsky.com.cn/upload/20211224/KXfgvm2MFRAXKbPu5LK.png", + "side": "back" +} +``` + +**响应 msg:** + +``` +【驾驶证识别·平台业务拒绝】平台返回了未在本地维护的错误码,需结合错误描述人工判读|定位信息:错误码=282103,错误描述=recognize error, failed to match the template,目标页面=副页(back)|处置指引:请依据错误描述调整入参或影像后重试;持续失败请附带 traceId 联系技术支持 +``` + +**响应 data(摘要):** + +```json +{} +``` + +### TC19 side=front 别名 + +- **类型**:平台识别 +- **期望**:进入平台识别链路 +- **HTTP 状态码**:200 +- **业务 code**:`200` +- **结果**:通过 — 平台链路返回提示:并发限流 + +**请求体:** + +```json +{ + "imageUrlOrBase64": "https://www.opsky.com.cn/upload/20211224/KXfgvm2MFRAXKbPu5LK.png", + "side": "front" +} +``` + +**响应 msg:** + +``` +【驾驶证识别·并发限流】单位时间请求过于密集,已触发平台 QPS 限流,请拉长调用间隔后重试|定位信息:错误码=18,错误描述=Open api qps request limit reached,目标页面=正页(front/face)|处置指引:请在客户端增加限流/退避策略,避免瞬时并发过高 +``` + +**响应 data(摘要):** + +```json +{} +``` + +## 结论 + +1. **入参校验类用例(TC01–TC15)**:均返回 HTTP 200 + 业务 code 200,错误说明写入 `msg`,`data` 为空对象,符合「校验失败也返回 ok」的约定。 +2. **平台识别类用例(TC16–TC19)**:使用公网 PNG / 1x1 Base64 触发百度识别链路;测试图非驾驶证,字段为空或带平台提示属正常。 +3. **建议**:补充真实驾驶证正/副页样本,验证字段映射完整性。 diff --git a/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeDriverLicense/run_tests.py b/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeDriverLicense/run_tests.py new file mode 100644 index 0000000..7178bf0 --- /dev/null +++ b/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeDriverLicense/run_tests.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python3 +"""RecognizeDriverLicenseController 本地接口测试脚本。""" + +import json +import urllib.request +from datetime import datetime +from pathlib import Path + +BASE_URL = "http://localhost:8888/driver/license/recognize" +OUT_DIR = Path(__file__).parent +RESULTS_FILE = OUT_DIR / "test_results.jsonl" +REPORT_FILE = OUT_DIR / "TEST_REPORT.md" + +CASES = [ + ("TC01", "缺少 imageUrlOrBase64", {"side": "face"}, "入参校验", "影像源缺失"), + ("TC02", "imageUrlOrBase64 空字符串", {"imageUrlOrBase64": "", "side": "face"}, "入参校验", "影像源缺失"), + ("TC03", "imageUrlOrBase64 仅空白", {"imageUrlOrBase64": " ", "side": "face"}, "入参校验", "影像源缺失"), + ("TC04", "side 非法值", {"imageUrlOrBase64": "https://example.com/a.jpg", "side": "invalid"}, "入参校验", "页面标识无效"), + ("TC05", "www 缺少协议头", {"imageUrlOrBase64": "www.example.com/a.jpg", "side": "face"}, "入参校验", "链接缺少协议头"), + ("TC06", "ftp 协议", {"imageUrlOrBase64": "ftp://example.com/a.jpg", "side": "face"}, "入参校验", "链接协议无效"), + ("TC07", "Unix 本地路径", {"imageUrlOrBase64": "/tmp/driver.jpg", "side": "face"}, "入参校验", "影像格式无效"), + ("TC08", "Windows 本地路径", {"imageUrlOrBase64": r"C:\images\driver.jpg", "side": "face"}, "入参校验", "影像格式无效"), + ("TC09", "非 Base64 普通文本", {"imageUrlOrBase64": "not-a-valid-image-input", "side": "face"}, "入参校验", "Base64 字符非法"), + ("TC10", "data 前缀缺少 base64 段", {"imageUrlOrBase64": "data:image/jpeg;xxx,abc", "side": "face"}, "入参校验", "Base64 前缀无效"), + ("TC11", "Base64 过短", {"imageUrlOrBase64": "abc", "side": "face"}, "入参校验", "Base64 内容过短"), + ("TC12", "Base64 填充位非法", {"imageUrlOrBase64": "abcd====", "side": "face"}, "入参校验", "Base64 字符非法"), + ("TC13", "http 协议少斜杠", {"imageUrlOrBase64": "http:/example.com/a.jpg", "side": "face"}, "入参校验", "链接缺少协议头"), + ("TC14", "双斜杠缺协议", {"imageUrlOrBase64": "//example.com/a.jpg", "side": "face"}, "入参校验", "链接缺少协议头"), + ("TC15", "file 协议", {"imageUrlOrBase64": "file:///tmp/a.jpg", "side": "face"}, "入参校验", "链接协议无效"), + ( + "TC16", + "有效 HTTPS 公网图片(正页)", + { + "imageUrlOrBase64": "https://www.opsky.com.cn/upload/20211224/KXfgvm2MFRAXKbPu5LK.png", + "side": "face", + }, + "平台识别", + None, + ), + ( + "TC17", + "有效 1x1 PNG Base64", + { + "imageUrlOrBase64": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==", + "side": "face", + }, + "平台识别", + None, + ), + ( + "TC18", + "side=back 公网图片", + { + "imageUrlOrBase64": "https://www.opsky.com.cn/upload/20211224/KXfgvm2MFRAXKbPu5LK.png", + "side": "back", + }, + "平台识别", + None, + ), + ( + "TC19", + "side=front 别名", + { + "imageUrlOrBase64": "https://www.opsky.com.cn/upload/20211224/KXfgvm2MFRAXKbPu5LK.png", + "side": "front", + }, + "平台识别", + None, + ), +] + + +def call_api(payload: dict) -> tuple[int, dict]: + data = json.dumps(payload).encode("utf-8") + req = urllib.request.Request( + BASE_URL, + data=data, + headers={"Content-Type": "application/json"}, + method="POST", + ) + with urllib.request.urlopen(req, timeout=60) as resp: + body = json.loads(resp.read().decode("utf-8")) + return resp.status, body + + +def extract_category(msg: str) -> str: + if not msg: + return "" + if "【驾驶证识别·" in msg and "】" in msg: + return msg.split("【驾驶证识别·", 1)[1].split("】", 1)[0] + return "" + + +def count_filled_fields(data: dict) -> int: + if not isinstance(data, dict): + return 0 + return sum(1 for v in data.values() if isinstance(v, str) and v.strip()) + + +def evaluate(case_id, expected_hint, response): + code = str(response.get("code", "")) + msg = response.get("msg") or "" + category = extract_category(msg) + + if code != "200": + return False, f"code 期望 200,实际 {code}" + + input_validation_categories = { + "影像源缺失", "页面标识无效", "链接缺少协议头", "链接协议无效", "影像格式无效", + "Base64 前缀无效", "Base64 内容过短", "Base64 字符非法", "Base64 解码失败", + "Base64 体积过大", "链接过长", "链接解析失败", "入参绑定失败", + } + + if expected_hint: + if expected_hint not in msg: + return False, f"msg 未包含期望分类「{expected_hint}」,实际分类「{category}」" + return True, "入参校验拦截成功,返回 R.ok + 提示" + + if category: + if category in input_validation_categories: + return False, f"期望进入平台识别,但仍在入参校验阶段:{category}" + return True, f"平台链路返回提示:{category}" + if count_filled_fields(response.get("data") or {}) > 0: + return True, "平台识别成功,返回结构化字段" + return True, "平台返回成功,data 字段为空(测试图非驾驶证,符合预期)" + + +def build_report(results: list[dict]) -> str: + passed = sum(1 for r in results if r["passed"]) + failed = len(results) - passed + now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + lines = [ + "# RecognizeDriverLicenseController 测试报告", + "", + "## 测试环境", + "", + "| 项 | 值 |", + "|---|---|", + f"| 测试时间 | {now} |", + "| 服务地址 | `http://localhost:8888` |", + "| 接口路径 | `POST /driver/license/recognize` |", + "| Content-Type | `application/json` |", + "| 鉴权 | macOS 本地环境跳过 Tencent 鉴权(LogAop 非 Linux 不拦截) |", + "", + "## 汇总", + "", + f"- 总用例:**{len(results)}**", + f"- 通过:**{passed}**", + f"- 失败:**{failed}**", + f"- 通过率:**{passed * 100 // len(results)}%**", + "", + "## 用例明细", + "", + "| 编号 | 场景 | 类型 | HTTP | code | 结果 | 说明 |", + "|---|---|---|---|---|---|---|", + ] + + for r in results: + status = "✅ 通过" if r["passed"] else "❌ 失败" + msg_short = (r["response"].get("msg") or "(无提示)")[:60].replace("|", "/") + lines.append( + f"| {r['id']} | {r['name']} | {r['type']} | {r['httpCode']} | " + f"{r['response'].get('code', '')} | {status} | {r['verdict']} |" + ) + + lines.extend(["", "## 详细记录", ""]) + + for r in results: + lines.extend( + [ + f"### {r['id']} {r['name']}", + "", + f"- **类型**:{r['type']}", + f"- **期望**:{r['expect']}", + f"- **HTTP 状态码**:{r['httpCode']}", + f"- **业务 code**:`{r['response'].get('code', '')}`", + f"- **结果**:{'通过' if r['passed'] else '失败'} — {r['verdict']}", + "", + "**请求体:**", + "", + "```json", + json.dumps(r["request"], ensure_ascii=False, indent=2), + "```", + "", + "**响应 msg:**", + "", + "```", + r["response"].get("msg") or "(空)", + "```", + "", + "**响应 data(摘要):**", + "", + "```json", + json.dumps(r["response"].get("data"), ensure_ascii=False, indent=2), + "```", + "", + ] + ) + + lines.extend( + [ + "## 结论", + "", + "1. **入参校验类用例(TC01–TC15)**:均返回 HTTP 200 + 业务 code 200,错误说明写入 `msg`,`data` 为空对象,符合「校验失败也返回 ok」的约定。", + "2. **平台识别类用例(TC16–TC19)**:使用公网 PNG / 1x1 Base64 触发百度识别链路;测试图非驾驶证,字段为空或带平台提示属正常。", + "3. **建议**:补充真实驾驶证正/副页样本,验证字段映射完整性。", + "", + ] + ) + return "\n".join(lines) + + +def main() -> None: + results = [] + RESULTS_FILE.write_text("", encoding="utf-8") + + for case_id, name, payload, case_type, expected_hint in CASES: + try: + http_code, response = call_api(payload) + passed, verdict = evaluate(case_id, expected_hint, response) + error = None + except Exception as exc: # noqa: BLE001 + http_code, response = 0, {"code": "error", "msg": str(exc), "data": {}} + passed, verdict = False, f"请求异常:{exc}" + error = str(exc) + + entry = { + "id": case_id, + "name": name, + "type": case_type, + "request": payload, + "httpCode": http_code, + "response": response, + "expect": expected_hint or "进入平台识别链路", + "passed": passed, + "verdict": verdict, + "error": error, + } + results.append(entry) + with RESULTS_FILE.open("a", encoding="utf-8") as f: + f.write(json.dumps(entry, ensure_ascii=False) + "\n") + print(f"{case_id} {'PASS' if passed else 'FAIL'} - {verdict}") + + REPORT_FILE.write_text(build_report(results), encoding="utf-8") + print(f"\nReport written to: {REPORT_FILE}") + + +if __name__ == "__main__": + main() diff --git a/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeDriverLicense/test_results.jsonl b/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeDriverLicense/test_results.jsonl new file mode 100644 index 0000000..6cd6b6c --- /dev/null +++ b/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeDriverLicense/test_results.jsonl @@ -0,0 +1,19 @@ +{"id": "TC01", "name": "缺少 imageUrlOrBase64", "type": "入参校验", "request": {"side": "face"}, "httpCode": 200, "response": {"data": {}, "code": "200", "msg": "【驾驶证识别·影像源缺失】imageUrlOrBase64 未提供或仅包含空白字符|定位信息:入参长度=0,side=face|处置指引:请传 HTTP/HTTPS 图片链接,或 jpg/jpeg/png/bmp 的 Base64 字符串", "traceId": "on10048420260606015153"}, "expect": "影像源缺失", "passed": true, "verdict": "入参校验拦截成功,返回 R.ok + 提示", "error": null} +{"id": "TC02", "name": "imageUrlOrBase64 空字符串", "type": "入参校验", "request": {"imageUrlOrBase64": "", "side": "face"}, "httpCode": 200, "response": {"data": {}, "code": "200", "msg": "【驾驶证识别·影像源缺失】imageUrlOrBase64 未提供或仅包含空白字符|定位信息:入参长度=0,side=face|处置指引:请传 HTTP/HTTPS 图片链接,或 jpg/jpeg/png/bmp 的 Base64 字符串", "traceId": "on84448620260606015153"}, "expect": "影像源缺失", "passed": true, "verdict": "入参校验拦截成功,返回 R.ok + 提示", "error": null} +{"id": "TC03", "name": "imageUrlOrBase64 仅空白", "type": "入参校验", "request": {"imageUrlOrBase64": " ", "side": "face"}, "httpCode": 200, "response": {"data": {}, "code": "200", "msg": "【驾驶证识别·影像源缺失】imageUrlOrBase64 未提供或仅包含空白字符|定位信息:入参长度=0,side=face|处置指引:请传 HTTP/HTTPS 图片链接,或 jpg/jpeg/png/bmp 的 Base64 字符串", "traceId": "on64248820260606015153"}, "expect": "影像源缺失", "passed": true, "verdict": "入参校验拦截成功,返回 R.ok + 提示", "error": null} +{"id": "TC04", "name": "side 非法值", "type": "入参校验", "request": {"imageUrlOrBase64": "https://example.com/a.jpg", "side": "invalid"}, "httpCode": 200, "response": {"data": {}, "code": "200", "msg": "【驾驶证识别·页面标识无效】参数 side 的取值无法映射到驾驶证正页或副页识别模式|定位信息:当前 side=invalid,合法取值=face|front|back|处置指引:正页(含纸质正页、电子驾驶证正页)请传 face 或 front;副页(含实习记录等)请传 back", "traceId": "on38349020260606015153"}, "expect": "页面标识无效", "passed": true, "verdict": "入参校验拦截成功,返回 R.ok + 提示", "error": null} +{"id": "TC05", "name": "www 缺少协议头", "type": "入参校验", "request": {"imageUrlOrBase64": "www.example.com/a.jpg", "side": "face"}, "httpCode": 200, "response": {"data": {}, "code": "200", "msg": "【驾驶证识别·链接缺少协议头】链接以 www. 开头但缺少协议头,无法识别为 HTTP(S) 地址|定位信息:入参前缀=www.example.com/a.jpg,side=face|处置指引:若以链接传图,请补全为 https:// 或 http:// 开头", "traceId": "on75749120260606015153"}, "expect": "链接缺少协议头", "passed": true, "verdict": "入参校验拦截成功,返回 R.ok + 提示", "error": null} +{"id": "TC06", "name": "ftp 协议", "type": "入参校验", "request": {"imageUrlOrBase64": "ftp://example.com/a.jpg", "side": "face"}, "httpCode": 200, "response": {"data": {}, "code": "200", "msg": "【驾驶证识别·链接协议无效】检测到 ftp:// 协议,本接口仅支持 HTTP/HTTPS 图片链接|定位信息:入参前缀=ftp://example.com/a.jpg,side=face|处置指引:图片链接须以 http:// 或 https:// 开头,且可被公网访问", "traceId": "on79049320260606015153"}, "expect": "链接协议无效", "passed": true, "verdict": "入参校验拦截成功,返回 R.ok + 提示", "error": null} +{"id": "TC07", "name": "Unix 本地路径", "type": "入参校验", "request": {"imageUrlOrBase64": "/tmp/driver.jpg", "side": "face"}, "httpCode": 200, "response": {"data": {}, "code": "200", "msg": "【驾驶证识别·影像格式无效】检测到以 / 开头的本地绝对路径,无法作为网络图片链接使用|定位信息:入参前缀=/tmp/driver.jpg,side=face|处置指引:请改为 HTTP/HTTPS 图片链接,或将图片转为 Base64 后传入", "traceId": "on19349420260606015153"}, "expect": "影像格式无效", "passed": true, "verdict": "入参校验拦截成功,返回 R.ok + 提示", "error": null} +{"id": "TC08", "name": "Windows 本地路径", "type": "入参校验", "request": {"imageUrlOrBase64": "C:\\images\\driver.jpg", "side": "face"}, "httpCode": 200, "response": {"data": {}, "code": "200", "msg": "【驾驶证识别·影像格式无效】检测到 Windows 本地路径(如 C:\\...),无法作为网络图片链接使用|定位信息:入参前缀=C:\\images\\driver.jpg,side=face|处置指引:请改为 HTTP/HTTPS 图片链接,或将图片转为 Base64 后传入", "traceId": "on77849520260606015153"}, "expect": "影像格式无效", "passed": true, "verdict": "入参校验拦截成功,返回 R.ok + 提示", "error": null} +{"id": "TC09", "name": "非 Base64 普通文本", "type": "入参校验", "request": {"imageUrlOrBase64": "not-a-valid-image-input", "side": "face"}, "httpCode": 200, "response": {"data": {}, "code": "200", "msg": "【驾驶证识别·Base64 字符非法】内容包含非 Base64 合法字符(仅允许 A-Z、a-z、0-9、+、/、=)|定位信息:非法片段示例=not-a-va,side=face|处置指引:请检查是否误传了普通文本、JSON 字段名或文件路径;链接须以 http:// 或 https:// 开头", "traceId": "on77149720260606015153"}, "expect": "Base64 字符非法", "passed": true, "verdict": "入参校验拦截成功,返回 R.ok + 提示", "error": null} +{"id": "TC10", "name": "data 前缀缺少 base64 段", "type": "入参校验", "request": {"imageUrlOrBase64": "data:image/jpeg;xxx,abc", "side": "face"}, "httpCode": 200, "response": {"data": {}, "code": "200", "msg": "【驾驶证识别·Base64 前缀无效】data: 前缀格式不正确,缺少 base64, 段|定位信息:入参前缀=data:image/jpeg;xxx,abc,side=face|处置指引:请使用 data:image/jpeg;base64,{数据} 格式,或直接传纯 Base64 字符串", "traceId": "on34149820260606015153"}, "expect": "Base64 前缀无效", "passed": true, "verdict": "入参校验拦截成功,返回 R.ok + 提示", "error": null} +{"id": "TC11", "name": "Base64 过短", "type": "入参校验", "request": {"imageUrlOrBase64": "abc", "side": "face"}, "httpCode": 200, "response": {"data": {}, "code": "200", "msg": "【驾驶证识别·Base64 内容过短】剥离前缀后 Base64 仅 3 字符,不足以构成有效图片|定位信息:原始入参长度=3 字符,side=face|处置指引:请确认图片已完整编码,未截断", "traceId": "on39249920260606015153"}, "expect": "Base64 内容过短", "passed": true, "verdict": "入参校验拦截成功,返回 R.ok + 提示", "error": null} +{"id": "TC12", "name": "Base64 填充位非法", "type": "入参校验", "request": {"imageUrlOrBase64": "abcd====", "side": "face"}, "httpCode": 200, "response": {"data": {}, "code": "200", "msg": "【驾驶证识别·Base64 字符非法】内容包含非 Base64 合法字符(仅允许 A-Z、a-z、0-9、+、/、=)|定位信息:非法片段示例=abcd====,side=face|处置指引:请检查是否误传了普通文本、JSON 字段名或文件路径;链接须以 http:// 或 https:// 开头", "traceId": "on17850020260606015153"}, "expect": "Base64 字符非法", "passed": true, "verdict": "入参校验拦截成功,返回 R.ok + 提示", "error": null} +{"id": "TC13", "name": "http 协议少斜杠", "type": "入参校验", "request": {"imageUrlOrBase64": "http:/example.com/a.jpg", "side": "face"}, "httpCode": 200, "response": {"data": {}, "code": "200", "msg": "【驾驶证识别·链接缺少协议头】链接协议写法错误,应为 http:// 而非 http:/|定位信息:入参前缀=http:/example.com/a.jpg,side=face|处置指引:若以链接传图,请补全为 https:// 或 http:// 开头", "traceId": "on65750120260606015153"}, "expect": "链接缺少协议头", "passed": true, "verdict": "入参校验拦截成功,返回 R.ok + 提示", "error": null} +{"id": "TC14", "name": "双斜杠缺协议", "type": "入参校验", "request": {"imageUrlOrBase64": "//example.com/a.jpg", "side": "face"}, "httpCode": 200, "response": {"data": {}, "code": "200", "msg": "【驾驶证识别·链接缺少协议头】链接以 // 开头但缺少协议头(如 https:),无法识别为完整 URL|定位信息:入参前缀=//example.com/a.jpg,side=face|处置指引:若以链接传图,请补全为 https:// 或 http:// 开头", "traceId": "on25950220260606015153"}, "expect": "链接缺少协议头", "passed": true, "verdict": "入参校验拦截成功,返回 R.ok + 提示", "error": null} +{"id": "TC15", "name": "file 协议", "type": "入参校验", "request": {"imageUrlOrBase64": "file:///tmp/a.jpg", "side": "face"}, "httpCode": 200, "response": {"data": {}, "code": "200", "msg": "【驾驶证识别·链接协议无效】检测到 file:// 本地文件协议,不支持直接读取本地文件|定位信息:入参前缀=file:///tmp/a.jpg,side=face|处置指引:图片链接须以 http:// 或 https:// 开头,且可被公网访问", "traceId": "on73850420260606015153"}, "expect": "链接协议无效", "passed": true, "verdict": "入参校验拦截成功,返回 R.ok + 提示", "error": null} +{"id": "TC16", "name": "有效 HTTPS 公网图片(正页)", "type": "平台识别", "request": {"imageUrlOrBase64": "https://www.opsky.com.cn/upload/20211224/KXfgvm2MFRAXKbPu5LK.png", "side": "face"}, "httpCode": 200, "response": {"data": {}, "code": "200", "msg": "【驾驶证识别·平台业务拒绝】平台返回了未在本地维护的错误码,需结合错误描述人工判读|定位信息:错误码=282103,错误描述=recognize error, failed to match the template,目标页面=正页(front/face)|处置指引:请依据错误描述调整入参或影像后重试;持续失败请附带 traceId 联系技术支持", "traceId": "on70750520260606015153"}, "expect": "进入平台识别链路", "passed": true, "verdict": "平台链路返回提示:平台业务拒绝", "error": null} +{"id": "TC17", "name": "有效 1x1 PNG Base64", "type": "平台识别", "request": {"imageUrlOrBase64": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==", "side": "face"}, "httpCode": 200, "response": {"data": {}, "code": "200", "msg": "【驾驶证识别·影像不合规】像素尺寸或编码后体积不满足规范(边长 15~4096px,编码后≤4M)|定位信息:错误码=216202,错误描述=image size error,目标页面=正页(front/face)|处置指引:请压缩或裁剪图片,保证最长边≤4096px、最短边≥15px,且 urlencode 后≤4M", "traceId": "on73109520260606015154"}, "expect": "进入平台识别链路", "passed": true, "verdict": "平台链路返回提示:影像不合规", "error": null} +{"id": "TC18", "name": "side=back 公网图片", "type": "平台识别", "request": {"imageUrlOrBase64": "https://www.opsky.com.cn/upload/20211224/KXfgvm2MFRAXKbPu5LK.png", "side": "back"}, "httpCode": 200, "response": {"data": {}, "code": "200", "msg": "【驾驶证识别·平台业务拒绝】平台返回了未在本地维护的错误码,需结合错误描述人工判读|定位信息:错误码=282103,错误描述=recognize error, failed to match the template,目标页面=副页(back)|处置指引:请依据错误描述调整入参或影像后重试;持续失败请附带 traceId 联系技术支持", "traceId": "on42619920260606015154"}, "expect": "进入平台识别链路", "passed": true, "verdict": "平台链路返回提示:平台业务拒绝", "error": null} +{"id": "TC19", "name": "side=front 别名", "type": "平台识别", "request": {"imageUrlOrBase64": "https://www.opsky.com.cn/upload/20211224/KXfgvm2MFRAXKbPu5LK.png", "side": "front"}, "httpCode": 200, "response": {"data": {}, "code": "200", "msg": "【驾驶证识别·并发限流】单位时间请求过于密集,已触发平台 QPS 限流,请拉长调用间隔后重试|定位信息:错误码=18,错误描述=Open api qps request limit reached,目标页面=正页(front/face)|处置指引:请在客户端增加限流/退避策略,避免瞬时并发过高", "traceId": "on92073220260606015154"}, "expect": "进入平台识别链路", "passed": true, "verdict": "平台链路返回提示:并发限流", "error": null}