Procházet zdrojové kódy

feat: 开票成功后自动插入微信卡包

- 新增 WxPbUtil.insertInvoiceToCard 调用微信卡券 API 将电子发票推入用户卡包
- InvoiceStatusJob 轮询开票成功后自动插卡,失败不影响主流程
- 插卡成功后记录到 InvoiceDetail.cardInformation

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
skyline před 2 dny
rodič
revize
5ddfef7a8e

+ 3 - 0
admin/src/main/java/com/kym/admin/jobs/InvoiceStatusJob.java

@@ -67,6 +67,9 @@ public class InvoiceStatusJob {
                     huapiaoerInvoiceService.sendInvoiceEmail(invoice.getApplyId(), InvoiceQueryType.BLUE.getCode(), invoice.getEmail());
                 }
 
+                // 插入微信卡包
+                huapiaoerInvoiceService.insertToCardPackage(invoice, result);
+
                 log.info("发票轮询处理成功, applyId:{}, invoiceCode:{}, invoiceNumber:{}",
                         invoice.getApplyId(), result.getInvoicecode(), result.getInvoicenumber());
             } catch (Exception e) {

+ 5 - 0
common/src/main/java/com/kym/common/utils/wx/WxPbConfig.java

@@ -67,4 +67,9 @@ public class WxPbConfig {
      */
     static String API_SEND_KF_MSG = "https://https://api.weixin.qq.com/cgi-bin/message/mass/send?access_token=%s";
 
+    /**
+     * 电子发票插入微信卡包
+     */
+    static String API_INVOICE_INSERT_CARD = "https://api.weixin.qq.com/card/invoice/insert?access_token=%s";
+
 }

+ 52 - 0
common/src/main/java/com/kym/common/utils/wx/WxPbUtil.java

@@ -34,6 +34,10 @@ public class WxPbUtil {
 
     private static WxAccess config = null;
 
+    public static String getAppId() {
+        return WxPbConfig.PUBLIC_APP_ID;
+    }
+
     /**
      * 发送客服消息
      *
@@ -355,6 +359,54 @@ public class WxPbUtil {
     }
 
 
+    /**
+     * 将电子发票插入用户微信卡包
+     *
+     * @return 卡券 code,失败返回 null
+     */
+    public static String insertInvoiceToCard(String openid, String title, String invoiceNumber,
+                                              String invoiceCode, int totalAmount, String buyerTaxId,
+                                              String sellerTaxId, String pdfUrl, List<Map<String, Object>> items) {
+        String accessToken = getAccessToken();
+        String url = String.format(WxPbConfig.API_INVOICE_INSERT_CARD, accessToken);
+
+        Map<String, Object> invoiceInfo = new HashMap<>();
+        invoiceInfo.put("title", title);
+        invoiceInfo.put("number", invoiceNumber);
+        invoiceInfo.put("code", invoiceCode);
+        invoiceInfo.put("type", "增值税电子普通发票");
+        invoiceInfo.put("fee", totalAmount);
+        invoiceInfo.put("tax", (int)(totalAmount - totalAmount / 1.13));
+        invoiceInfo.put("saler_number", sellerTaxId);
+        invoiceInfo.put("buyer_number", buyerTaxId);
+        invoiceInfo.put("url", pdfUrl);
+        invoiceInfo.put("items", items);
+
+        Map<String, Object> card = new HashMap<>();
+        card.put("card_type", "INVOICE");
+        card.put("invoice_info", invoiceInfo);
+
+        Map<String, Object> body = new HashMap<>();
+        body.put("code", "");
+        body.put("openid", openid);
+        body.put("card", card);
+
+        try {
+            String data = JSON.toJSONString(body);
+            Map<String, Object> result = sslRequest(url, "post", data);
+            if (!checkResp(result)) {
+                logger.error("WxPbUtil.insertInvoiceToCard error, resp:{}", JSON.toJSONString(result));
+                return null;
+            }
+            String cardCode = (String) result.get("code");
+            logger.info("WxPbUtil.insertInvoiceToCard success, openid:{}, cardCode:{}", openid, cardCode);
+            return cardCode;
+        } catch (Exception e) {
+            logger.error("WxPbUtil.insertInvoiceToCard error, openid:{}", openid, e);
+            return null;
+        }
+    }
+
     private static Map<String, Object> sslRequest(String reqUrl, String method, String body) {
         logger.debug("WxPbUtil.sslRequest req url:{},method:{},body:{}", reqUrl, method, body);
         long startTime = System.currentTimeMillis();

+ 69 - 0
service/src/main/java/com/kym/service/miniapp/HuapiaoerInvoiceService.java

@@ -5,6 +5,7 @@ import com.alibaba.fastjson2.JSONObject;
 import com.baomidou.dynamic.datasource.annotation.DS;
 import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
 import com.kym.common.config.HuapiaoerProperties;
+import com.kym.common.utils.wx.WxPbUtil;
 import com.kym.entity.admin.InvoiceDetail;
 import com.kym.entity.miniapp.Invoice;
 import com.kym.entity.wechat.FaPiao;
@@ -27,6 +28,7 @@ import org.springframework.stereotype.Service;
 
 import java.time.LocalDateTime;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -95,6 +97,73 @@ public class HuapiaoerInvoiceService {
         }
     }
 
+    /**
+     * 将已开具的电子发票插入用户微信卡包
+     */
+    public void insertToCardPackage(Invoice invoice, InvoiceInfo result) {
+        if (invoice.getOpenid() == null || invoice.getOpenid().isBlank()) {
+            log.info("用户无 openid,跳过卡包插入, applyId:{}", invoice.getApplyId());
+            return;
+        }
+
+        var orderDetails = invoice.getOrderDetails();
+        int elecMoney = orderDetails.stream().mapToInt(InvoiceOrderDetail::getElecMoney).sum();
+        int serviceMoney = orderDetails.stream().mapToInt(InvoiceOrderDetail::getServiceMoney).sum();
+        int discount = orderDetails.stream().mapToInt(InvoiceOrderDetail::getServiceMoneyDiscount).sum();
+        int netServiceMoney = serviceMoney - discount;
+
+        var items = new ArrayList<Map<String, Object>>();
+        if (elecMoney > 0) {
+            var item = new HashMap<String, Object>();
+            item.put("name", "充电电费");
+            item.put("price", elecMoney);
+            item.put("unit", "");
+            item.put("num", 1);
+            items.add(item);
+        }
+        if (netServiceMoney > 0) {
+            var item = new HashMap<String, Object>();
+            item.put("name", "充电服务费");
+            item.put("price", netServiceMoney);
+            item.put("unit", "");
+            item.put("num", 1);
+            items.add(item);
+        }
+
+        String cardCode = WxPbUtil.insertInvoiceToCard(
+                invoice.getOpenid(),
+                invoice.getInvoiceTitle(),
+                result.getInvoicenumber(),
+                result.getInvoicecode(),
+                invoice.getInvoiceAmount(),
+                invoice.getTaxId(),
+                properties.getNsrsbh(),
+                result.getInvoiceurl(),
+                items
+        );
+
+        if (cardCode != null) {
+            try {
+                DynamicDataSourceContextHolder.push("db-admin");
+                var cardInfo = new FapiaoApplications.CardInfo();
+                cardInfo.setCard_appid(WxPbUtil.getAppId());
+                cardInfo.setCard_openid(invoice.getOpenid());
+                cardInfo.setCard_code(cardCode);
+                cardInfo.setCard_status("INSERTED");
+
+                invoiceDetailService.lambdaUpdate()
+                        .set(InvoiceDetail::getCardInformation, cardInfo)
+                        .eq(InvoiceDetail::getApplyId, invoice.getApplyId())
+                        .update();
+                log.info("卡包插入成功, applyId:{}, cardCode:{}", invoice.getApplyId(), cardCode);
+            } finally {
+                DynamicDataSourceContextHolder.poll();
+            }
+        } else {
+            log.warn("卡包插入失败, applyId:{}", invoice.getApplyId());
+        }
+    }
+
     /**
      * 根据航信查询结果更新开票详情
      */