|
|
@@ -0,0 +1,682 @@
|
|
|
+package com.muzhi.lemeng.service.impl;
|
|
|
+
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.alibaba.fastjson.JSONArray;
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
+import com.muzhi.lemeng.common.R;
|
|
|
+import com.muzhi.lemeng.common.alibaba.AlibabaMailBox;
|
|
|
+import com.muzhi.lemeng.common.dingtalkyida.YiDaFormData;
|
|
|
+import com.muzhi.lemeng.common.dingtalkyida.YiDaFormDataV2;
|
|
|
+import com.muzhi.lemeng.common.tencent.TencentInvoice;
|
|
|
+import com.muzhi.lemeng.common.time.BeijingToUtcConverter;
|
|
|
+import com.muzhi.lemeng.service.AccessTokenService;
|
|
|
+import com.muzhi.lemeng.service.AliBtripTrainTicketService;
|
|
|
+import com.muzhi.lemeng.service.AliBtripVatInvoiceService;
|
|
|
+import com.muzhi.lemeng.service.AlibabaMailBoxService;
|
|
|
+import com.tencentcloudapi.common.exception.TencentCloudSDKException;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import okhttp3.*;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import java.time.LocalDate;
|
|
|
+import java.time.ZoneId;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.HashSet;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Locale;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Set;
|
|
|
+
|
|
|
+
|
|
|
+@Service
|
|
|
+@Component
|
|
|
+@Slf4j
|
|
|
+public class AlibabaMailBoxServiceImpl implements AlibabaMailBoxService {
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private AccessTokenService accessTokenService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private AliBtripTrainTicketService aliBtripTrainTicketService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private AliBtripVatInvoiceService aliBtripVatInvoiceService;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 搜索邮件,并解析发票信息
|
|
|
+ * NOTE 邮件标题匹配各类发票邮件标识:【发票、电子发票、电子票】
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public R queryMessages(){
|
|
|
+
|
|
|
+ //TODO 指定代收邮箱地址
|
|
|
+ String emailAddress = "mailassistant@cnlemon.net";
|
|
|
+
|
|
|
+
|
|
|
+ /*搜索邮件*/
|
|
|
+ log.info("开始循环搜索邮件--->");
|
|
|
+ //邮件信息返回字段筛选
|
|
|
+ String select = "toRecipients";//邮件的接收人(可选字段:在URL中增加$select=toRecipients查询参数后返回,如需多个字段使用英文逗号分隔)
|
|
|
+ // 1. 获取当前日期
|
|
|
+// LocalDate today = LocalDate.now();
|
|
|
+ LocalDate yesterday = LocalDate.now().minusDays(1);//获取昨天的日期
|
|
|
+ // 2. 定义格式化器 (yyyy-MM-dd)
|
|
|
+ DateTimeFormatter formatterInput = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
|
+ // 3. 格式化日期
|
|
|
+ String inputDate = yesterday.format(formatterInput);
|
|
|
+ System.out.println("获取邮箱邮件的日期: " + inputDate);
|
|
|
+
|
|
|
+ //查询时段
|
|
|
+// inputDate = "2026-08-21"; //TODO 【测试】指定日期,测试完毕需要注释
|
|
|
+
|
|
|
+ Set<String> aliBtripInvoiceNumbers = queryAliBtripInvoiceNumbers(inputDate);
|
|
|
+
|
|
|
+ String[] range = BeijingToUtcConverter.convertBeijingDateToUtcRange(inputDate);
|
|
|
+ System.out.println("北京时间 " + inputDate + " 00:00:00 对应 UTC: " + range[0]);
|
|
|
+ System.out.println("北京时间 " + inputDate + " 23:59:59 对应 UTC: " + range[1]);
|
|
|
+ String startTime = range[0];//邮件发送时间
|
|
|
+ String endTime = range[1];//邮件发送时间
|
|
|
+
|
|
|
+ //TODO 【测试】指定日期,测试完毕需要注释
|
|
|
+// startTime = "2026-04-21T16:00:00Z";
|
|
|
+// endTime = "2026-08-05T15:59:59Z";
|
|
|
+
|
|
|
+ //构造查询条件
|
|
|
+ String folderId = "2";//邮件文件夹ID(2:指收件箱)
|
|
|
+ String query = "date>"+ startTime +" AND date<"+ endTime +" AND folderId:"+ folderId + " AND (subject:发票 OR subject:电子发票 OR subject:电子票)";//查询语句
|
|
|
+ //分页参数
|
|
|
+ String cursor = "0";//游标,邮件的起始偏移标识, 其值取自响应中的nextCursor字段。“”表示从头开始
|
|
|
+ int size = 100;//返回邮件数量,最大100
|
|
|
+
|
|
|
+ String nextCursor = "";
|
|
|
+ JSONArray messages = null;
|
|
|
+ do {
|
|
|
+ log.info("第{}次循环搜索邮件==========", cursor);
|
|
|
+ Map bodyMap = new HashMap();
|
|
|
+ bodyMap.put("query", query);
|
|
|
+ bodyMap.put("cursor", cursor);
|
|
|
+ bodyMap.put("size", size);
|
|
|
+ String bodyStr = JSON.toJSONString(bodyMap);
|
|
|
+ System.out.println("请求体body:" + bodyStr);
|
|
|
+ try {
|
|
|
+ OkHttpClient client = new OkHttpClient().newBuilder()
|
|
|
+ .build();
|
|
|
+ MediaType mediaType = MediaType.parse("application/json");
|
|
|
+ RequestBody body = RequestBody.create(mediaType, bodyStr);
|
|
|
+ Request request = new Request.Builder()
|
|
|
+ .url("https://alimail-cn.aliyuncs.com/v2/users/"+ emailAddress +"/messages/query?$select="+ select)
|
|
|
+ .method("POST", body)
|
|
|
+ .addHeader("Content-Type", "application/json")
|
|
|
+ .addHeader("Authorization", "Bearer " + accessTokenService.getAlibabaMailboxAccessToken())
|
|
|
+ .build();
|
|
|
+ Response response = client.newCall(request).execute();
|
|
|
+ String resStr = response.body().string();
|
|
|
+ JSONObject jsonObject = JSON.parseObject(resStr);
|
|
|
+ System.out.println("返回体resStr对象:" + jsonObject);
|
|
|
+ //获取邮件总数
|
|
|
+ int total = jsonObject.getInteger("total");
|
|
|
+ log.info("当前搜索到邮件总数total:" + total);
|
|
|
+ //下一页光标 $表示已经到达邮件列表尾部
|
|
|
+ nextCursor = jsonObject.getString("nextCursor");
|
|
|
+ //邮件列表
|
|
|
+ messages = jsonObject.getJSONArray("messages");
|
|
|
+ }catch (Exception e){
|
|
|
+ log.error("搜索邮件异常:{}", e);
|
|
|
+ }
|
|
|
+ if (messages.size() > 0){
|
|
|
+ for (int i = 0; i < messages.size(); i++) {
|
|
|
+ //邮件收件人数组信息
|
|
|
+ JSONArray toRecipients = messages.getJSONObject(i).getJSONArray("toRecipients");
|
|
|
+ JSONObject jsonObject1 = toRecipients.getJSONObject(0);
|
|
|
+ String email = jsonObject1.getString("email");
|
|
|
+ System.out.println("收件人email:" + email);
|
|
|
+ String name = jsonObject1.getString("name");
|
|
|
+ System.out.println("收件人name:" + name);
|
|
|
+ //邮件id
|
|
|
+ String emailId = messages.getJSONObject(i).getString("id");
|
|
|
+ System.out.println("邮件id:" + emailId);
|
|
|
+ /*根据邮箱地址获取收件人userId*/
|
|
|
+ //查询表单实例数据
|
|
|
+ Map mapAll = new HashMap();
|
|
|
+ //邮箱地址
|
|
|
+ mapAll.put("textField_mob3qfjb", email);
|
|
|
+ String mapAllStr = JSON.toJSONString(mapAll);
|
|
|
+ System.out.println("封装查询的字段参数:" + mapAllStr);
|
|
|
+ JSONObject queryYidaForm = YiDaFormDataV2.getQueryYidaForm("FORM-E489F03D805D4E169BA6A8755B4E72E6TN6N", mapAllStr, accessTokenService.getAccessToken());
|
|
|
+ JSONArray data = queryYidaForm.getJSONArray("data");
|
|
|
+ String emailUserId = null;
|
|
|
+ if (data.isEmpty()) {
|
|
|
+ System.out.println("根据邮箱地址查询【用户信息】返回数据为空!");
|
|
|
+ } else {
|
|
|
+ JSONObject formData = data.getJSONObject(0).getJSONObject("formData");
|
|
|
+ //成员userId
|
|
|
+ emailUserId = formData.getJSONArray("employeeField_mm1g5703_id").getString(0);
|
|
|
+ System.out.println("成员userId===>" + emailUserId);
|
|
|
+ }
|
|
|
+
|
|
|
+ /*根据邮件id获取邮件中的附件列表*/
|
|
|
+ log.info("开始获取邮件中的附件列表--->");
|
|
|
+ JSONArray fileArray = AlibabaMailBox.MailAttachments(emailAddress, emailId, accessTokenService.getAlibabaMailboxAccessToken());
|
|
|
+ if (fileArray.size() > 0){
|
|
|
+ for (int j = 0; j < fileArray.size(); j++) {
|
|
|
+ JSONObject attachment = fileArray.getJSONObject(j);
|
|
|
+ float fileSize = attachment.getFloat("size");
|
|
|
+ System.out.println("文件大小: " + fileSize);
|
|
|
+ String fileName = attachment.getString("name");
|
|
|
+ System.out.println("文件名称: " + fileName);
|
|
|
+ String fileId = attachment.getString("id");
|
|
|
+ System.out.println("文件id: " + fileId);
|
|
|
+
|
|
|
+ // 判断是否为 PDF(忽略大小写)
|
|
|
+ boolean isPdf = fileName != null && fileName.toLowerCase().endsWith(".pdf");
|
|
|
+ // 判断是否包含指定内容的文件
|
|
|
+ boolean hasInvoiceKeyword = fileName.contains("行程");
|
|
|
+ // 或者用 contains(但 endWith 更准确)
|
|
|
+ // boolean isPdf = fileName != null && fileName.toLowerCase().contains(".pdf");
|
|
|
+ System.out.println("文件名: " + fileName + " → 是PDF? " + isPdf + " → 文件名包含[行程]? " + hasInvoiceKeyword);
|
|
|
+ //NOTE 判断邮件中的附件是否为pdf
|
|
|
+ if (isPdf && !hasInvoiceKeyword){
|
|
|
+ log.info("该附件是pdf,开始处理文件,文件名:{},文件id:{}", fileName, fileId);
|
|
|
+ /*根据文件id获取文件流进行下载转存文件*/
|
|
|
+ Map mapEnd = AlibabaMailBox.downLoadMailFile("pdf", emailAddress, emailId, fileId, fileName, accessTokenService.getAlibabaMailboxAccessToken());
|
|
|
+ System.out.println("mapEnd===>" + mapEnd);
|
|
|
+ String fileNewUrl = (String) mapEnd.get("fileNewUrl");
|
|
|
+ System.out.println("服务器访问全地址:" + fileNewUrl);
|
|
|
+ if (fileNewUrl != null){
|
|
|
+ log.info("文件转存地址不为空,开始进行发票识别===");
|
|
|
+ /*调用发票识别*/
|
|
|
+ try {
|
|
|
+ JSONObject jsonObject = TencentInvoice.doRecognizeGeneralInvoice(fileNewUrl);
|
|
|
+ log.info("发票识别结果:" + jsonObject);
|
|
|
+ JSONArray jsonArray = jsonObject.getJSONArray("MixedInvoiceItems");
|
|
|
+ if (jsonArray != null && jsonArray.size() > 0) {
|
|
|
+ log.info("发票识别成功!");
|
|
|
+ JSONObject jsonObject11 = jsonArray.getJSONObject(0);
|
|
|
+ int Type = jsonObject11.getInteger("Type");
|
|
|
+ System.out.println("发票类型编号:" + Type);//示例:16
|
|
|
+ String SubType = jsonObject11.getString("SubType");
|
|
|
+ System.out.println("发票类型:" + SubType);//示例:VatElectronicInvoiceFull
|
|
|
+ String SubTypeDescription = jsonObject11.getString("SubTypeDescription");
|
|
|
+ System.out.println("-->发票类型描述:" + SubTypeDescription);//示例:电子发票(普通发票)
|
|
|
+ String TypeDescription = jsonObject11.getString("TypeDescription");
|
|
|
+ System.out.println("-->票种类型描述:" + TypeDescription);//示例:全电发票
|
|
|
+ //发票号码
|
|
|
+ String invoiceNumber = null;
|
|
|
+ String invoiceDateStr = null;
|
|
|
+ //开票日期
|
|
|
+ long invoiceDate = 0;
|
|
|
+ //发票代码
|
|
|
+ String invoiceCode = null;
|
|
|
+ //校验码
|
|
|
+ String checkCode = null;
|
|
|
+ //购买方名称
|
|
|
+ String buyerName = null;
|
|
|
+ //购买方纳税人识别号
|
|
|
+ String buyerTaxpayerId = null;
|
|
|
+ //销售方名称
|
|
|
+ String sellerName = null;
|
|
|
+ //销售方纳税人识别号
|
|
|
+ String sellerTaxpayerId = null;
|
|
|
+ //含税总额
|
|
|
+ float totalAmount = 0;
|
|
|
+ //税额
|
|
|
+ float taxAmount = 0;
|
|
|
+ //不含税总额
|
|
|
+ float amountWithoutTax = 0;
|
|
|
+ //发票备注
|
|
|
+ String invoiceRemark = null;
|
|
|
+ //税率
|
|
|
+ float taxRate = 0;
|
|
|
+ if (SubType.equals("VatElectronicInvoiceFull") || SubType.equals("VatElectronicSpecialInvoiceFull")){//NOTE 电子发票(普通发票)、电子发票(专用发票)
|
|
|
+ JSONObject singleInvoiceInfos = jsonObject11.getJSONObject("SingleInvoiceInfos");
|
|
|
+ JSONObject VatElectronicInvoiceFull = singleInvoiceInfos.getJSONObject(SubType);
|
|
|
+
|
|
|
+ if (SubType.equals("VatElectronicSpecialInvoiceFull")){
|
|
|
+ taxRate = 6;
|
|
|
+ }
|
|
|
+ System.out.println("发票税率:" + taxRate);
|
|
|
+ //发票号码
|
|
|
+ invoiceNumber = VatElectronicInvoiceFull.getString("Number");
|
|
|
+ System.out.println("发票号码:" + invoiceNumber);
|
|
|
+ //开票日期
|
|
|
+ invoiceDateStr = VatElectronicInvoiceFull.getString("Date");
|
|
|
+ System.out.println("开票日期:" + invoiceDateStr);
|
|
|
+ // 定义格式化器
|
|
|
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
|
|
|
+ // 解析字符串为 LocalDate
|
|
|
+ LocalDate localDate = LocalDate.parse(invoiceDateStr, formatter);
|
|
|
+ // 转换为毫秒级时间戳(基于系统默认时区)
|
|
|
+ invoiceDate = localDate.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
|
|
|
+ System.out.println("开票日期时间戳: " + invoiceDate);
|
|
|
+ //发票代码(该类型发票不存在该字段)
|
|
|
+ //校验码(该类型发票不存在该字段)
|
|
|
+ //购买方名称
|
|
|
+ buyerName = VatElectronicInvoiceFull.getString("Buyer");
|
|
|
+ System.out.println("购买方名称:" + buyerName);
|
|
|
+ //购买方纳税人识别号
|
|
|
+ buyerTaxpayerId = VatElectronicInvoiceFull.getString("BuyerTaxID");
|
|
|
+ System.out.println("购买方纳税人识别号:" + buyerTaxpayerId);
|
|
|
+ //销售方名称
|
|
|
+ sellerName = VatElectronicInvoiceFull.getString("Seller");
|
|
|
+ System.out.println("销售方名称:" + sellerName);
|
|
|
+ //销售方纳税人识别号
|
|
|
+ sellerTaxpayerId = VatElectronicInvoiceFull.getString("SellerTaxID");
|
|
|
+ System.out.println("销售方纳税人识别号:" + sellerTaxpayerId);
|
|
|
+ //含税总额(价税合计)
|
|
|
+ totalAmount = VatElectronicInvoiceFull.getFloat("Total");
|
|
|
+ System.out.println("含税总额(价税合计):" + totalAmount);
|
|
|
+ //税额
|
|
|
+ taxAmount = VatElectronicInvoiceFull.getFloat("Tax");
|
|
|
+ System.out.println("税额:" + taxAmount);
|
|
|
+ //不含税总额(税前金额)
|
|
|
+ amountWithoutTax = VatElectronicInvoiceFull.getFloat("PretaxAmount");
|
|
|
+ System.out.println("不含税总额(税前金额):" + amountWithoutTax);
|
|
|
+ //发票备注
|
|
|
+ invoiceRemark = VatElectronicInvoiceFull.getString("Remark");
|
|
|
+ System.out.println("发票备注:" + invoiceRemark);
|
|
|
+ }
|
|
|
+ if (SubType.equals("ElectronicTrainTicketFull")){//NOTE 电子发票(火车票)
|
|
|
+ JSONObject singleInvoiceInfos = jsonObject11.getJSONObject("SingleInvoiceInfos");
|
|
|
+ JSONObject VatElectronicInvoiceFull = singleInvoiceInfos.getJSONObject(SubType);
|
|
|
+
|
|
|
+ //税率
|
|
|
+ taxRate = 9;
|
|
|
+ System.out.println("发票税率:" + taxRate);
|
|
|
+ //发票号码
|
|
|
+ invoiceNumber = VatElectronicInvoiceFull.getString("Number");
|
|
|
+ System.out.println("发票号码:" + invoiceNumber);
|
|
|
+ //开票日期
|
|
|
+ invoiceDateStr = VatElectronicInvoiceFull.getString("Date");
|
|
|
+ System.out.println("开票日期:" + invoiceDateStr);
|
|
|
+ // 定义格式化器
|
|
|
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
|
|
|
+ // 解析字符串为 LocalDate
|
|
|
+ LocalDate localDate = LocalDate.parse(invoiceDateStr, formatter);
|
|
|
+ // 转换为毫秒级时间戳(基于系统默认时区)
|
|
|
+ invoiceDate = localDate.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
|
|
|
+ System.out.println("开票日期时间戳: " + invoiceDate);
|
|
|
+ //发票代码(该类型发票不存在该字段)
|
|
|
+ //校验码(该类型发票不存在该字段)
|
|
|
+ //购买方名称
|
|
|
+ buyerName = VatElectronicInvoiceFull.getString("Buyer");
|
|
|
+ System.out.println("购买方名称:" + buyerName);
|
|
|
+ //购买方纳税人识别号
|
|
|
+ buyerTaxpayerId = VatElectronicInvoiceFull.getString("BuyerTaxID");
|
|
|
+ System.out.println("购买方纳税人识别号:" + buyerTaxpayerId);
|
|
|
+ //销售方名称
|
|
|
+ sellerName = VatElectronicInvoiceFull.getString("Seller") != null ? VatElectronicInvoiceFull.getString("Seller") : null;
|
|
|
+ System.out.println("销售方名称:" + sellerName);
|
|
|
+ //销售方纳税人识别号
|
|
|
+ sellerTaxpayerId = VatElectronicInvoiceFull.getString("SellerTaxID") != null ? VatElectronicInvoiceFull.getString("SellerTaxID") : null;
|
|
|
+ System.out.println("销售方纳税人识别号:" + sellerTaxpayerId);
|
|
|
+ //含税总额(票价)
|
|
|
+ totalAmount = VatElectronicInvoiceFull.getFloat("Fare");
|
|
|
+ System.out.println("含税总额(票价):" + totalAmount);
|
|
|
+ //税额
|
|
|
+ taxAmount = VatElectronicInvoiceFull.getFloat("Tax");
|
|
|
+ System.out.println("税额:" + taxAmount);
|
|
|
+ //不含税总额(税前金额)
|
|
|
+ amountWithoutTax = VatElectronicInvoiceFull.getFloat("PretaxAmount") != null ? VatElectronicInvoiceFull.getFloat("PretaxAmount") : 0;
|
|
|
+ System.out.println("不含税总额(税前金额):" + amountWithoutTax);
|
|
|
+ //发票备注
|
|
|
+ invoiceRemark = VatElectronicInvoiceFull.getString("Remark");
|
|
|
+ System.out.println("发票备注:" + invoiceRemark);
|
|
|
+ }
|
|
|
+ if (SubType.equals("VatElectronicCommonInvoice") || SubType.equals("VatElectronicSpecialInvoice")){//NOTE 增值税电子普通发票、增值税电子专用发票
|
|
|
+ JSONObject singleInvoiceInfos = jsonObject11.getJSONObject("SingleInvoiceInfos");
|
|
|
+ JSONObject VatElectronicInvoiceFull = singleInvoiceInfos.getJSONObject(SubType);
|
|
|
+
|
|
|
+ if (SubType.equals("VatElectronicSpecialInvoice")){
|
|
|
+ taxRate = 6;
|
|
|
+ }
|
|
|
+ System.out.println("发票税率:" + taxRate);
|
|
|
+ //发票号码
|
|
|
+ invoiceNumber = VatElectronicInvoiceFull.getString("Number");
|
|
|
+ System.out.println("发票号码:" + invoiceNumber);
|
|
|
+ //开票日期
|
|
|
+ invoiceDateStr = VatElectronicInvoiceFull.getString("Date");
|
|
|
+ System.out.println("开票日期:" + invoiceDateStr);
|
|
|
+ // 定义格式化器
|
|
|
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
|
|
|
+ // 解析字符串为 LocalDate
|
|
|
+ LocalDate localDate = LocalDate.parse(invoiceDateStr, formatter);
|
|
|
+ // 转换为毫秒级时间戳(基于系统默认时区)
|
|
|
+ invoiceDate = localDate.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
|
|
|
+ System.out.println("开票日期时间戳: " + invoiceDate);
|
|
|
+ //发票代码
|
|
|
+ invoiceCode = VatElectronicInvoiceFull.getString("Code");
|
|
|
+ System.out.println("发票代码: " + invoiceCode);
|
|
|
+ //校验码
|
|
|
+ checkCode = VatElectronicInvoiceFull.getString("CheckCode");
|
|
|
+ System.out.println("校验码: " + checkCode);
|
|
|
+ //购买方名称
|
|
|
+ buyerName = VatElectronicInvoiceFull.getString("Buyer");
|
|
|
+ System.out.println("购买方名称:" + buyerName);
|
|
|
+ //购买方纳税人识别号
|
|
|
+ buyerTaxpayerId = VatElectronicInvoiceFull.getString("BuyerTaxID");
|
|
|
+ System.out.println("购买方纳税人识别号:" + buyerTaxpayerId);
|
|
|
+ //销售方名称
|
|
|
+ sellerName = VatElectronicInvoiceFull.getString("Seller");
|
|
|
+ System.out.println("销售方名称:" + sellerName);
|
|
|
+ //销售方纳税人识别号
|
|
|
+ sellerTaxpayerId = VatElectronicInvoiceFull.getString("SellerTaxID");
|
|
|
+ System.out.println("销售方纳税人识别号:" + sellerTaxpayerId);
|
|
|
+ //含税总额(价税合计)
|
|
|
+ totalAmount = VatElectronicInvoiceFull.getFloat("Total");
|
|
|
+ System.out.println("含税总额(价税合计):" + totalAmount);
|
|
|
+ //税额
|
|
|
+ taxAmount = VatElectronicInvoiceFull.getFloat("Tax");
|
|
|
+ System.out.println("税额:" + taxAmount);
|
|
|
+ //不含税总额(税前金额)
|
|
|
+ amountWithoutTax = VatElectronicInvoiceFull.getFloat("PretaxAmount");
|
|
|
+ System.out.println("不含税总额(税前金额):" + amountWithoutTax);
|
|
|
+ //发票备注
|
|
|
+ invoiceRemark = VatElectronicInvoiceFull.getString("Remark");
|
|
|
+ System.out.println("发票备注:" + invoiceRemark);
|
|
|
+ }
|
|
|
+ if (aliBtripInvoiceNumbers.contains(normalizeInvoiceNumber(invoiceNumber))) {
|
|
|
+ log.info("发票号码 {} 已存在于阿里商旅明细中,跳过当前附件", invoiceNumber);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ Map formMap = new HashMap<>();
|
|
|
+ //基础信息
|
|
|
+ if (emailUserId == null){
|
|
|
+ emailUserId = "326829332";
|
|
|
+ }
|
|
|
+ formMap.put("employeeField_mm1ntqs3", emailUserId);//NOTE 发票所属人userId,如果根据email地址没有匹配到宜搭用户信息表上的人员,默认使用默认的userId【外部系统开发对接userId:326829332】
|
|
|
+ formMap.put("selectField_mne83ozt", "发票识别完成");//发票识别状态:记录票夹完成、发票识别完成
|
|
|
+ formMap.put("selectField_mmya15ss", "等待验真");//验真结果:等待验真、验真成功、验真失败
|
|
|
+ formMap.put("textField_mne83p0o", email);//收件人邮箱地址
|
|
|
+ formMap.put("textField_mne83ozz", "发票识别完成,等待验真!");//发票识别验真返回信息
|
|
|
+ //发票信息
|
|
|
+ Map fileMap = new HashMap();
|
|
|
+ fileMap.put("previewUrl",fileNewUrl);
|
|
|
+ fileMap.put("size",fileSize);
|
|
|
+ fileMap.put("name",fileName);
|
|
|
+ fileMap.put("downloadUrl",fileNewUrl);
|
|
|
+ fileMap.put("url",fileNewUrl);
|
|
|
+ List fileList = new ArrayList();
|
|
|
+ fileList.add(fileMap);
|
|
|
+ formMap.put("attachmentField_mm1ntqs4",fileList);//发票文件
|
|
|
+ formMap.put("textField_mne83p0p", fileName);//发票文件名称
|
|
|
+ //发票信息-识别解析内容
|
|
|
+ formMap.put("textField_mne83p0w", SubTypeDescription);//发票类型
|
|
|
+ formMap.put("textField_mm1ntqtb", invoiceNumber);//发票号码
|
|
|
+ formMap.put("dateField_mm1ntqtc", invoiceDate);//开票日期
|
|
|
+ formMap.put("textField_mne83p0j", TypeDescription);//票种类型
|
|
|
+ formMap.put("textField_mne83p06", invoiceCode);//发票代码
|
|
|
+ formMap.put("textField_mne83p0a", checkCode);//校验码
|
|
|
+ formMap.put("textField_mm1ntqth", buyerName);//购买方名称
|
|
|
+ formMap.put("textField_mm1ntqti", buyerTaxpayerId);//购买方纳税人识别号
|
|
|
+ formMap.put("textField_mm1ntqtv", sellerName);//销售方名称
|
|
|
+ formMap.put("textField_mm1ntqtw", sellerTaxpayerId);//销售方纳税人识别号
|
|
|
+ formMap.put("numberField_mm1ntqua", totalAmount);//发票含税总额
|
|
|
+ formMap.put("numberField_mm1ntqub", taxAmount);//发票税额
|
|
|
+ formMap.put("numberField_mne83p09", amountWithoutTax);//发票不含税总额
|
|
|
+ formMap.put("textareaField_mm1ntquo", invoiceRemark);//发票备注
|
|
|
+ formMap.put("numberField_mqoo3y0c", taxRate);//发票税率
|
|
|
+ //系统信息
|
|
|
+ formMap.put("textField_mne83p05", fileNewUrl);//文件转存url地址
|
|
|
+ formMap.put("radioField_mm1ntqup", "未报销");//报销状态
|
|
|
+ formMap.put("radioField_mm1ntquc", "邮箱同步");//发票来源
|
|
|
+ formMap.put("textField_moibz1e1", invoiceDateStr);//开票日期
|
|
|
+
|
|
|
+ String formDataJson = JSON.toJSONString(formMap);
|
|
|
+ System.out.println("mapJson:" + formDataJson);
|
|
|
+
|
|
|
+ Map searchMap = new HashMap<>();
|
|
|
+ searchMap.put("key", "textField_mm1ntqtb");
|
|
|
+ searchMap.put("value", invoiceNumber);
|
|
|
+ searchMap.put("type", "TEXT");
|
|
|
+ searchMap.put("operator", "eq");
|
|
|
+ searchMap.put("componentName", "TextField");
|
|
|
+ List searchList = new ArrayList();
|
|
|
+ searchList.add(searchMap);
|
|
|
+ String searchCondition = JSON.toJSONString(searchList);
|
|
|
+ System.out.println("查询条件searchCondition:" + searchCondition);
|
|
|
+ /*新增或更新到宜搭*/
|
|
|
+ log.info("开始新增或更新到宜搭「票夹」表单--->");
|
|
|
+ YiDaFormData.CreateOrUpdateFormData(
|
|
|
+ "FORM-2D49DB5769F542B8B0D836B4BE5DB20BETRV",
|
|
|
+ searchCondition,
|
|
|
+ formDataJson,
|
|
|
+ accessTokenService.getAccessToken());
|
|
|
+ }else {
|
|
|
+ log.info("发票识别失败!");//STOP 识别失败的不记录票夹
|
|
|
+// Map formMap = new HashMap<>();
|
|
|
+// //基础信息
|
|
|
+// if (emailUserId == null){
|
|
|
+// emailUserId = "326829332";
|
|
|
+// }
|
|
|
+// formMap.put("employeeField_mm1ntqs3", emailUserId);//NOTE 发票所属人userId,如果根据email地址没有匹配到宜搭用户信息表上的人员,默认使用默认的userId【外部系统开发对接userId:326829332】
|
|
|
+// formMap.put("selectField_mne83ozt", "记录票夹完成");//发票识别状态:记录票夹完成、发票识别完成
|
|
|
+// formMap.put("selectField_mmya15ss", "等待验真");//验真结果:等待验真、验真成功、验真失败
|
|
|
+// formMap.put("textField_mne83p0o", email);//收件人邮箱地址
|
|
|
+// formMap.put("textField_mne83ozz", "发票识别失败!");//发票识别验真返回信息
|
|
|
+// //发票信息
|
|
|
+// Map fileMap = new HashMap();
|
|
|
+// fileMap.put("previewUrl",fileNewUrl);
|
|
|
+// fileMap.put("size",fileSize);
|
|
|
+// fileMap.put("name",fileName);
|
|
|
+// fileMap.put("downloadUrl",fileNewUrl);
|
|
|
+// fileMap.put("url",fileNewUrl);
|
|
|
+// List fileList = new ArrayList();
|
|
|
+// fileList.add(fileMap);
|
|
|
+// formMap.put("attachmentField_mm1ntqs4",fileList);//发票文件
|
|
|
+// formMap.put("textField_mne83p0p", fileName);//发票文件名称
|
|
|
+// //系统信息
|
|
|
+// formMap.put("textField_mne83p05", fileNewUrl);//文件转存url地址
|
|
|
+// formMap.put("radioField_mm1ntqup", "未报销");//报销状态
|
|
|
+// formMap.put("radioField_mm1ntquc", "邮箱同步");//发票来源
|
|
|
+//
|
|
|
+// String formDataJson = JSON.toJSONString(formMap);
|
|
|
+// System.out.println("mapJson:" + formDataJson);
|
|
|
+//
|
|
|
+// Map searchMap = new HashMap<>();
|
|
|
+// searchMap.put("key", "textField_mne83p0p");
|
|
|
+// searchMap.put("value", fileName);
|
|
|
+// searchMap.put("type", "TEXT");
|
|
|
+// searchMap.put("operator", "eq");
|
|
|
+// searchMap.put("componentName", "TextField");
|
|
|
+// List searchList = new ArrayList();
|
|
|
+// searchList.add(searchMap);
|
|
|
+// String searchCondition = JSON.toJSONString(searchList);
|
|
|
+// System.out.println("查询条件searchCondition:" + searchCondition);
|
|
|
+// /*新增或更新到宜搭*/
|
|
|
+// log.info("开始新增或更新到宜搭「票夹」表单--->");
|
|
|
+// YiDaFormData.CreateOrUpdateFormData(
|
|
|
+// "FORM-2D49DB5769F542B8B0D836B4BE5DB20BETRV",
|
|
|
+// searchCondition,
|
|
|
+// formDataJson,
|
|
|
+// accessTokenService.getAccessToken());
|
|
|
+ }
|
|
|
+ }catch (TencentCloudSDKException e){
|
|
|
+ log.error("发票识别异常:" + e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }else {
|
|
|
+ log.info("--->【3】该文件不是pdf文件,且该文件是行程单,不进行识别和写入宜搭!");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 判断是否为 压缩包(忽略大小写)
|
|
|
+ boolean isPackage = fileName != null && (fileName.toLowerCase().endsWith(".zip") || fileName.toLowerCase().endsWith(".rar"));
|
|
|
+ System.out.println("文件名: " + fileName + " → 是压缩包? " + isPackage);
|
|
|
+ //NOTE 判断邮件中的附件是否为压缩包
|
|
|
+// if (isPackage && !hasInvoiceKeyword) {
|
|
|
+// log.info("该附件是压缩包,开始解压处理文件===");
|
|
|
+// if (fileName != null && fileName.toLowerCase().endsWith(".zip")){
|
|
|
+// log.info("该附件是zip格式,开始处理文件,文件名:{},文件id:{}", fileName, fileId);
|
|
|
+// /*根据文件id获取文件流进行下载转存文件*/
|
|
|
+// Map mapEnd = AlibabaMailBox.downLoadMailFile("zip", emailAddress, emailId, fileId, fileName, accessTokenService.getAlibabaMailboxAccessToken());
|
|
|
+// String fileNewUrl = (String) mapEnd.get("fileNewUrl");
|
|
|
+// if (fileNewUrl != null){
|
|
|
+// /*调用发票识别*/
|
|
|
+// //TODO
|
|
|
+//
|
|
|
+// Map formMap = new HashMap<>();
|
|
|
+// //基础信息
|
|
|
+// if (emailUserId == null){
|
|
|
+// emailUserId = "326829332";
|
|
|
+// }
|
|
|
+// formMap.put("employeeField_mm1ntqs3", emailUserId);//NOTE 发票所属人userId,如果根据email地址没有匹配到宜搭用户信息表上的人员,默认使用默认的userId【外部系统开发对接userId:326829332】
|
|
|
+// formMap.put("selectField_mne83ozt", "记录票夹完成");//发票识别状态:记录票夹完成、发票识别完成
|
|
|
+// formMap.put("selectField_mmya15ss", "等待验真");//验真结果:等待验真、验真成功、验真失败
|
|
|
+// formMap.put("textField_mne83p0o", email);//收件人邮箱地址
|
|
|
+// formMap.put("textField_mne83ozz", null);//发票识别验真返回信息
|
|
|
+// //发票信息
|
|
|
+// Map fileMap = new HashMap();
|
|
|
+// fileMap.put("previewUrl",fileNewUrl);
|
|
|
+// fileMap.put("size",fileSize);
|
|
|
+// fileMap.put("name",fileName);
|
|
|
+// fileMap.put("downloadUrl",fileNewUrl);
|
|
|
+// fileMap.put("url",fileNewUrl);
|
|
|
+// List fileList = new ArrayList();
|
|
|
+// fileList.add(fileMap);
|
|
|
+// formMap.put("attachmentField_mm1ntqs4",fileList);//发票文件
|
|
|
+// formMap.put("textField_mne83p0p", fileName);//发票文件名称
|
|
|
+// formMap.put("textField_mne83p05", fileNewUrl);//文件转存url地址
|
|
|
+//
|
|
|
+// String formDataJson = JSON.toJSONString(formMap);
|
|
|
+// System.out.println("mapJson:" + formDataJson);
|
|
|
+//
|
|
|
+// Map searchMap = new HashMap<>();
|
|
|
+// searchMap.put("key", "textField_mne83p0p");
|
|
|
+// searchMap.put("value", fileName);
|
|
|
+// searchMap.put("type", "TEXT");
|
|
|
+// searchMap.put("operator", "eq");
|
|
|
+// searchMap.put("componentName", "TextField");
|
|
|
+// List searchList = new ArrayList();
|
|
|
+// searchList.add(searchMap);
|
|
|
+// String searchCondition = JSON.toJSONString(searchList);
|
|
|
+// System.out.println("查询条件searchCondition:" + searchCondition);
|
|
|
+// /*新增或更新到宜搭*/
|
|
|
+// log.info("开始新增或更新到宜搭「票夹」表单--->");
|
|
|
+// YiDaFormData.CreateOrUpdateFormData(
|
|
|
+// "FORM-2D49DB5769F542B8B0D836B4BE5DB20BETRV",
|
|
|
+// searchCondition,
|
|
|
+// formDataJson,
|
|
|
+// accessTokenService.getAccessToken());
|
|
|
+// }
|
|
|
+// }
|
|
|
+// if (fileName != null && fileName.toLowerCase().endsWith(".rar")){
|
|
|
+// log.info("该附件是rar格式,开始处理文件,文件名:{},文件id:{}", fileName, fileId);
|
|
|
+//
|
|
|
+// /*根据文件id获取文件流进行下载转存文件*/
|
|
|
+// Map mapEnd = AlibabaMailBox.downLoadMailFile("rar", emailAddress, emailId, fileId, fileName, accessTokenService.getAlibabaMailboxAccessToken());
|
|
|
+// String fileNewUrl = (String) mapEnd.get("fileNewUrl");
|
|
|
+// if (fileNewUrl != null){
|
|
|
+// /*调用发票识别*/
|
|
|
+// //TODO
|
|
|
+//
|
|
|
+// Map formMap = new HashMap<>();
|
|
|
+// //基础信息
|
|
|
+// if (emailUserId == null){
|
|
|
+// emailUserId = "326829332";
|
|
|
+// }
|
|
|
+// formMap.put("employeeField_mm1ntqs3", emailUserId);//NOTE 发票所属人userId,如果根据email地址没有匹配到宜搭用户信息表上的人员,默认使用默认的userId【外部系统开发对接userId:326829332】
|
|
|
+// formMap.put("selectField_mne83ozt", "记录票夹完成");//发票识别状态:记录票夹完成、发票识别完成
|
|
|
+// formMap.put("selectField_mmya15ss", "等待验真");//验真结果:等待验真、验真成功、验真失败
|
|
|
+// formMap.put("textField_mne83p0o", email);//收件人邮箱地址
|
|
|
+// formMap.put("textField_mne83ozz", null);//发票识别验真返回信息
|
|
|
+// //发票信息
|
|
|
+// Map fileMap = new HashMap();
|
|
|
+// fileMap.put("previewUrl",fileNewUrl);
|
|
|
+// fileMap.put("size",fileSize);
|
|
|
+// fileMap.put("name",fileName);
|
|
|
+// fileMap.put("downloadUrl",fileNewUrl);
|
|
|
+// fileMap.put("url",fileNewUrl);
|
|
|
+// List fileList = new ArrayList();
|
|
|
+// fileList.add(fileMap);
|
|
|
+// formMap.put("attachmentField_mm1ntqs4",fileList);//发票文件
|
|
|
+// formMap.put("textField_mne83p0p", fileName);//发票文件名称
|
|
|
+// formMap.put("textField_mne83p05", fileNewUrl);//文件转存url地址
|
|
|
+//
|
|
|
+// String formDataJson = JSON.toJSONString(formMap);
|
|
|
+// System.out.println("mapJson:" + formDataJson);
|
|
|
+//
|
|
|
+// Map searchMap = new HashMap<>();
|
|
|
+// searchMap.put("key", "textField_mne83p0p");
|
|
|
+// searchMap.put("value", fileName);
|
|
|
+// searchMap.put("type", "TEXT");
|
|
|
+// searchMap.put("operator", "eq");
|
|
|
+// searchMap.put("componentName", "TextField");
|
|
|
+// List searchList = new ArrayList();
|
|
|
+// searchList.add(searchMap);
|
|
|
+// String searchCondition = JSON.toJSONString(searchList);
|
|
|
+// System.out.println("查询条件searchCondition:" + searchCondition);
|
|
|
+// /*新增或更新到宜搭*/
|
|
|
+// log.info("开始新增或更新到宜搭「票夹」表单--->");
|
|
|
+// YiDaFormData.CreateOrUpdateFormData(
|
|
|
+// "FORM-2D49DB5769F542B8B0D836B4BE5DB20BETRV",
|
|
|
+// searchCondition,
|
|
|
+// formDataJson,
|
|
|
+// accessTokenService.getAccessToken());
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }else {
|
|
|
+// log.info("--->【3】该文件不是压缩包文件!");
|
|
|
+// }
|
|
|
+
|
|
|
+ }
|
|
|
+ }else {
|
|
|
+ log.info("--->【2】当前邮件未获取到发票附件列表!");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }else {
|
|
|
+ log.info("--->【1】当前邮箱未获取到邮件列表!");
|
|
|
+ }
|
|
|
+
|
|
|
+ cursor = nextCursor;
|
|
|
+ log.info("下一页光标nextCursor:" + cursor);
|
|
|
+ }while (!cursor.equals("$"));
|
|
|
+
|
|
|
+ log.info("--->结束循环搜索邮件<---");
|
|
|
+ return R.ok().message("解析邮箱发票结束");
|
|
|
+ }
|
|
|
+
|
|
|
+ private Set<String> queryAliBtripInvoiceNumbers(String inputDate) {
|
|
|
+ Set<String> invoiceNumbers = new HashSet<>();
|
|
|
+ try {
|
|
|
+ JSONArray trainTickets = aliBtripTrainTicketService.queryByInvoiceDate(
|
|
|
+ inputDate + " 00:00:00", inputDate + " 23:59:59");
|
|
|
+ for (int i = 0; i < trainTickets.size(); i++) {
|
|
|
+ JSONObject trainTicket = trainTickets.getJSONObject(i);
|
|
|
+ addInvoiceNumber(invoiceNumbers, trainTicket.getString("electronic_ticket_no"));
|
|
|
+ }
|
|
|
+ log.info("阿里商旅火车票凭证明细查询完成,共 {} 条", trainTickets.size());
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("查询阿里商旅火车票凭证明细失败,不执行该来源的发票去重", e);
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ JSONArray vatInvoices = aliBtripVatInvoiceService.queryByBillDate(inputDate);
|
|
|
+ for (int i = 0; i < vatInvoices.size(); i++) {
|
|
|
+ addInvoiceNumber(invoiceNumbers, vatInvoices.getJSONObject(i).getString("invoice_no"));
|
|
|
+ }
|
|
|
+ log.info("阿里商旅增值税发票明细查询完成,共 {} 条", vatInvoices.size());
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("查询阿里商旅增值税发票明细失败,不执行该来源的发票去重", e);
|
|
|
+ }
|
|
|
+ return invoiceNumbers;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void addInvoiceNumber(Set<String> invoiceNumbers, String invoiceNumber) {
|
|
|
+ String normalized = normalizeInvoiceNumber(invoiceNumber);
|
|
|
+ if (normalized != null) {
|
|
|
+ invoiceNumbers.add(normalized);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String normalizeInvoiceNumber(String invoiceNumber) {
|
|
|
+ if (invoiceNumber == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ String normalized = invoiceNumber.replaceAll("\\s+", "").toUpperCase(Locale.ROOT);
|
|
|
+ return normalized.isEmpty() ? null : normalized;
|
|
|
+ }
|
|
|
+}
|