|
|
@@ -0,0 +1,166 @@
|
|
|
+package com.malk.guyuan.controller;
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.malk.guyuan.server.model.McInvoiceDto;
|
|
|
+import com.malk.guyuan.server.model.McInvoiceKind;
|
|
|
+import com.malk.guyuan.server.tencent.TXYConf;
|
|
|
+import com.malk.guyuan.service.tencent.TXYInvoice;
|
|
|
+import com.malk.server.aliwork.YDConf;
|
|
|
+import com.malk.server.aliwork.YDParam;
|
|
|
+import com.malk.server.common.McException;
|
|
|
+import com.malk.server.common.McR;
|
|
|
+import com.malk.service.aliwork.YDClient;
|
|
|
+import com.malk.utils.UtilMap;
|
|
|
+import com.malk.utils.UtilMc;
|
|
|
+import com.malk.utils.UtilNumber;
|
|
|
+import com.tencentcloudapi.common.exception.TencentCloudSDKException;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+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;
|
|
|
+import org.springframework.web.bind.annotation.RestController;
|
|
|
+
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Optional;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 错误抛出与拦截详见CatchException
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@RestController
|
|
|
+@RequestMapping
|
|
|
+public class IVController {
|
|
|
+
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private TXYInvoice txyInvoice;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private YDClient ydClient;
|
|
|
+
|
|
|
+ private String findValue(List<Map<String, String>> infos, String... names) {
|
|
|
+ for (String name : names) {
|
|
|
+ Optional optional = infos.stream().filter(info -> info.get("Name").equals(name)).findAny();
|
|
|
+ if (optional.isPresent()) {
|
|
|
+ return String.valueOf(((Map) optional.get()).get("Value"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 混票识别
|
|
|
+ */
|
|
|
+ @PostMapping("invoice-iv")
|
|
|
+ McR invoice_iv(@RequestBody Map<String, String> data) throws TencentCloudSDKException {
|
|
|
+
|
|
|
+ McException.assertParamException_Null(data, "url");
|
|
|
+ String imageUrl = ydClient.convertTemporaryUrl(data.get("url"));
|
|
|
+ log.info("混票识别, 免登地址, {}", imageUrl);
|
|
|
+
|
|
|
+ // ppExt: 通用字段定义
|
|
|
+ List<Map> invoices = (List<Map>) txyInvoice.doMixedInvoiceOCR(imageUrl).get("MixedInvoiceItems");
|
|
|
+ List<McInvoiceDto> result = invoices.stream().map(item -> {
|
|
|
+ String kind = TXYConf.TYPE_INVOICE.get(item.get("Type").toString());
|
|
|
+ List<Map<String, String>> infos = (List<Map<String, String>>) item.get("SingleInvoiceInfos");
|
|
|
+
|
|
|
+ McInvoiceDto.assertSuccess(item, kind); // 响应断言
|
|
|
+
|
|
|
+ String invoiceName = findValue(infos, "发票名称");
|
|
|
+ if (kind.equals("全电发票")) {
|
|
|
+ kind = invoiceName.contains("增值税专用发票") ? "增值税电子专用发票" : "增值税电子普通发票";
|
|
|
+ }
|
|
|
+ if (kind.equals("增值税发票")) {
|
|
|
+ kind = invoiceName.contains("增值税专用发票") ? "增值税专用发票" : "增值税普通发票";
|
|
|
+ }
|
|
|
+ McInvoiceDto invoiceDto = McInvoiceDto.builder()
|
|
|
+ .name(invoiceName)
|
|
|
+ .kindName(kind)
|
|
|
+ .kind(McInvoiceKind.getKindCode(kind))
|
|
|
+ .code(findValue(infos, "发票代码", "票据代码")) // 发票, 非税发票
|
|
|
+ // 储存唯一ID [发票, 火车票, 行程单]
|
|
|
+ .serial(findValue(infos, "发票号码", "编号", "电子客票号码", "票据号码").replace("No", "")) // 发票, 非税发票
|
|
|
+ .date(findValue(infos, "开票日期").replace("年", "-").replace("月", "-").replace("日", ""))
|
|
|
+ .checkCode(findValue(infos, "校验码"))
|
|
|
+ .amount(new BigDecimal(UtilNumber.replaceCurrencyCHY(findValue(infos, "小写金额", "价税合计(小写)", "合计金额", "票价", "金额")))) // 发票, 全电票, 行程单, 火车票, 过路过桥费
|
|
|
+ .excludingTax(new BigDecimal(findValue(infos, "金额", "票价", "小写金额"))) // 行程单, 火车票, 定额发票
|
|
|
+ .buyerName(findValue(infos, "购买方名称", "交款人")) // 发票, 非税发票
|
|
|
+ .buyerTaxId(findValue(infos, "购买方识别号", "购买方统一社会信用代码/纳税人识别号", "交款人统一社会信用代码")) // 发票, 全电票, 非税发票
|
|
|
+ .sellerName(findValue(infos, "销售方名称", "填开单位")) // 行程单
|
|
|
+ .sellerTaxId(findValue(infos, "销售方识别号", "销售方统一社会信用代码/纳税人识别号", "销售单位代号")) // 发票, 全电票, 行程单
|
|
|
+ .passengerName(findValue(infos, "旅客姓名", "姓名")) // 行程单, 火车票
|
|
|
+ .seatType(findValue(infos, "座位等级", "席别")) // 行程单, 火车票
|
|
|
+ .departurePort(findValue(infos, "始发地", "出发站", "入口")) // 行程单, 火车票, 过路过桥费
|
|
|
+ .arrivePort(findValue(infos, "目的地", "到达站", "出口")) // 行程单, 火车票, 过路过桥费
|
|
|
+ .trainNo(findValue(infos, "航班号", "车次", "车牌号")) // 行程单, 火车票, 出租车
|
|
|
+ .insuranceCosts(UtilNumber.setBigDecimal((findValue(infos, "保险费")))) // 行程单
|
|
|
+ .fuelCosts(UtilNumber.setBigDecimal((findValue(infos, "燃油附加费")))) // 行程单
|
|
|
+ .constructionCosts(UtilNumber.setBigDecimal((findValue(infos, "民航发展基金")))) // 行程单
|
|
|
+ .build();
|
|
|
+ // 机票行程单
|
|
|
+ if (kind.equals(McInvoiceKind.JP.getDesc())) {
|
|
|
+ String date = findValue(infos, "日期").replace("年", "-").replace("月", "-").replace("日", " ");
|
|
|
+ invoiceDto.setDepartureTime(date + " " + findValue(infos, "时间"));
|
|
|
+ }
|
|
|
+ // 火车票
|
|
|
+ if (kind.equals(McInvoiceKind.HC.getDesc())) {
|
|
|
+ invoiceDto.setDepartureTime(findValue(infos, "出发时间").replace("年", "-").replace("月", "-").replace("日", " "));
|
|
|
+ }
|
|
|
+ // 出租车
|
|
|
+ if (kind.equals(McInvoiceKind.CZC.getDesc())) {
|
|
|
+ String date = findValue(infos, "日期").replace("年", "-").replace("月", "-").replace("日", " ");
|
|
|
+ invoiceDto.setDepartureTime(date + " " + findValue(infos, "上车"));
|
|
|
+ }
|
|
|
+ return invoiceDto;
|
|
|
+ }).collect(Collectors.toList());
|
|
|
+ return McR.success(McInvoiceDto.formatResponse(result));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 发票查重, 验真
|
|
|
+ */
|
|
|
+ @PostMapping("invoice-va")
|
|
|
+ McR invoice_va(@RequestBody Map data) {
|
|
|
+ McException.assertParamException_Null(data, "param");
|
|
|
+ List<McInvoiceDto> invoices = JSON.parseArray(JSON.toJSONString(data.get("param")), McInvoiceDto.class);
|
|
|
+
|
|
|
+ log.info("发票查重, 验真, {}", invoices);
|
|
|
+ invoices.forEach(UtilMc.consumerWithIndex((item, index) -> {
|
|
|
+
|
|
|
+ McInvoiceDto dto = (McInvoiceDto) item;
|
|
|
+ String invoiceNo = dto.getSerial(); // 唯一标识, 发票号码
|
|
|
+
|
|
|
+ String serial = "第【" + (index + 1) + "】张发票";
|
|
|
+ McException.assertAccessException(StringUtils.isBlank(invoiceNo), serial + ", 识别结果为空, 请检查!");
|
|
|
+ YDParam ydParam = YDParam.builder()
|
|
|
+ .formUuid("FORM-W2A66Z910O9B3LP9C6IYUDPRVWY62DO0YHIILY")
|
|
|
+ .searchFieldJson(JSON.toJSONString(UtilMap.map("radioField_liihyrtb, textField_liihyrt8", "否", invoiceNo)))
|
|
|
+ .build();
|
|
|
+ List<String> idList = (List<String>) ydClient.queryData(ydParam, YDConf.FORM_QUERY.retrieve_search_form_id).getData();
|
|
|
+ if (idList.size() > 0) {
|
|
|
+ McException.exceptionAccess(serial + "已存在, 请勿重复提交!");
|
|
|
+ }
|
|
|
+ // prd 仅仅识别增值税普通发票
|
|
|
+ if (dto.getKindName().contains("普通发票")) {
|
|
|
+ try {
|
|
|
+ // ppExt: 识别与验真后抬头对比
|
|
|
+ Map rsp = txyInvoice.VatInvoiceVerifyNew(dto.getKindName(), dto.getCode(), invoiceNo, dto.getDate(), String.valueOf(dto.getAmount()), dto.getCheckCode(), String.valueOf(dto.getExcludingTax()));
|
|
|
+ Map invoice = (Map) rsp.get("Invoice");
|
|
|
+ McException.assertAccessException(!dto.getBuyerName().equals(invoice.get("BuyerName")), "发票有疑问, 购买方名称不匹配!");
|
|
|
+ McException.assertAccessException(!dto.getBuyerTaxId().equals(invoice.get("BuyerTaxCode")), "发票有疑问, 购买方税号不匹配!");
|
|
|
+ McException.assertAccessException(!dto.getSellerName().equals(invoice.get("SellerName")), "发票有疑问, 销售方名称不匹配!");
|
|
|
+ McException.assertAccessException(!dto.getSellerTaxId().equals(invoice.get("SellerTaxCode")), "发票有疑问, 销售方税号不匹配!");
|
|
|
+ } catch (TencentCloudSDKException e) {
|
|
|
+ McException.exceptionAccess(serial + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }));
|
|
|
+
|
|
|
+ return McR.success("发票查重, 验真响应");
|
|
|
+ }
|
|
|
+}
|