| | |
| | | import java.io.FileOutputStream; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.HashMap; |
| | | import java.util.HashSet; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | 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.exception.ServiceException; |
| | | 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; |
| | |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * 导出对账单一式多联格式 |
| | | * |
| | | * @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("付款单位(甲方):XXXXXXXXXXXXXX"); |
| | | 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")); |
| | | } |
| | | |
| | | |
| | | /** |