package com.malk.xzkj.service.impl; import com.alibaba.fastjson.JSON; import com.malk.server.common.McException; import com.malk.utils.UtilMap; import com.malk.utils.UtilNumber; import com.malk.xzkj.config.TXYConf; import com.malk.xzkj.service.TXYInvoice; import com.tencentcloudapi.common.Credential; import com.tencentcloudapi.common.exception.TencentCloudSDKException; import com.tencentcloudapi.common.profile.ClientProfile; import com.tencentcloudapi.common.profile.HttpProfile; import com.tencentcloudapi.ocr.v20181119.OcrClient; import com.tencentcloudapi.ocr.v20181119.models.*; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.Map; @Slf4j @Service public class TXYImplInvoice implements TXYInvoice { @Autowired private TXYConf txyConf; // 创建请求描述 private ClientProfile doRequest(String endPoint) { HttpProfile httpProfile = new HttpProfile(); httpProfile.setEndpoint(endPoint); ClientProfile clientProfile = new ClientProfile(); clientProfile.setHttpProfile(httpProfile); return clientProfile; } /** * 混贴票据识别 * * @apiNote https://cloud.tencent.com/document/product/866/37835 */ @Override public Map doMixedInvoiceOCR(String image) throws TencentCloudSDKException { Credential cred = new Credential(txyConf.getSecretId(), txyConf.getSecretKey()); ClientProfile clientProfile = doRequest("ocr.tencentcloudapi.com"); OcrClient client = new OcrClient(cred, txyConf.getRegion(), clientProfile); MixedInvoiceOCRRequest req = new MixedInvoiceOCRRequest(); if (image.startsWith("http")) { req.setImageUrl(image); } else { req.setImageBase64(image); } req.setReturnMultiplePage(true); // PDF多页识别, 仅支持返回文件前30页 //log.debug("请求参数, {}", JSON.toJSONString(req)); MixedInvoiceOCRResponse resp = client.MixedInvoiceOCR(req); String result = MixedInvoiceOCRResponse.toJsonString(resp); log.debug("请求响应, {}", result); return (Map) JSON.parse(result); } /** * 通用票据识别(高级版) * * @apiNote https://cloud.tencent.com/document/api/866/90802 */ @Override public Map doRecognizeGeneralInvoice(String image) throws TencentCloudSDKException { Credential cred = new Credential(txyConf.getSecretId(), txyConf.getSecretKey()); ClientProfile clientProfile = doRequest("ocr.tencentcloudapi.com"); OcrClient client = new OcrClient(cred, txyConf.getRegion(), clientProfile); RecognizeGeneralInvoiceRequest req = new RecognizeGeneralInvoiceRequest(); if (image.startsWith("http")) { req.setImageUrl(image); } else { req.setImageBase64(image); } req.setEnableMultiplePage(true); // PDF多页识别, 仅支持返回文件前30页 req.setEnableCutImage(false); // 返回切割图片base64 RecognizeGeneralInvoiceResponse resp = client.RecognizeGeneralInvoice(req); String result = MixedInvoiceOCRResponse.toJsonString(resp); log.debug("请求响应, {}", result); return (Map) JSON.parse(result); } /** * 发票验真[新版] * * @apiNote https://cloud.tencent.com/document/product/866/73674 */ @Override public Map doVatInvoiceVerifyNew(String invoiceKind, String invoiceCode, String invoiceNo, String invoiceDate, String amount, String checkCode, String excludingTax, String tips) throws TencentCloudSDKException { Credential cred = new Credential(txyConf.getSecretId(), txyConf.getSecretKey()); ClientProfile clientProfile = doRequest("ocr.tencentcloudapi.com"); OcrClient client = new OcrClient(cred, txyConf.getRegion(), clientProfile); // 一些特殊发票校验码只有5位, 兼容 if (StringUtils.isNotBlank(checkCode) && checkCode.length() > 6) { checkCode = checkCode.substring(checkCode.length() - 6); } VatInvoiceVerifyNewRequest req = new VatInvoiceVerifyNewRequest(); req.setInvoiceNo(invoiceNo); req.setInvoiceDate(invoiceDate); req.setInvoiceCode(invoiceCode); req.setCheckCode(checkCode); // ppExt: 全电票, 新版本识别接口, 返回名称为: 电子发票(普通发票) 不包含全电标识, 发类型为: 全电发票. 注意取值 if (invoiceKind.contains("全电")) { // 全电票, 需要价税合计且无发票代码与校验码 req.setCheckCode(null); req.setInvoiceCode(null); req.setAmount(amount); } else { req.setAmount(null); req.setAmount(excludingTax); } log.debug("发票验真, {}, {}", invoiceKind, JSON.toJSONString(req)); VatInvoiceVerifyNewResponse resp = client.VatInvoiceVerifyNew(req); String result = VatInvoiceVerifyNewResponse.toJsonString(resp); Map rsp = (Map) JSON.parse(result); // 因全电票取值, 取值价税合计, 单独校验下金额 Map invoice = (Map) rsp.get("Invoice"); if (StringUtils.isBlank(tips)) { tips = "发票有疑问"; } log.debug("请求响应, {}", result); McException.assertAccessException(!UtilNumber.equalBigDecimal(UtilMap.getString(invoice, "AmountWithTax"), amount), tips + ", 价税合计金额不匹配!"); // ppExt: 增值税卷票: 票面无税率, 税额. 但接口验证返回或本质上发票是有税率, 税额. 因此取消后置判断 McException.assertAccessException(!invoiceKind.contains("卷票") && !UtilNumber.equalBigDecimal(UtilMap.getString(invoice, "AmountWithoutTax"), excludingTax), tips + ", 不含税金额不匹配!"); return rsp; } /** * 名片识别 * * @apiNote https://console.cloud.tencent.com/api/explorer?Product=ocr&Version=2018-11-19&Action=BusinessCardOCR */ @Override public Map doBusinessCardOCR(String image) throws TencentCloudSDKException { Credential cred = new Credential(txyConf.getSecretId(), txyConf.getSecretKey()); ClientProfile clientProfile = doRequest("ocr.tencentcloudapi.com"); OcrClient client = new OcrClient(cred, txyConf.getRegion(), clientProfile); BusinessCardOCRRequest req = new BusinessCardOCRRequest(); if (image.startsWith("http")) { req.setImageUrl(image); } else { req.setImageBase64(image); } req.setConfig(JSON.toJSONString(UtilMap.map("RetImageType", "PROPROCESS"))); //log.debug("请求参数, {}", JSON.toJSONString(req)); BusinessCardOCRResponse resp = client.BusinessCardOCR(req); String result = BusinessCardOCRResponse.toJsonString(resp); log.debug("请求响应, {}", result); return (Map) JSON.parse(result); } }