From 3c8ed575db70e0b995c7ee541ff3753ee8b57d87 Mon Sep 17 00:00:00 2001
From: wujianwei <wjw@11.com>
Date: 星期二, 31 三月 2026 11:11:09 +0800
Subject: [PATCH] 导出应收费用对账单

---
 tms/src/main/java/com/ruoyi/tms/service/impl/TmsReceivableFeeServiceImpl.java |  630 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 628 insertions(+), 2 deletions(-)

diff --git a/tms/src/main/java/com/ruoyi/tms/service/impl/TmsReceivableFeeServiceImpl.java b/tms/src/main/java/com/ruoyi/tms/service/impl/TmsReceivableFeeServiceImpl.java
index cd87ec8..4bf074c 100644
--- a/tms/src/main/java/com/ruoyi/tms/service/impl/TmsReceivableFeeServiceImpl.java
+++ b/tms/src/main/java/com/ruoyi/tms/service/impl/TmsReceivableFeeServiceImpl.java
@@ -1,15 +1,29 @@
 package com.ruoyi.tms.service.impl;
 
+import java.io.File;
+import java.io.FileOutputStream;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.redis.RedisCache;
 import com.ruoyi.common.enums.SystemDataNoEnum;
 import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.common.utils.UtilException;
+import com.ruoyi.common.utils.file.DownloadExportUtil;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.common.config.RuoYiConfig;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xssf.streaming.SXSSFSheet;
+import org.apache.poi.xssf.streaming.SXSSFWorkbook;
+import org.apache.poi.xssf.usermodel.XSSFColor;
 import javax.annotation.Resource;
 
 import com.ruoyi.common.utils.SecurityUtils;
@@ -18,11 +32,21 @@
 import com.ruoyi.tms.domain.*;
 import com.ruoyi.tms.mapper.*;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.transaction.annotation.Transactional;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 import org.springframework.scheduling.annotation.Async;
+import org.springframework.core.task.AsyncTaskExecutor;
+import org.springframework.core.task.SimpleAsyncTaskExecutor;
+import org.springframework.web.client.RestTemplate;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import com.alibaba.fastjson2.JSON;
 import com.ruoyi.common.utils.PageUtils;
 import com.ruoyi.common.constant.Constants;
 import com.ruoyi.common.annotation.DataSource;
@@ -57,7 +81,14 @@
     private TmsArBillMapper tmsArBillMapper;
     @Resource
     private TmsArBillItemMapper tmsArBillItemMapper;
+    
+    @Autowired
+    private RestTemplate restTemplate;
+    @Value("${custom.cwxtApi.url}")
+    private String url;
 
+    @Autowired
+    private RedisCache redisCache;
     /**
      * 鏌ヨ搴旀敹璐圭敤
      *
@@ -136,6 +167,468 @@
             PageUtils.startPage(pageNum, Constants.EXPORT_PATE_SIZE);
             return selectTmsReceivableFeeList(tmsReceivableFee);
         });
+    }
+
+    /**
+     * 瀵煎嚭瀵硅处鍗曚竴寮忓鑱旀牸寮�
+     *
+     * @param tmsReceivableFee 搴旀敹璐圭敤鏌ヨ鏉′欢
+     * @param exportKey 瀵煎嚭鍔熻兘鐨勫敮涓�鏍囪瘑
+     */
+    @DataSource(DataSourceType.SLAVE)
+    @Async
+    @Override
+    public void exportArBillFormat(TmsReceivableFee tmsReceivableFee, String exportKey) {
+        String fileName = ExcelUtil.encodeFileName("瀵硅处鍗�");
+
+        // 璁剧疆褰撳墠浠诲姟涓�"涓嬭浇涓�"鐘舵��
+        DownloadExportUtil.deleteDownloadFile(redisCache, exportKey, "0");
+
+        try {
+            // 鎵ц瀵煎嚭骞惰幏鍙栨枃浠跺悕
+            fileName = exportArBillData(fileName, tmsReceivableFee);
+            // 璁剧疆涓嬭浇瀹屾垚鐘舵��
+            DownloadExportUtil.setDownloadFile(redisCache, exportKey, fileName);
+            logger.info("Export completed for key: {}, file: {}", exportKey, fileName);
+        } catch (Exception e) {
+            logger.error("Export failed for key: {}, error: {}", exportKey, e.getMessage(), e);
+            DownloadExportUtil.deleteDownloadFile(redisCache, exportKey, "1"); // 璁剧疆澶辫触鐘舵��
+            throw e;
+        }
+    }
+
+    /**
+     * 瀵煎嚭瀵硅处鍗曚竴寮忓鑱旀暟鎹�
+     *
+     * @param fileName 鏂囦欢鍚�
+     * @param tmsReceivableFee 搴旀敹璐圭敤鏌ヨ鏉′欢
+     * @return 瀵煎嚭鍚庣殑鏂囦欢鍚�
+     */
+    protected String exportArBillData(String fileName, TmsReceivableFee tmsReceivableFee) {
+        try (SXSSFWorkbook workbook = new SXSSFWorkbook(1000)) {
+            // 鍒涘缓宸ヤ綔琛�
+            SXSSFSheet sheet = workbook.createSheet("瀵硅处鍗�");
+            
+            // 璁剧疆鍒楀
+            sheet.setColumnWidth(0, 5000);  // 搴忓彿
+            sheet.setColumnWidth(1, 5000);  // 瑁呰揣鏃ユ湡
+            sheet.setColumnWidth(2, 8000);  // 瑁呰揣鐐�
+            sheet.setColumnWidth(3, 8000);  // 鍗歌揣鐐�
+            sheet.setColumnWidth(4, 5000);  // 杞︾墝
+            sheet.setColumnWidth(5, 5000);  // 鍨嬪彿
+            sheet.setColumnWidth(6, 5000);  // 杩愯垂
+            sheet.setColumnWidth(7, 5000);  // 鏃犵紳璐�
+            sheet.setColumnWidth(8, 5000);  // 棣欐腐娓呭叧璐�
+            sheet.setColumnWidth(9, 10000); // 澶囨敞
+            
+            // 鍒涘缓鏍峰紡
+            Map<String, CellStyle> styles = createArBillStyles(workbook);
+            
+            // 鏍囬鍖哄煙
+            createTitleArea(sheet, styles, tmsReceivableFee);
+            
+            // 琛ㄥご
+            createTableHeader(sheet, styles);
+            
+            // 鏁版嵁鍖哄煙
+            int startRow = 4;
+            List<TmsReceivableFee> feeList = selectTmsReceivableFeeList(tmsReceivableFee);
+            BigDecimal totalFreight = BigDecimal.ZERO;
+            BigDecimal totalSeamless = BigDecimal.ZERO;
+            BigDecimal totalCustoms = BigDecimal.ZERO;
+            
+            for (int i = 0; i < feeList.size(); i++) {
+                TmsReceivableFee fee = feeList.get(i);
+                Row row = sheet.createRow(startRow + i);
+                
+                // 搴忓彿
+                Cell cell0 = row.createCell(0);
+                cell0.setCellValue(i + 1);
+                cell0.setCellStyle(styles.get("data"));
+                
+                // 瑁呰揣鏃ユ湡
+                Cell cell1 = row.createCell(1);
+                if (fee.getDispatchConfirmTime() != null) {
+                    cell1.setCellValue(DateUtils.parseDateToStr("yyyy-MM-dd", fee.getDispatchConfirmTime()));
+                }
+                cell1.setCellStyle(styles.get("data"));
+                
+                // 瑁呰揣鐐�
+                Cell cell2 = row.createCell(2);
+                // 杩欓噷闇�瑕佹牴鎹疄闄呮暟鎹粨鏋勮幏鍙栬璐х偣淇℃伅
+                cell2.setCellValue("" + (fee.getProjectName() != null ? fee.getProjectName() : ""));
+                cell2.setCellStyle(styles.get("data"));
+                
+                // 鍗歌揣鐐�
+                Cell cell3 = row.createCell(3);
+                // 杩欓噷闇�瑕佹牴鎹疄闄呮暟鎹粨鏋勮幏鍙栧嵏璐х偣淇℃伅
+                cell3.setCellValue("" + (fee.getCustomerName() != null ? fee.getCustomerName() : ""));
+                cell3.setCellStyle(styles.get("data"));
+                
+                // 杞︾墝
+                Cell cell4 = row.createCell(4);
+                // 杩欓噷闇�瑕佹牴鎹疄闄呮暟鎹粨鏋勮幏鍙栬溅鐗屽彿
+                cell4.setCellValue("");
+                cell4.setCellStyle(styles.get("data"));
+                
+                // 鍨嬪彿
+                Cell cell5 = row.createCell(5);
+                // 杩欓噷闇�瑕佹牴鎹疄闄呮暟鎹粨鏋勮幏鍙栧瀷鍙�
+                cell5.setCellValue("");
+                cell5.setCellStyle(styles.get("data"));
+                
+                // 杩愯垂
+                Cell cell6 = row.createCell(6);
+                BigDecimal freight = fee.getReceivableRMBAmount();
+                cell6.setCellValue(freight != null ? freight.doubleValue() : 0);
+                cell6.setCellStyle(styles.get("data"));
+                totalFreight = totalFreight.add(freight != null ? freight : BigDecimal.ZERO);
+                
+                // 鏃犵紳璐�
+                Cell cell7 = row.createCell(7);
+                BigDecimal seamless = BigDecimal.ZERO; // 杩欓噷闇�瑕佹牴鎹疄闄呮暟鎹粨鏋勮幏鍙栨棤缂濊垂
+                cell7.setCellValue(seamless.doubleValue());
+                cell7.setCellStyle(styles.get("data"));
+                totalSeamless = totalSeamless.add(seamless);
+                
+                // 棣欐腐娓呭叧璐�
+                Cell cell8 = row.createCell(8);
+                BigDecimal customs = fee.getReceivableHKBAmount();
+                cell8.setCellValue(customs != null ? customs.doubleValue() : 0);
+                cell8.setCellStyle(styles.get("data"));
+                totalCustoms = totalCustoms.add(customs != null ? customs : BigDecimal.ZERO);
+                
+                // 澶囨敞
+                Cell cell9 = row.createCell(9);
+                cell9.setCellValue("" + (fee.getDispatchNo() != null ? fee.getDispatchNo() : ""));
+                cell9.setCellStyle(styles.get("data"));
+            }
+            
+            // 灏忚琛�
+            int subTotalRow = startRow + feeList.size();
+            Row subTotal = sheet.createRow(subTotalRow);
+            Cell subTotalCell = subTotal.createCell(2);
+            subTotalCell.setCellValue("灏忚");
+            subTotalCell.setCellStyle(styles.get("total"));
+            sheet.addMergedRegion(new CellRangeAddress(subTotalRow, subTotalRow, 2, 5));
+            
+            Cell subTotalFreight = subTotal.createCell(6);
+            subTotalFreight.setCellValue(totalFreight.doubleValue());
+            subTotalFreight.setCellStyle(styles.get("total"));
+            
+            Cell subTotalSeamless = subTotal.createCell(7);
+            subTotalSeamless.setCellValue(totalSeamless.doubleValue());
+            subTotalSeamless.setCellStyle(styles.get("total"));
+            
+            Cell subTotalCustoms = subTotal.createCell(8);
+            subTotalCustoms.setCellValue(totalCustoms.doubleValue());
+            subTotalCustoms.setCellStyle(styles.get("total"));
+            
+            // 鍚堣琛�
+            int totalRow = subTotalRow + 1;
+            Row total = sheet.createRow(totalRow);
+            Cell totalCell = total.createCell(2);
+            totalCell.setCellValue("鍚堣(CNB)");
+            totalCell.setCellStyle(styles.get("total"));
+            sheet.addMergedRegion(new CellRangeAddress(totalRow, totalRow, 2, 5));
+            
+            Cell totalFreightCell = total.createCell(6);
+            totalFreightCell.setCellValue(totalFreight.doubleValue());
+            totalFreightCell.setCellStyle(styles.get("total"));
+            
+            Cell totalSeamlessCell = total.createCell(7);
+            totalSeamlessCell.setCellValue(totalSeamless.doubleValue());
+            totalSeamlessCell.setCellStyle(styles.get("total"));
+            
+            Cell totalCustomsCell = total.createCell(8);
+            totalCustomsCell.setCellValue(totalCustoms.doubleValue());
+            totalCustomsCell.setCellStyle(styles.get("total"));
+            
+            // 澶囨敞璇存槑
+            createNotesArea(sheet, styles, totalRow + 1);
+            
+            // 璐︽埛淇℃伅
+            createAccountInfo(sheet, styles, totalRow + 4);
+            
+            // 绛惧瓧鐩栫珷鍖哄煙
+            createSignatureArea(sheet, styles, totalRow + 8);
+            
+            // 淇濆瓨鏂囦欢
+            String path = RuoYiConfig.getDownloadPath() + fileName;
+            File file = new File(path);
+            File parentFile = file.getParentFile();
+            if (!parentFile.exists()) {
+                parentFile.mkdirs();
+            }
+            
+            try (FileOutputStream fos = new FileOutputStream(file)) {
+                workbook.write(fos);
+            }
+            
+            return fileName;
+        } catch (Exception e) {
+            logger.error("Export AR bill format failed: {}", e.getMessage(), e);
+            throw new UtilException("Export failed!");
+        }
+    }
+
+    /**
+     * 鍒涘缓瀵硅处鍗曚竴寮忓鑱旀牱寮�
+     *
+     * @param workbook 宸ヤ綔绨�
+     * @return 鏍峰紡鏄犲皠
+     */
+    private Map<String, CellStyle> createArBillStyles(SXSSFWorkbook workbook) {
+        Map<String, CellStyle> styles = new HashMap<>();
+        
+        // 鏍囬鏍峰紡
+        CellStyle titleStyle = workbook.createCellStyle();
+        titleStyle.setAlignment(HorizontalAlignment.CENTER);
+        titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+        Font titleFont = workbook.createFont();
+        titleFont.setFontName("Arial");
+        titleFont.setFontHeightInPoints((short) 16);
+        titleFont.setBold(true);
+        titleStyle.setFont(titleFont);
+        styles.put("title", titleStyle);
+        
+        // 琛ㄥご鏍峰紡
+        CellStyle headerStyle = workbook.createCellStyle();
+        headerStyle.setAlignment(HorizontalAlignment.CENTER);
+        headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+        headerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
+        headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+        Font headerFont = workbook.createFont();
+        headerFont.setFontName("Arial");
+        headerFont.setFontHeightInPoints((short) 10);
+        headerFont.setBold(true);
+        headerStyle.setFont(headerFont);
+        setDefaultBorders(headerStyle);
+        styles.put("header", headerStyle);
+        
+        // 鏁版嵁鏍峰紡
+        CellStyle dataStyle = workbook.createCellStyle();
+        dataStyle.setAlignment(HorizontalAlignment.CENTER);
+        dataStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+        Font dataFont = workbook.createFont();
+        dataFont.setFontName("Arial");
+        dataFont.setFontHeightInPoints((short) 10);
+        dataStyle.setFont(dataFont);
+        setDefaultBorders(dataStyle);
+        styles.put("data", dataStyle);
+        
+        // 鍚堣鏍峰紡
+        CellStyle totalStyle = workbook.createCellStyle();
+        totalStyle.setAlignment(HorizontalAlignment.CENTER);
+        totalStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+        totalStyle.setFillForegroundColor(IndexedColors.GREY_125_PERCENT.getIndex());
+        totalStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+        Font totalFont = workbook.createFont();
+        totalFont.setFontName("Arial");
+        totalFont.setFontHeightInPoints((short) 10);
+        totalFont.setBold(true);
+        totalStyle.setFont(totalFont);
+        setDefaultBorders(totalStyle);
+        styles.put("total", totalStyle);
+        
+        // 鏅�氭枃鏈牱寮�
+        CellStyle textStyle = workbook.createCellStyle();
+        textStyle.setAlignment(HorizontalAlignment.LEFT);
+        textStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+        Font textFont = workbook.createFont();
+        textFont.setFontName("Arial");
+        textFont.setFontHeightInPoints((short) 10);
+        textStyle.setFont(textFont);
+        styles.put("text", textStyle);
+        
+        return styles;
+    }
+
+    /**
+     * 璁剧疆榛樿杈规
+     *
+     * @param style 鍗曞厓鏍兼牱寮�
+     */
+    private void setDefaultBorders(CellStyle style) {
+        style.setBorderRight(BorderStyle.THIN);
+        style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+        style.setBorderLeft(BorderStyle.THIN);
+        style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+        style.setBorderTop(BorderStyle.THIN);
+        style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+        style.setBorderBottom(BorderStyle.THIN);
+        style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
+    }
+
+    /**
+     * 鍒涘缓鏍囬鍖哄煙
+     *
+     * @param sheet 宸ヤ綔琛�
+     * @param styles 鏍峰紡鏄犲皠
+     * @param tmsReceivableFee 搴旀敹璐圭敤鏌ヨ鏉′欢
+     */
+    private void createTitleArea(SXSSFSheet sheet, Map<String, CellStyle> styles, TmsReceivableFee tmsReceivableFee) {
+        // 鏍囬琛�
+        Row titleRow = sheet.createRow(0);
+        titleRow.setHeightInPoints(30);
+        Cell titleCell = titleRow.createCell(0);
+        titleCell.setCellValue("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
+        titleCell.setCellStyle(styles.get("title"));
+        sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 9));
+        
+        // 瀵硅处鍗曡
+        Row billRow = sheet.createRow(1);
+        billRow.setHeightInPoints(20);
+        Cell toCell = billRow.createCell(0);
+        toCell.setCellValue("TO: XXXXXXXXXXXXXXXXXXXXXXXXXXX");
+        toCell.setCellStyle(styles.get("text"));
+        
+        Cell billTitleCell = billRow.createCell(3);
+        billTitleCell.setCellValue("2025骞�06鏈堝璐﹀崟");
+        billTitleCell.setCellStyle(styles.get("text"));
+        sheet.addMergedRegion(new CellRangeAddress(1, 1, 3, 6));
+        
+        // 鍏徃淇℃伅琛�
+        Row companyRow = sheet.createRow(2);
+        companyRow.setHeightInPoints(20);
+        Cell fromCell = companyRow.createCell(0);
+        fromCell.setCellValue("FROM:鐝犳捣甯傛眹鐣呬氦閫氭姇璧勬湁闄愬叕鍙�");
+        fromCell.setCellStyle(styles.get("text"));
+    }
+
+    /**
+     * 鍒涘缓琛ㄦ牸琛ㄥご
+     *
+     * @param sheet 宸ヤ綔琛�
+     * @param styles 鏍峰紡鏄犲皠
+     */
+    private void createTableHeader(SXSSFSheet sheet, Map<String, CellStyle> styles) {
+        Row headerRow = sheet.createRow(3);
+        headerRow.setHeightInPoints(25);
+        
+        String[] headers = {"搴忓彿", "瑁呰揣鏃ユ湡", "瑁呰揣鐐�", "鍗歌揣鐐�", "杞︾墝", "鍨嬪彿", "杩愯垂(浜烘皯甯�)", "鏃犵紳璐�(浜烘皯甯�)", "棣欐腐娓呭叧璐�(浜烘皯甯�)", "澶囨敞"};
+        for (int i = 0; i < headers.length; i++) {
+            Cell cell = headerRow.createCell(i);
+            cell.setCellValue(headers[i]);
+            cell.setCellStyle(styles.get("header"));
+        }
+    }
+
+    /**
+     * 鍒涘缓澶囨敞璇存槑鍖哄煙
+     *
+     * @param sheet 宸ヤ綔琛�
+     * @param styles 鏍峰紡鏄犲皠
+     * @param startRow 璧峰琛�
+     */
+    private void createNotesArea(SXSSFSheet sheet, Map<String, CellStyle> styles, int startRow) {
+        Row note1Row = sheet.createRow(startRow);
+        Cell note1Cell = note1Row.createCell(0);
+        note1Cell.setCellValue("1.渚濇嵁鍚堝悓鐩稿叧瑙勫畾锛岃吹鍙搁渶鍚戞垜鍙告敮浠橈骏锛堝ぇ鍐欓噾棰濓細锛夌殑璐圭敤锛岃璐靛徃鍙婃椂瀹夋帓鏀粯銆�");
+        note1Cell.setCellStyle(styles.get("text"));
+        sheet.addMergedRegion(new CellRangeAddress(startRow, startRow, 0, 9));
+        
+        Row note2Row = sheet.createRow(startRow + 1);
+        Cell note2Cell = note2Row.createCell(0);
+        note2Cell.setCellValue("2.浼犵湡浠朵笌鍘熶欢璧峰悓绛夋硶寰嬫晥鍔涖��");
+        note2Cell.setCellStyle(styles.get("text"));
+        sheet.addMergedRegion(new CellRangeAddress(startRow + 1, startRow + 1, 0, 9));
+    }
+
+    /**
+     * 鍒涘缓璐︽埛淇℃伅鍖哄煙
+     *
+     * @param sheet 宸ヤ綔琛�
+     * @param styles 鏍峰紡鏄犲皠
+     * @param startRow 璧峰琛�
+     */
+    private void createAccountInfo(SXSSFSheet sheet, Map<String, CellStyle> styles, int startRow) {
+        Row accountTitleRow = sheet.createRow(startRow);
+        Cell accountTitleCell = accountTitleRow.createCell(0);
+        accountTitleCell.setCellValue("鐑﹁鏍稿纭骞惰浆鑷虫垜鍙稿涓嬪笎鍙凤細");
+        accountTitleCell.setCellStyle(styles.get("text"));
+        sheet.addMergedRegion(new CellRangeAddress(startRow, startRow, 0, 9));
+        
+        Row bankRow = sheet.createRow(startRow + 1);
+        Cell bankCell = bankRow.createCell(0);
+        bankCell.setCellValue("寮�鎴烽摱琛�:涓浗鍐滀笟鍙戝睍閾惰鐝犳捣甯傚垎琛�");
+        bankCell.setCellStyle(styles.get("text"));
+        sheet.addMergedRegion(new CellRangeAddress(startRow + 1, startRow + 1, 0, 9));
+        
+        Row accountRow = sheet.createRow(startRow + 2);
+        Cell accountCell = accountRow.createCell(0);
+        accountCell.setCellValue("璐﹀彿:20344990100000422001");
+        accountCell.setCellStyle(styles.get("text"));
+        sheet.addMergedRegion(new CellRangeAddress(startRow + 2, startRow + 2, 0, 9));
+        
+        Row nameRow = sheet.createRow(startRow + 3);
+        Cell nameCell = nameRow.createCell(0);
+        nameCell.setCellValue("鎴峰悕:鐝犳捣甯傛眹鐣呬氦閫氭姇璧勬湁闄愬叕鍙�");
+        nameCell.setCellStyle(styles.get("text"));
+        sheet.addMergedRegion(new CellRangeAddress(startRow + 3, startRow + 3, 0, 9));
+    }
+
+    /**
+     * 鍒涘缓绛惧瓧鐩栫珷鍖哄煙
+     *
+     * @param sheet 宸ヤ綔琛�
+     * @param styles 鏍峰紡鏄犲皠
+     * @param startRow 璧峰琛�
+     */
+    private void createSignatureArea(SXSSFSheet sheet, Map<String, CellStyle> styles, int startRow) {
+        Row payerRow = sheet.createRow(startRow);
+        Cell payerCell = payerRow.createCell(0);
+        payerCell.setCellValue("浠樻鍗曚綅锛堢敳鏂癸級锛歑XXXXXXXXXXXXX");
+        payerCell.setCellStyle(styles.get("text"));
+        
+        Cell payeeCell = payerRow.createCell(6);
+        payeeCell.setCellValue("鏀舵鍗曚綅锛堜箼鏂癸級锛氱彔娴峰競姹囩晠浜ら�氭姇璧勬湁闄愬叕鍙�");
+        payeeCell.setCellStyle(styles.get("text"));
+        
+        Row creatorRow = sheet.createRow(startRow + 1);
+        Cell creatorCell = creatorRow.createCell(0);
+        creatorCell.setCellValue("鍒惰〃浜哄憳锛�");
+        creatorCell.setCellStyle(styles.get("text"));
+        
+        Cell creatorCell2 = creatorRow.createCell(6);
+        creatorCell2.setCellValue("鍒惰〃浜哄憳锛�");
+        creatorCell2.setCellStyle(styles.get("text"));
+        
+        Row checkerRow = sheet.createRow(startRow + 2);
+        Cell checkerCell = checkerRow.createCell(0);
+        checkerCell.setCellValue("瀹℃牳浜哄憳锛�");
+        checkerCell.setCellStyle(styles.get("text"));
+        
+        Cell checkerCell2 = checkerRow.createCell(6);
+        checkerCell2.setCellValue("瀹℃牳浜哄憳锛�");
+        checkerCell2.setCellStyle(styles.get("text"));
+        
+        Row reviewerRow = sheet.createRow(startRow + 3);
+        Cell reviewerCell = reviewerRow.createCell(0);
+        reviewerCell.setCellValue("澶嶆牳浜哄憳锛�");
+        reviewerCell.setCellStyle(styles.get("text"));
+        
+        Cell reviewerCell2 = reviewerRow.createCell(6);
+        reviewerCell2.setCellValue("澶嶆牳浜哄憳锛�");
+        reviewerCell2.setCellStyle(styles.get("text"));
+        
+        Row stampRow = sheet.createRow(startRow + 4);
+        Cell stampCell = stampRow.createCell(0);
+        stampCell.setCellValue("鐩栫珷锛�");
+        stampCell.setCellStyle(styles.get("text"));
+        
+        Cell stampCell2 = stampRow.createCell(6);
+        stampCell2.setCellValue("鐩栫珷锛�");
+        stampCell2.setCellStyle(styles.get("text"));
+        
+        Row dateRow = sheet.createRow(startRow + 5);
+        Cell dateCell = dateRow.createCell(0);
+        dateCell.setCellValue("鏃ユ湡锛�2026骞�  鏈�  鏃�");
+        dateCell.setCellStyle(styles.get("text"));
+        
+        Cell dateCell2 = dateRow.createCell(6);
+        dateCell2.setCellValue("鏃ユ湡锛�2026骞�  鏈�  鏃�");
+        dateCell2.setCellStyle(styles.get("text"));
     }
 
 
@@ -257,7 +750,10 @@
 
         tmsArBill.setSettleAmount(BigDecimal.ZERO);
         tmsReceivableFees.forEach(item ->{
-
+            List<TmsReceivableFeeItem> tmsReceivableFeeItems = tmsReceivableFeeItemMapper.selectTmsReceivableFeeItemList(new TmsReceivableFeeItem() {{
+                setHeadId(item.getId());
+            }});
+            item.setItems(tmsReceivableFeeItems);
             BigDecimal rmbAmount = item.getReceivableHKBAmount()
                     .multiply(exchangeRate)
                     .setScale(2, RoundingMode.HALF_UP);
@@ -308,8 +804,138 @@
             return billItem;
         }).collect(Collectors.toList());
         tmsArBillItemMapper.insertTmsArBillItemBatch(rmb);
+
+        // 鍚戝閮ㄧ郴缁熸帹閫佹暟鎹�
+        AsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();
+        executor.execute(() -> pushToExternalSystem(tmsArBill, tmsReceivableFees));
+        
         return AjaxResult.success();
     }
+    
+    /**
+     * 鍚戝閮ㄧ郴缁熸帹閫佹暟鎹�
+     * @param tmsArBill 搴旀敹璐﹀崟
+     * @param tmsReceivableFees 搴旀敹璐圭敤鍒楄〃
+     */
+    @Async
+    protected void pushToExternalSystem(TmsArBill tmsArBill, List<TmsReceivableFee> tmsReceivableFees) {
+        java.util.Map<String, Object> requestBody = new java.util.HashMap<>();
+        try {
+            // 鏋勫缓璇锋眰浣�
+            String apiUrl = url+"/addBill";
+            
+            // 鏋勫缓bill閮ㄥ垎
+            java.util.Map<String, Object> billMap = new java.util.HashMap<>();
+            billMap.put("billName", tmsArBill.getBillName());
+            billMap.put("customerName", tmsArBill.getCustomerName());
+            billMap.put("payee", "");
+            billMap.put("responsiblePerson", "");
+            billMap.put("responsibleLeader", "");
+            billMap.put("settlementMethod", "");
+            billMap.put("businessType", "");
+            billMap.put("promotionRequirement", "");
+            billMap.put("isInternalSettlement", "0");
+            billMap.put("internalSettlementUnit", "");
+            billMap.put("documentCount", tmsReceivableFees.size());
+            billMap.put("totalAmount", tmsArBill.getSettleAmount());
+            billMap.put("currency", "RMB");
+            billMap.put("discountAmount", 0.00);
+            billMap.put("receivedAmount", 0.00);
+            billMap.put("pendingAmount", tmsArBill.getSettleAmount());
+            billMap.put("exchangeRate", tmsArBill.getSettleRate());
+            billMap.put("cnyAmount", tmsArBill.getSettleAmount());
+            billMap.put("periodType", "");
+            billMap.put("businessStartDate", "");
+            billMap.put("businessEndDate", "");
+            billMap.put("billingStartDate", "");
+            billMap.put("billingEndDate", "");
+            billMap.put("billGenerateDate", "");
+            billMap.put("billSendDate", "");
+            billMap.put("billDueDate", "");
+            billMap.put("settlementCategory", "");
+            billMap.put("settlementPeriod", "");
+            billMap.put("status", "0");
+            billMap.put("remark", "");
+            
+            // 鏋勫缓fees閮ㄥ垎
+            List<java.util.Map<String, Object>> feesList = new java.util.ArrayList<>();
+            for (int i = 0; i < tmsReceivableFees.size(); i++) {
+                TmsReceivableFee fee = tmsReceivableFees.get(i);
+                java.util.Map<String, Object> feeMap = new java.util.HashMap<>();
+                feeMap.put("serialNumber", String.format("%03d", i + 1));
+                feeMap.put("relatedBillNo", "");
+                feeMap.put("sourceSystem", "TMS");
+                feeMap.put("businessSector", "0");
+                feeMap.put("documentType", "0");
+                feeMap.put("documentNo", fee.getDispatchNo() != null ? fee.getDispatchNo() : "");
+                feeMap.put("isInternalSettlement", "0");
+                feeMap.put("internalSettlementUnit", "");
+                feeMap.put("customerName", tmsArBill.getCustomerName());
+                feeMap.put("projectName", fee.getProjectName() != null ? fee.getProjectName() : "");
+                feeMap.put("businessTime", fee.getDispatchConfirmTime());
+                feeMap.put("receivableConfirmTime", fee.getDispatchConfirmTime());
+                feeMap.put("receivableAmount", fee.getReceivableRMBAmount().add(fee.getReceivableHKBAmount()));
+                // 鏋勫缓receivableAmountStr
+                BigDecimal rmbAmount = fee.getReceivableRMBAmount();
+                BigDecimal hkbAmount = fee.getReceivableHKBAmount();
+                StringBuilder amountStr = new StringBuilder();
+                if (rmbAmount.compareTo(BigDecimal.ZERO) > 0) {
+                    amountStr.append(rmbAmount).append("浜烘皯甯�");
+                }
+                if (hkbAmount.compareTo(BigDecimal.ZERO) > 0) {
+                    if (amountStr.length() > 0) {
+                        amountStr.append(" ");
+                    }
+                    amountStr.append(hkbAmount).append("娓竵");
+                }
+                feeMap.put("receivableAmountStr", amountStr.toString());
+                feeMap.put("status", "1");
+                feeMap.put("remark", "");
+                
+                // 鏋勫缓feeDetails閮ㄥ垎
+                List<java.util.Map<String, Object>> feeDetailsList = new java.util.ArrayList<>();
+                List<TmsReceivableFeeItem> items = fee.getItems();
+                for (int j = 0; j < items.size(); j++) {
+                    TmsReceivableFeeItem item = items.get(j);
+                    java.util.Map<String, Object> feeDetailMap = new java.util.HashMap<>();
+                    feeDetailMap.put("serialNumber", String.format("%03d", j + 1));
+                    feeDetailMap.put("feeType", item.getFeeType());
+                    feeDetailMap.put("feeName", item.getFeeName());
+                    feeDetailMap.put("billingUnit", "娆�");
+                    feeDetailMap.put("unitPrice", item.getRegisterAmount());
+                    feeDetailMap.put("billingQuantity", item.getRegisterAmount());
+                    feeDetailMap.put("billingAmount", item.getRegisterAmount());
+                    feeDetailMap.put("actualAmount", item.getRegisterAmount());
+                    feeDetailMap.put("currency", item.getCurrency());
+                    feeDetailMap.put("feeRegTime", item.getRegisterTime());
+                    feeDetailMap.put("remark", "");
+                    feeDetailsList.add(feeDetailMap);
+                }
+
+
+                feeMap.put("feeDetails", feeDetailsList);
+                feesList.add(feeMap);
+            }
+            
+            // 鏋勫缓瀹屾暣璇锋眰浣�
+            requestBody.put("bill", billMap);
+            requestBody.put("fees", feesList);
+            
+            // 璁剧疆HTTP澶�
+            HttpHeaders headers = new HttpHeaders();
+            headers.setContentType(MediaType.APPLICATION_JSON);
+            HttpEntity<String> entity = new HttpEntity<>(JSON.toJSONString(requestBody), headers);
+            
+            // 鍙戦�丄PI璇锋眰
+            ResponseEntity<String> response = restTemplate.exchange(apiUrl, HttpMethod.POST, entity, String.class);
+            logger.info("鎺ㄩ�佹暟鎹埌澶栭儴绯荤粺鎴愬姛锛屽搷搴�: {}", response.getBody());
+        } catch (Exception e) {
+            logger.error("鎺ㄩ�佹暟鎹埌澶栭儴绯荤粺澶辫触锛岃处鍗旾D: {}, 瀹㈡埛: {}", 
+                tmsArBill.getId(), tmsArBill.getCustomerName(), e);
+            logger.debug("鎺ㄩ�佸け璐ョ殑璇锋眰鏁版嵁: {}", JSON.toJSONString(requestBody));
+            // 鎺ㄩ�佸け璐ヤ笉褰卞搷涓绘祦绋嬶紝璁板綍鏃ュ織鍗冲彲
+        }
+    }
 
     /**
      * 鍒犻櫎搴旀敹璐圭敤淇℃伅

--
Gitblit v1.8.0