From ed11a1871b0b92cf93218c66609a83c2ae5ed54b Mon Sep 17 00:00:00 2001 From: quyixiao <2621048238@qq.com> Date: Thu, 21 May 2026 09:27:57 +0800 Subject: [PATCH] rjquwhtn --- .../heyu/api/controller/BaseController.java | 9 +- .../car/RecognizeDriverLicenseController.java | 103 ++++++++++--- .../RecognizeDrivingLicenseController.java | 5 +- .../car/RecognizeLicensePlateController.java | 121 +++++++++++----- .../car/RecognizeTaxiInvoiceController.java | 137 ++++++++++++------ .../car/RecognizeTrainTicketController.java | 107 ++++++++------ .../resp/car/RecognizeTaxiInvoiceResp.java | 45 ++++++ 7 files changed, 377 insertions(+), 150 deletions(-) diff --git a/api-web/api-interface/src/main/java/com/heyu/api/controller/BaseController.java b/api-web/api-interface/src/main/java/com/heyu/api/controller/BaseController.java index 1fea33c..14f376e 100644 --- a/api-web/api-interface/src/main/java/com/heyu/api/controller/BaseController.java +++ b/api-web/api-interface/src/main/java/com/heyu/api/controller/BaseController.java @@ -20,7 +20,6 @@ import java.util.Map; @SuppressWarnings({"unchecked", "deprecation"}) public class BaseController { - private static final String VEHICLE_LICENSE_URI = "/rest/2.0/ocr/v1/vehicle_license"; private static final String BAIDU_API_KEY = "zs9oN4gSuoS3eK8dVJg6jyKh"; private static final String BAIDU_SECRET_KEY = "uHIRXkj6rbW1eXy8eRVCeP1e3cRQKXay"; private static final OkHttpClient HTTP_CLIENT = new OkHttpClient().newBuilder().build(); @@ -56,7 +55,7 @@ public class BaseController { return value == null || value.trim().length() == 0; } - protected Map requestBaidu(String content) { + protected Map requestBaidu(String uri, String content) { String result = null; try { if (StringUtils.isBlank(content)) { @@ -67,7 +66,7 @@ public class BaseController { content = content.substring(1); } - Request baiduRequest = createBaiduRequest(content); + Request baiduRequest = createBaiduRequest(uri, content); try (Response response = HTTP_CLIENT.newCall(baiduRequest).execute()) { result = response.body().string(); return com.alibaba.fastjson.JSONObject.parseObject(result, Map.class); @@ -81,11 +80,11 @@ public class BaseController { return null; } - private Request createBaiduRequest(String content) { + private Request createBaiduRequest(String uri, String content) { MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded"); okhttp3.RequestBody body = okhttp3.RequestBody.create(mediaType, content); return new Request.Builder() - .url("https://aip.baidubce.com/" + VEHICLE_LICENSE_URI + "?access_token=" + getBaiduAccessToken()) + .url("https://aip.baidubce.com/" + uri + "?access_token=" + getBaiduAccessToken()) .method("POST", body) .addHeader("Content-Type", "application/x-www-form-urlencoded") .addHeader("Accept", "application/json") diff --git a/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeDriverLicenseController.java b/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeDriverLicenseController.java index 34129a1..87ac12c 100644 --- a/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeDriverLicenseController.java +++ b/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeDriverLicenseController.java @@ -1,11 +1,9 @@ package com.heyu.api.controller.car; -import com.heyu.api.baidu.handle.traffic.BDriverLicenseHandle; import com.heyu.api.baidu.request.traffic.BDriverLicenseRequest; import com.heyu.api.controller.BaseController; import com.heyu.api.data.annotation.EbAuthentication; import com.heyu.api.data.constants.ApiConstants; -import com.heyu.api.data.utils.ApiR; import com.heyu.api.data.utils.MapUtils; import com.heyu.api.data.utils.R; import com.heyu.api.data.utils.StringUtils; @@ -13,7 +11,6 @@ import com.heyu.api.request.car.DriverLicenseRecognizeRequest; import com.heyu.api.resp.car.RecognizeDriverLicenseBackResp; import com.heyu.api.resp.car.RecognizeDriverLicenseFaceResp; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -37,37 +34,32 @@ import java.util.Map; @SuppressWarnings("unchecked") public class RecognizeDriverLicenseController extends BaseController { - @Autowired - private BDriverLicenseHandle bDriverLicenseHandle; + private static final String DRIVER_LICENSE_URI = "/rest/2.0/ocr/v1/driving_license"; @EbAuthentication(tencent = ApiConstants.TENCENT_AUTH) @PostMapping("/recognize") - public R recognize(@RequestBody DriverLicenseRecognizeRequest request) { + public R recognize(@RequestBody DriverLicenseRecognizeRequest request) { long start = System.currentTimeMillis(); - if (request == null) { - return R.error("请求参数不能为空"); - } - if (isBlank(request.getImageBase64()) && isBlank(request.getImageUrl())) { - return R.error("imageBase64和imageUrl不能同时为空"); - } - log.info("驾驶证识别-参数校验耗时:{}ms", System.currentTimeMillis() - start); - long t1 = System.currentTimeMillis(); BDriverLicenseRequest bRequest = toBaiduRequest(request); - log.info("驾驶证识别-构建百度请求耗时:{}ms", System.currentTimeMillis() - t1); + String checkMsg = checkRequest(bRequest); + if (!isBlank(checkMsg)) { + return R.error(checkMsg); + } + log.info("驾驶证识别-参数校验及构建请求耗时:{}ms", System.currentTimeMillis() - t1); long t2 = System.currentTimeMillis(); - ApiR apiR = bDriverLicenseHandle.handle(bRequest); + Map data = requestBaidu(DRIVER_LICENSE_URI, getContent(bRequest)); long baiduCost = System.currentTimeMillis() - t2; log.info("驾驶证识别-调用百度OCR接口耗时:{}ms", baiduCost); - if (apiR == null || !apiR.isSuccess() || apiR.getData() == null) { - return R.error(thirdError(apiR)); + if (data == null) { + log.info("驾驶证识别-识别失败, 耗时:{}ms", System.currentTimeMillis() - start); + return R.error("驾驶证识别失败"); } long t3 = System.currentTimeMillis(); - Map data = apiR.getData(); Object resp; if (ApiConstants.face.equals(request.getSide())) { resp = toFaceResp(data); @@ -82,6 +74,9 @@ public class RecognizeDriverLicenseController extends BaseController { private BDriverLicenseRequest toBaiduRequest(DriverLicenseRecognizeRequest request) { BDriverLicenseRequest bRequest = new BDriverLicenseRequest(); + if (request == null) { + return bRequest; + } bRequest.setImageBase64(request.getImageBase64()); bRequest.setImageUrl(request.getImageUrl()); @@ -96,6 +91,76 @@ public class RecognizeDriverLicenseController extends BaseController { return bRequest; } + private String checkRequest(BDriverLicenseRequest request) { + if (!hasImage(request)) { + return "imageUrl和imageBase64不能同时为空"; + } + if (checkNotTrueFalse(request.getDetectDirection())) { + return "detectDirection 必须传 " + ApiConstants.trueOrFalse + + ",false:默认值,不检测朝向, true:检测朝向"; + } + if (checkNotFrontBack(request.getDrivingLicenseSide())) { + return "drivingLicenseSide 必须传 " + ApiConstants.frontOrback + + ",front:识别驾驶证正页、电子驾驶证正页, back:识别驾驶证副页"; + } + if (checkNotTrueFalse(request.getUnifiedValidPeriod())) { + return "unifiedValidPeriod 必须传 " + ApiConstants.trueOrFalse + + ",false:默认值,不进行归一化处理, true:归一化格式输出"; + } + if (checkNotTrueFalse(request.getQualityWarn())) { + return "qualityWarn 必须传 " + ApiConstants.trueOrFalse + + ",false:不输出质量告警信息, true:输出遮挡、不完整质量告警信息"; + } + if (checkNotTrueFalse(request.getRiskWarn())) { + return "riskWarn 必须传 " + ApiConstants.trueOrFalse + + ",false:不输出风险告警信息, true:开启,输出驾驶正复印、翻拍、PS等告警信息"; + } + return null; + } + + private boolean hasImage(BDriverLicenseRequest request) { + return request != null && (!isBlank(request.getImageUrl()) || !isBlank(request.getImageBase64())); + } + + private String getContent(BDriverLicenseRequest request) { + StringBuffer sb = getImageContent(request); + if (StringUtils.isNotBlank(request.getDetectDirection())) { + sb.append("&detect_direction=").append(request.getDetectDirection()); + } + if (StringUtils.isNotBlank(request.getDrivingLicenseSide())) { + sb.append("&driving_license_side=").append(request.getDrivingLicenseSide()); + } + if (StringUtils.isNotBlank(request.getUnifiedValidPeriod())) { + sb.append("&unified_valid_period=").append(request.getUnifiedValidPeriod()); + } + if (StringUtils.isNotBlank(request.getQualityWarn())) { + sb.append("&quality_warn=").append(request.getQualityWarn()); + } + if (StringUtils.isNotBlank(request.getRiskWarn())) { + sb.append("&risk_warn=").append(request.getRiskWarn()); + } + return sb.toString(); + } + + private StringBuffer getImageContent(BDriverLicenseRequest request) { + StringBuffer sb = new StringBuffer(); + if (StringUtils.isNotBlank(request.getImageBase64())) { + sb.append("&image=").append(request.getImageBase64()); + } + if (StringUtils.isNotBlank(request.getImageUrl())) { + sb.append("&url=").append(request.getImageUrl()); + } + return sb; + } + + private boolean checkNotTrueFalse(String value) { + return !ApiConstants.trueOrFalse.contains(value); + } + + private boolean checkNotFrontBack(String value) { + return !ApiConstants.frontOrback.contains(value); + } + private RecognizeDriverLicenseFaceResp toFaceResp(Map data) { RecognizeDriverLicenseFaceResp resp = new RecognizeDriverLicenseFaceResp(); diff --git a/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeDrivingLicenseController.java b/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeDrivingLicenseController.java index e0d7760..556248d 100644 --- a/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeDrivingLicenseController.java +++ b/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeDrivingLicenseController.java @@ -3,7 +3,6 @@ package com.heyu.api.controller.car; import com.heyu.api.baidu.request.traffic.BVehicleLicenseRequest; import com.heyu.api.controller.BaseController; import com.heyu.api.data.annotation.EbAuthentication; -import com.heyu.api.data.annotation.NotIntercept; import com.heyu.api.data.constants.ApiConstants; import com.heyu.api.data.utils.MapUtils; import com.heyu.api.data.utils.R; @@ -43,6 +42,8 @@ import java.util.Map; @SuppressWarnings("unchecked") public class RecognizeDrivingLicenseController extends BaseController { + private static final String VEHICLE_LICENSE_URI = "/rest/2.0/ocr/v1/vehicle_license"; + /** * 行驶证识别接口 * 支持行驶证正反面识别,使用百度云OCR接口 @@ -64,7 +65,7 @@ public class RecognizeDrivingLicenseController extends BaseController { log.info("行驶证识别-参数校验及构建请求耗时:{}ms", System.currentTimeMillis() - t1); long t2 = System.currentTimeMillis(); - Map data = requestBaidu(getContent(bVehicleLicenseRequest)); + Map data = requestBaidu(VEHICLE_LICENSE_URI, getContent(bVehicleLicenseRequest)); long baiduCost = System.currentTimeMillis() - t2; log.info("行驶证识别-调用百度OCR接口耗时:{}ms", baiduCost); diff --git a/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeLicensePlateController.java b/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeLicensePlateController.java index 9b40376..f5451d4 100644 --- a/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeLicensePlateController.java +++ b/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeLicensePlateController.java @@ -1,17 +1,15 @@ package com.heyu.api.controller.car; -import com.heyu.api.baidu.handle.traffic.BLicensePlateHandle; import com.heyu.api.baidu.request.traffic.BLicensePlateRequest; -import com.heyu.api.baidu.response.traffic.BLicensePlateResp; import com.heyu.api.controller.BaseController; import com.heyu.api.data.annotation.NotIntercept; -import com.heyu.api.data.utils.ApiR; +import com.heyu.api.data.utils.MapUtils; import com.heyu.api.data.utils.R; +import com.heyu.api.data.utils.StringUtils; import com.heyu.api.request.car.LicensePlateRecognizeRequest; import com.heyu.api.resp.car.RecognizeLicensePlateResp; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -19,8 +17,8 @@ import org.springframework.web.bind.annotation.RestController; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.Map; /** * 车牌识别(百度OCR) @@ -35,56 +33,52 @@ import java.util.List; @RestController @RequestMapping("/car/license/plate") @NotIntercept +@SuppressWarnings("unchecked") public class RecognizeLicensePlateController extends BaseController { - @Autowired - private BLicensePlateHandle bLicensePlateHandle; + private static final String LICENSE_PLATE_URI = "/rest/2.0/ocr/v1/license_plate"; @PostMapping("/recognize") - public R recognize(@RequestBody LicensePlateRecognizeRequest request) { + public R recognize(@RequestBody LicensePlateRecognizeRequest request) { long start = System.currentTimeMillis(); - if (request == null) { - return R.error("请求参数不能为空"); - } - if (isBlank(request.getImageBase64()) && isBlank(request.getImageUrl())) { - return R.error("imageBase64和imageUrl不能同时为空"); - } - log.info("车牌识别-参数校验耗时:{}ms", System.currentTimeMillis() - start); - long t1 = System.currentTimeMillis(); BLicensePlateRequest bRequest = toBaiduRequest(request); - log.info("车牌识别-构建百度请求耗时:{}ms", System.currentTimeMillis() - t1); + String checkMsg = checkRequest(bRequest); + if (!isBlank(checkMsg)) { + return R.error(checkMsg); + } + log.info("车牌识别-参数校验及构建请求耗时:{}ms", System.currentTimeMillis() - t1); long t2 = System.currentTimeMillis(); - ApiR apiR = bLicensePlateHandle.handle(bRequest); + Map data = requestBaidu(LICENSE_PLATE_URI, getContent(bRequest)); long baiduCost = System.currentTimeMillis() - t2; log.info("车牌识别-调用百度OCR接口耗时:{}ms", baiduCost); - if (apiR == null || !apiR.isSuccess() || apiR.getData() == null) { - return R.error(thirdError(apiR)); + if (data == null) { + log.info("车牌识别-识别失败, 耗时:{}ms", System.currentTimeMillis() - start); + return R.error("车牌识别失败"); } long t3 = System.currentTimeMillis(); - BLicensePlateResp bResp = apiR.getData(); - List plates = bResp.getWordsResult(); + List> plates = MapUtils.getByExpr(data, "words_result"); if (CollectionUtils.isEmpty(plates)) { log.info("车牌识别-未识别到车牌信息, 耗时:{}ms", System.currentTimeMillis() - start); return R.error("未识别到车牌信息"); } // 取第一张车牌 - BLicensePlateResp.WordsResultDTO plate = plates.get(0); + Map plate = plates.get(0); RecognizeLicensePlateResp resp = new RecognizeLicensePlateResp(); - resp.setPlateNumber(plate.getNumber()); - resp.setPlateType(mapPlateColor(plate.getColor())); + resp.setPlateNumber((String) plate.get("number")); + resp.setPlateType(mapPlateColor((String) plate.get("color"))); // 百度返回字符级置信度列表,取最小值作为整体置信度(保守策略),平均值作为车牌类型置信度 - List probabilities = plate.getProbability(); + List probabilities = (List) plate.get("probability"); if (CollectionUtils.isNotEmpty(probabilities)) { - double minConf = probabilities.stream().mapToDouble(Double::doubleValue).min().orElse(0.0); + double minConf = probabilities.stream().mapToDouble(Number::doubleValue).min().orElse(0.0); resp.setConfidence((float) minConf); - double avgConf = probabilities.stream().mapToDouble(Double::doubleValue).average().orElse(0.0); + double avgConf = probabilities.stream().mapToDouble(Number::doubleValue).average().orElse(0.0); resp.setPlateTypeConfidence((float) avgConf); } log.info("车牌识别-结果映射耗时:{}ms", System.currentTimeMillis() - t3); @@ -95,20 +89,81 @@ public class RecognizeLicensePlateController extends BaseController { private BLicensePlateRequest toBaiduRequest(LicensePlateRecognizeRequest request) { BLicensePlateRequest bRequest = new BLicensePlateRequest(); + if (request == null) { + return bRequest; + } // base64编码后进行urlencode是百度API的要求,因为base64的+/=在form-urlencoded中有特殊含义 - if (isNotBlank(request.getImageBase64())) { + if (StringUtils.isNotBlank(request.getImageBase64())) { bRequest.setImageBase64(encodeUrl(request.getImageBase64())); } // imageUrl 中可能包含 & 等特殊字符,需URL编码后再传给百度API,否则会被解析为form分隔符 - if (isNotBlank(request.getImageUrl())) { + if (StringUtils.isNotBlank(request.getImageUrl())) { bRequest.setImageUrl(encodeUrl(request.getImageUrl())); } - // detectComplete和detectRisk在BLicensePlateRequest中无默认值,handle的check()要求必须为true/false + // detectComplete和detectRisk在BLicensePlateRequest中无默认值,下方check()要求必须为true/false bRequest.setDetectComplete("false"); bRequest.setDetectRisk("false"); return bRequest; } + private String checkRequest(BLicensePlateRequest request) { + if (!hasImage(request)) { + return "imageUrl和imageBase64不能同时为空"; + } + if (checkNotTrueFalse(request.getMultiDetect())) { + return "multiDetect必须为true/false, 是否检测多张车牌,默认为false,当置为true的时候可以对一张图片内的多张车牌进行识别"; + } + if (checkNotTrueFalse(request.getMultiScale())) { + return "multiScale必须为true/false, 在高拍等车牌较小的场景下可开启,默认为false"; + } + if (checkNotTrueFalse(request.getDetectComplete())) { + return "detectComplete必须为true/false, 是否开启车牌遮挡检测功能"; + } + if (checkNotTrueFalse(request.getDetectRisk())) { + return "detectRisk必须为true/false, 是否开启车牌PS检测功能"; + } + return null; + } + + private boolean hasImage(BLicensePlateRequest request) { + return request != null && (!isBlank(request.getImageUrl()) || !isBlank(request.getImageBase64())); + } + + private String getContent(BLicensePlateRequest request) { + StringBuffer sb = getImageContent(request); + if (StringUtils.isNotBlank(request.getMultiDetect())) { + sb.append("&multi_detect=").append(request.getMultiDetect()); + } + if (StringUtils.isNotBlank(request.getMultiScale())) { + sb.append("&multi_scale=").append(request.getMultiScale()); + } + if (StringUtils.isNotBlank(request.getDetectComplete())) { + sb.append("&detect_complete=").append(request.getDetectComplete()); + } + if (StringUtils.isNotBlank(request.getDetectRisk())) { + sb.append("&detect_risk=").append(request.getDetectRisk()); + } + return sb.toString(); + } + + private StringBuffer getImageContent(BLicensePlateRequest request) { + StringBuffer sb = new StringBuffer(); + if (StringUtils.isNotBlank(request.getImageBase64())) { + sb.append("&image=").append(request.getImageBase64()); + } + if (StringUtils.isNotBlank(request.getImageUrl())) { + sb.append("&url=").append(request.getImageUrl()); + } + return sb; + } + + private boolean checkNotTrueFalse(String value) { + if (value == null) { + return false; + } + return !"true".equals(value) && !"false".equals(value); + } + private String encodeUrl(String url) { try { return URLEncoder.encode(url, "UTF-8"); @@ -118,10 +173,6 @@ public class RecognizeLicensePlateController extends BaseController { } } - private boolean isNotBlank(String value) { - return value != null && value.trim().length() > 0; - } - /** * 将百度车牌颜色映射为中文车牌类型 */ diff --git a/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeTaxiInvoiceController.java b/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeTaxiInvoiceController.java index 9aac6af..613b78a 100644 --- a/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeTaxiInvoiceController.java +++ b/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeTaxiInvoiceController.java @@ -1,16 +1,13 @@ package com.heyu.api.controller.car; -import com.heyu.api.baidu.handle.financial.BTaxiReceiptHandle; import com.heyu.api.baidu.request.financial.BTaxiReceiptRequest; -import com.heyu.api.baidu.response.financial.BTaxiReceiptResp; import com.heyu.api.controller.BaseController; -import com.heyu.api.data.annotation.NotIntercept; -import com.heyu.api.data.utils.ApiR; +import com.heyu.api.data.utils.MapUtils; import com.heyu.api.data.utils.R; +import com.heyu.api.data.utils.StringUtils; import com.heyu.api.request.car.TaxiInvoiceRecognizeRequest; import com.heyu.api.resp.car.RecognizeTaxiInvoiceResp; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -18,6 +15,7 @@ import org.springframework.web.bind.annotation.RestController; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; +import java.util.Map; /** * https://console.bce.baidu.com/support/#/api?product=AI&project=文字识别&parent=财务票据OCR&api=rest%2F2.0%2Focr%2Fv1%2Ftaxi_receipt&method=post @@ -26,49 +24,69 @@ import java.net.URLEncoder; * * 支持识别全国各大城市的出租车票的全部字段,包括发票号码、发票代码、电话、日期、金额、 * 上下车时间、里程等。 + * + * 接口返回参数: + *
+ * 参数              是否必须  类型      说明
+ * log_id            是        uint64    请求标识码,随机数,唯一。
+ * words_result_num  是        uint32    识别结果数,表示 words_result 的元素个数
+ * words_result      是        object{}  识别结果数组
+ *   + InvoiceCode           是  string  发票代码
+ *   + InvoiceNum            是  string  发票号码
+ *   + TaxiNum               是  string  车牌号
+ *   + Date                  是  string  日期
+ *   + Time                  是  string  上下车时间
+ *   + PickupTime            是  string  上车时间
+ *   + DropoffTime           是  string  下车时间
+ *   + Fare                  是  string  金额
+ *   + FuelOilSurcharge      是  string  燃油附加费
+ *   + CallServiceSurcharge  是  string  叫车服务费
+ *   + TotalFare             是  string  总金额
+ *   + Location              是  string  开票城市
+ *   + Province              是  string  省
+ *   + City                  是  string  市
+ *   + PricePerkm            是  string  单价
+ *   + Distance              是  string  里程
+ * pdf_file_size     否        string    传入PDF文件的总页数,当 pdf_file 参数有效时返回该字段
+ * 
*/ @Slf4j @RestController @RequestMapping("/taxi/invoice") -@NotIntercept +@SuppressWarnings("unchecked") public class RecognizeTaxiInvoiceController extends BaseController { - @Autowired - private BTaxiReceiptHandle bTaxiReceiptHandle; + private static final String TAXI_RECEIPT_URI = "/rest/2.0/ocr/v1/taxi_receipt"; @PostMapping("/recognize") - public R recognize(@RequestBody TaxiInvoiceRecognizeRequest request) { + public R recognize(@RequestBody TaxiInvoiceRecognizeRequest request) { long start = System.currentTimeMillis(); - if (request == null) { - return R.error("请求参数不能为空"); - } - if (isBlank(request.getImageBase64()) && isBlank(request.getImageUrl())) { - return R.error("imageBase64和imageUrl不能同时为空"); - } - log.info("出租车发票识别-参数校验耗时:{}ms", System.currentTimeMillis() - start); - long t1 = System.currentTimeMillis(); BTaxiReceiptRequest bRequest = toBaiduRequest(request); - log.info("出租车发票识别-构建百度请求耗时:{}ms", System.currentTimeMillis() - t1); + String checkMsg = checkRequest(bRequest); + if (!isBlank(checkMsg)) { + return R.error(checkMsg); + } + log.info("出租车发票识别-参数校验及构建请求耗时:{}ms", System.currentTimeMillis() - t1); long t2 = System.currentTimeMillis(); - ApiR apiR = bTaxiReceiptHandle.handle(bRequest); + Map data = requestBaidu(TAXI_RECEIPT_URI, getContent(bRequest)); long baiduCost = System.currentTimeMillis() - t2; log.info("出租车发票识别-调用百度OCR接口耗时:{}ms", baiduCost); - if (apiR == null || !apiR.isSuccess() || apiR.getData() == null) { - return R.error(thirdError(apiR)); + if (data == null) { + log.info("出租车发票识别-识别失败, 耗时:{}ms", System.currentTimeMillis() - start); + return R.error("出租车发票识别失败"); } - long t3 = System.currentTimeMillis(); - BTaxiReceiptResp bResp = apiR.getData(); - BTaxiReceiptResp.WordsResultDTO wordsResult = bResp.getWordsResult(); + Map wordsResult = MapUtils.getByExpr(data, "words_result"); if (wordsResult == null) { log.info("出租车发票识别-未识别到发票信息, 耗时:{}ms", System.currentTimeMillis() - start); return R.error("未识别到出租车发票信息"); } + long t3 = System.currentTimeMillis(); RecognizeTaxiInvoiceResp resp = toResp(wordsResult); log.info("出租车发票识别-结果映射耗时:{}ms", System.currentTimeMillis() - t3); @@ -78,33 +96,62 @@ public class RecognizeTaxiInvoiceController extends BaseController { private BTaxiReceiptRequest toBaiduRequest(TaxiInvoiceRecognizeRequest request) { BTaxiReceiptRequest bRequest = new BTaxiReceiptRequest(); - if (isNotBlank(request.getImageBase64())) { + if (request == null) { + return bRequest; + } + if (StringUtils.isNotBlank(request.getImageBase64())) { bRequest.setImageBase64(encodeParam(request.getImageBase64())); } - if (isNotBlank(request.getImageUrl())) { + if (StringUtils.isNotBlank(request.getImageUrl())) { bRequest.setImageUrl(encodeParam(request.getImageUrl())); } return bRequest; } - private RecognizeTaxiInvoiceResp toResp(BTaxiReceiptResp.WordsResultDTO wordsResult) { + private String checkRequest(BTaxiReceiptRequest request) { + if (!hasImage(request)) { + return "imageUrl和imageBase64不能同时为空"; + } + return null; + } + + private boolean hasImage(BTaxiReceiptRequest request) { + return request != null && (!isBlank(request.getImageUrl()) || !isBlank(request.getImageBase64())); + } + + private String getContent(BTaxiReceiptRequest request) { + return getImageContent(request).toString(); + } + + private StringBuffer getImageContent(BTaxiReceiptRequest request) { + StringBuffer sb = new StringBuffer(); + if (StringUtils.isNotBlank(request.getImageBase64())) { + sb.append("&image=").append(request.getImageBase64()); + } + if (StringUtils.isNotBlank(request.getImageUrl())) { + sb.append("&url=").append(request.getImageUrl()); + } + return sb; + } + + private RecognizeTaxiInvoiceResp toResp(Map wordsResult) { RecognizeTaxiInvoiceResp resp = new RecognizeTaxiInvoiceResp(); - resp.setDate(wordsResult.getDate()); - resp.setFare(wordsResult.getFare()); - resp.setLocation(wordsResult.getLocation()); - resp.setInvoiceCode(wordsResult.getInvoiceCode()); - resp.setInvoiceNum(wordsResult.getInvoiceNum()); - resp.setTaxiNum(wordsResult.getTaxiNum()); - resp.setTime(wordsResult.getTime()); - resp.setPickupTime(wordsResult.getPickupTime()); - resp.setDropoffTime(wordsResult.getDropoffTime()); - resp.setFuelOilSurcharge(wordsResult.getFuelOilSurcharge()); - resp.setCallServiceSurcharge(wordsResult.getCallServiceSurcharge()); - resp.setTotalFare(wordsResult.getTotalFare()); - resp.setProvince(wordsResult.getProvince()); - resp.setCity(wordsResult.getCity()); - resp.setPricePerkm(wordsResult.getPricePerkm()); - resp.setDistance(wordsResult.getDistance()); + resp.setDate((String) wordsResult.get("Date")); + resp.setFare((String) wordsResult.get("Fare")); + resp.setLocation((String) wordsResult.get("Location")); + resp.setInvoiceCode((String) wordsResult.get("InvoiceCode")); + resp.setInvoiceNum((String) wordsResult.get("InvoiceNum")); + resp.setTaxiNum((String) wordsResult.get("TaxiNum")); + resp.setTime((String) wordsResult.get("Time")); + resp.setPickupTime((String) wordsResult.get("PickupTime")); + resp.setDropoffTime((String) wordsResult.get("DropoffTime")); + resp.setFuelOilSurcharge((String) wordsResult.get("FuelOilSurcharge")); + resp.setCallServiceSurcharge((String) wordsResult.get("CallServiceSurcharge")); + resp.setTotalFare((String) wordsResult.get("TotalFare")); + resp.setProvince((String) wordsResult.get("Province")); + resp.setCity((String) wordsResult.get("City")); + resp.setPricePerkm((String) wordsResult.get("PricePerkm")); + resp.setDistance((String) wordsResult.get("Distance")); return resp; } @@ -116,8 +163,4 @@ public class RecognizeTaxiInvoiceController extends BaseController { return value; } } - - private boolean isNotBlank(String value) { - return value != null && value.trim().length() > 0; - } } diff --git a/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeTrainTicketController.java b/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeTrainTicketController.java index 8f64b3a..329547b 100644 --- a/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeTrainTicketController.java +++ b/api-web/api-interface/src/main/java/com/heyu/api/controller/car/RecognizeTrainTicketController.java @@ -1,16 +1,14 @@ package com.heyu.api.controller.car; -import com.heyu.api.baidu.handle.financial.BTrainTicketHandle; import com.heyu.api.baidu.request.financial.BTrainTicketRequest; -import com.heyu.api.baidu.response.financial.BTrainTicketResp; import com.heyu.api.controller.BaseController; import com.heyu.api.data.annotation.NotIntercept; -import com.heyu.api.data.utils.ApiR; +import com.heyu.api.data.utils.MapUtils; import com.heyu.api.data.utils.R; +import com.heyu.api.data.utils.StringUtils; import com.heyu.api.request.car.TrainTicketRecognizeRequest; import com.heyu.api.resp.car.RecognizeTrainTicketResp; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -18,6 +16,7 @@ import org.springframework.web.bind.annotation.RestController; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; +import java.util.Map; /** * https://console.bce.baidu.com/support/#/api?product=AI&project=文字识别&parent=财务票据OCR&api=rest%2F2.0%2Focr%2Fv1%2Ftrain_ticket&method=post @@ -31,44 +30,40 @@ import java.net.URLEncoder; @RestController @RequestMapping("/train/ticket") @NotIntercept +@SuppressWarnings("unchecked") public class RecognizeTrainTicketController extends BaseController { - @Autowired - private BTrainTicketHandle bTrainTicketHandle; + private static final String TRAIN_TICKET_URI = "/rest/2.0/ocr/v1/train_ticket"; @PostMapping("/recognize") - public R recognize(@RequestBody TrainTicketRecognizeRequest request) { + public R recognize(@RequestBody TrainTicketRecognizeRequest request) { long start = System.currentTimeMillis(); - if (request == null) { - return R.error("请求参数不能为空"); - } - if (isBlank(request.getImageBase64()) && isBlank(request.getImageUrl())) { - return R.error("imageBase64和imageUrl不能同时为空"); - } - log.info("火车票识别-参数校验耗时:{}ms", System.currentTimeMillis() - start); - long t1 = System.currentTimeMillis(); BTrainTicketRequest bRequest = toBaiduRequest(request); - log.info("火车票识别-构建百度请求耗时:{}ms", System.currentTimeMillis() - t1); + String checkMsg = checkRequest(bRequest); + if (!isBlank(checkMsg)) { + return R.error(checkMsg); + } + log.info("火车票识别-参数校验及构建请求耗时:{}ms", System.currentTimeMillis() - t1); long t2 = System.currentTimeMillis(); - ApiR apiR = bTrainTicketHandle.handle(bRequest); + Map data = requestBaidu(TRAIN_TICKET_URI, getContent(bRequest)); long baiduCost = System.currentTimeMillis() - t2; log.info("火车票识别-调用百度OCR接口耗时:{}ms", baiduCost); - if (apiR == null || !apiR.isSuccess() || apiR.getData() == null) { - return R.error(thirdError(apiR)); + if (data == null) { + log.info("火车票识别-识别失败, 耗时:{}ms", System.currentTimeMillis() - start); + return R.error("火车票识别失败"); } - long t3 = System.currentTimeMillis(); - BTrainTicketResp bResp = apiR.getData(); - BTrainTicketResp.WordsResultDTO wordsResult = bResp.getWordsResult(); + Map wordsResult = MapUtils.getByExpr(data, "words_result"); if (wordsResult == null) { log.info("火车票识别-未识别到火车票信息, 耗时:{}ms", System.currentTimeMillis() - start); return R.error("未识别到火车票信息"); } + long t3 = System.currentTimeMillis(); RecognizeTrainTicketResp resp = toResp(wordsResult); log.info("火车票识别-结果映射耗时:{}ms", System.currentTimeMillis() - t3); @@ -78,44 +73,76 @@ public class RecognizeTrainTicketController extends BaseController { private BTrainTicketRequest toBaiduRequest(TrainTicketRecognizeRequest request) { BTrainTicketRequest bRequest = new BTrainTicketRequest(); - if (isNotBlank(request.getImageBase64())) { + if (request == null) { + return bRequest; + } + if (StringUtils.isNotBlank(request.getImageBase64())) { bRequest.setImageBase64(encodeParam(request.getImageBase64())); } - if (isNotBlank(request.getImageUrl())) { + if (StringUtils.isNotBlank(request.getImageUrl())) { bRequest.setImageUrl(encodeParam(request.getImageUrl())); } return bRequest; } - private RecognizeTrainTicketResp toResp(BTrainTicketResp.WordsResultDTO words) { + private String checkRequest(BTrainTicketRequest request) { + if (!hasImage(request)) { + return "imageUrl和imageBase64不能同时为空"; + } + return null; + } + + private boolean hasImage(BTrainTicketRequest request) { + return request != null && (!isBlank(request.getImageUrl()) || !isBlank(request.getImageBase64())); + } + + private String getContent(BTrainTicketRequest request) { + return getImageContent(request).toString(); + } + + private StringBuffer getImageContent(BTrainTicketRequest request) { + StringBuffer sb = new StringBuffer(); + if (StringUtils.isNotBlank(request.getImageBase64())) { + sb.append("&image=").append(request.getImageBase64()); + } + if (StringUtils.isNotBlank(request.getImageUrl())) { + sb.append("&url=").append(request.getImageUrl()); + } + return sb; + } + + private RecognizeTrainTicketResp toResp(Map words) { RecognizeTrainTicketResp resp = new RecognizeTrainTicketResp(); - resp.setName(words.getName()); - resp.setNumber(words.getTrainNum()); - resp.setDepartureStation(words.getStartingStation()); - resp.setDestination(words.getDestinationStation()); - resp.setLevel(words.getSeatCategory()); - resp.setSeat(words.getSeatNum()); + resp.setName((String) words.get("name")); + resp.setNumber((String) words.get("train_num")); + resp.setDepartureStation((String) words.get("starting_station")); + resp.setDestination((String) words.get("destination_station")); + resp.setLevel((String) words.get("seat_category")); + resp.setSeat((String) words.get("seat_num")); // 日期和时间合并:百度返回独立date和time字段,合并为"2017年08月05日 22:09开"格式 + String date = (String) words.get("date"); + String time = (String) words.get("time"); StringBuilder dateTime = new StringBuilder(); - if (isNotBlank(words.getDate())) { - dateTime.append(words.getDate()); + if (StringUtils.isNotBlank(date)) { + dateTime.append(date); } - if (isNotBlank(words.getTime())) { + if (StringUtils.isNotBlank(time)) { if (dateTime.length() > 0) { dateTime.append(" "); } - dateTime.append(words.getTime()); + dateTime.append(time); } resp.setDate(dateTime.length() > 0 ? dateTime.toString() : null); // 票价:百度返回字符串,转为Float - if (isNotBlank(words.getTicketRates())) { + String ticketRates = (String) words.get("ticket_rates"); + if (StringUtils.isNotBlank(ticketRates)) { try { - resp.setPrice(Float.parseFloat(words.getTicketRates())); + resp.setPrice(Float.parseFloat(ticketRates)); } catch (NumberFormatException e) { - log.warn("票价解析失败:{}", words.getTicketRates()); + log.warn("票价解析失败:{}", ticketRates); resp.setPrice(null); } } @@ -131,8 +158,4 @@ public class RecognizeTrainTicketController extends BaseController { return value; } } - - private boolean isNotBlank(String value) { - return value != null && value.trim().length() > 0; - } } diff --git a/api-web/api-interface/src/main/java/com/heyu/api/resp/car/RecognizeTaxiInvoiceResp.java b/api-web/api-interface/src/main/java/com/heyu/api/resp/car/RecognizeTaxiInvoiceResp.java index 7a860dc..53e7423 100644 --- a/api-web/api-interface/src/main/java/com/heyu/api/resp/car/RecognizeTaxiInvoiceResp.java +++ b/api-web/api-interface/src/main/java/com/heyu/api/resp/car/RecognizeTaxiInvoiceResp.java @@ -5,24 +5,69 @@ import lombok.Data; import java.util.List; +/** + * 出租车发票识别响应(百度OCR) + * + * 接口返回参数: + *
+ * 参数              是否必须  类型      说明
+ * log_id            是        uint64    请求标识码,随机数,唯一。
+ * words_result_num  是        uint32    识别结果数,表示 words_result 的元素个数
+ * words_result      是        object{}  识别结果数组
+ *   + InvoiceCode           是  string  发票代码
+ *   + InvoiceNum            是  string  发票号码
+ *   + TaxiNum               是  string  车牌号
+ *   + Date                  是  string  日期
+ *   + Time                  是  string  上下车时间
+ *   + PickupTime            是  string  上车时间
+ *   + DropoffTime           是  string  下车时间
+ *   + Fare                  是  string  金额
+ *   + FuelOilSurcharge      是  string  燃油附加费
+ *   + CallServiceSurcharge  是  string  叫车服务费
+ *   + TotalFare             是  string  总金额
+ *   + Location              是  string  开票城市
+ *   + Province              是  string  省
+ *   + City                  是  string  市
+ *   + PricePerkm            是  string  单价
+ *   + Distance              是  string  里程
+ * pdf_file_size     否        string    传入PDF文件的总页数,当 pdf_file 参数有效时返回该字段
+ * 
+ */ @Data public class RecognizeTaxiInvoiceResp extends BaseResp { + /** 日期 (对应百度返回字段 Date) */ private String date; + /** 金额 (对应百度返回字段 Fare) */ private String fare; + /** 开票城市 (对应百度返回字段 Location) */ private String location; + /** 发票代码 (对应百度返回字段 InvoiceCode) */ private String invoiceCode; + /** 发票号码 (对应百度返回字段 InvoiceNum) */ private String invoiceNum; + /** 车牌号 (对应百度返回字段 TaxiNum) */ private String taxiNum; + /** 上下车时间 (对应百度返回字段 Time) */ private String time; + /** 上车时间 (对应百度返回字段 PickupTime) */ private String pickupTime; + /** 下车时间 (对应百度返回字段 DropoffTime) */ private String dropoffTime; + /** 燃油附加费 (对应百度返回字段 FuelOilSurcharge) */ private String fuelOilSurcharge; + /** 叫车服务费 (对应百度返回字段 CallServiceSurcharge) */ private String callServiceSurcharge; + /** 总金额 (对应百度返回字段 TotalFare) */ private String totalFare; + /** 省 (对应百度返回字段 Province) */ private String province; + /** 市 (对应百度返回字段 City) */ private String city; + /** 单价 (对应百度返回字段 PricePerkm) */ private String pricePerkm; + /** 里程 (对应百度返回字段 Distance) */ private String distance; + /** 原始识别条目列表(保留扩展字段) */ private List rawItems; }