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; import com.ruoyi.system.service.ISysConfigService; import com.ruoyi.system.service.ISystemDataNoService; import com.ruoyi.tms.domain.*; import com.ruoyi.tms.mapper.*; import org.springframework.beans.factory.annotation.Autowired; 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; import com.ruoyi.common.enums.DataSourceType; import com.ruoyi.common.core.service.BaseService; import com.ruoyi.tms.service.ITmsReceivableFeeService; import com.ruoyi.common.core.text.Convert; /** * 应收费用Service业务层处理 * * @author ruoyi * @date 2025-12-12 */ @Service @Transactional(rollbackFor = Exception.class) public class TmsReceivableFeeServiceImpl extends BaseService implements ITmsReceivableFeeService { protected final Logger logger = LoggerFactory.getLogger(getClass()); @Resource private TmsReceivableFeeMapper tmsReceivableFeeMapper; @Resource private TmsReceivableFeeItemMapper tmsReceivableFeeItemMapper; @Resource private TmsDispatchOrderMapper tmsDispatchOrderMapper; @Resource ISysConfigService sysConfigService; @Autowired ISystemDataNoService systemDataNoService; @Resource private TmsArBillMapper tmsArBillMapper; @Resource private TmsArBillItemMapper tmsArBillItemMapper; @Autowired private RestTemplate restTemplate; @Value("${custom.cwxtApi.url}") private String url; @Autowired private RedisCache redisCache; /** * 查询应收费用 * * @param id 应收费用ID * @return 应收费用 */ @DataSource(DataSourceType.SLAVE) @Override public TmsReceivableFee selectTmsReceivableFeeById(Integer id) { TmsReceivableFee tmsReceivableFee = tmsReceivableFeeMapper.selectTmsReceivableFeeById(id); List tmsReceivableFeeItems = tmsReceivableFeeItemMapper.selectTmsReceivableFeeItemList(new TmsReceivableFeeItem() {{ setHeadId(id); }}); tmsReceivableFee.setItems(tmsReceivableFeeItems); return tmsReceivableFee; } @Override public TmsReceivableFee selectTmsReceivableFeeByDispatchNo(String dispatchNo) { TmsReceivableFee tmsReceivableFee = tmsReceivableFeeMapper.selectOne(new LambdaUpdateWrapper() .eq(TmsReceivableFee::getDispatchNo, dispatchNo) .ne(TmsReceivableFee::getStatus, 2) .last("limit 1") ); if(tmsReceivableFee != null){ List tmsReceivableFeeItems = tmsReceivableFeeItemMapper.selectTmsReceivableFeeItemList(new TmsReceivableFeeItem() {{ setHeadId(tmsReceivableFee.getId()); }}); tmsReceivableFee.setItems(tmsReceivableFeeItems); return tmsReceivableFee; } return null; } /** * 查询应收费用 记录数 * * @param tmsReceivableFee 应收费用 * @return 应收费用集合 */ @DataSource(DataSourceType.SLAVE) @Override public int selectTmsReceivableFeeCount(TmsReceivableFee tmsReceivableFee) { return tmsReceivableFeeMapper.selectTmsReceivableFeeCount(tmsReceivableFee); } /** * 查询应收费用列表 * * @param tmsReceivableFee 应收费用 * @return 应收费用 */ @DataSource(DataSourceType.SLAVE) @Override public List selectTmsReceivableFeeList(TmsReceivableFee tmsReceivableFee) { return tmsReceivableFeeMapper.selectTmsReceivableFeeList(tmsReceivableFee); } /** * 查询应收费用列表 异步 导出 * * @param tmsReceivableFee 应收费用 * @param exportKey 导出功能的唯一标识 * @return 应收费用集合 */ @DataSource(DataSourceType.SLAVE) @Async @Override public void export(TmsReceivableFee tmsReceivableFee,String exportKey) { super.export(TmsReceivableFee.class,exportKey,"tmsReceivableFeeData",(pageNum)->{ 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 styles = createArBillStyles(workbook); // 标题区域 createTitleArea(sheet, styles, tmsReceivableFee); // 表头 createTableHeader(sheet, styles); // 数据区域 int startRow = 4; List 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 createArBillStyles(SXSSFWorkbook workbook) { Map 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 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 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 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 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 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")); } /** * 新增应收费用 * * @param tmsReceivableFee 应收费用 * @return 结果 */ @Override public int insertTmsReceivableFee(TmsReceivableFee tmsReceivableFee) { tmsReceivableFee.setCreateTime(DateUtils.getNowDate()); return tmsReceivableFeeMapper.insertTmsReceivableFee(tmsReceivableFee); } /** * 新增应收费用[批量] * * @param tmsReceivableFees 应收费用 * @return 结果 */ @Override public int insertTmsReceivableFeeBatch(List tmsReceivableFees) { int rows = tmsReceivableFeeMapper.insertTmsReceivableFeeBatch(tmsReceivableFees); return rows; } /** * 修改应收费用 * * @param tmsReceivableFee 应收费用 * @return 结果 */ @Override public int updateTmsReceivableFee(TmsReceivableFee tmsReceivableFee) { tmsReceivableFee.setUpdateTime(DateUtils.getNowDate()); return tmsReceivableFeeMapper.updateTmsReceivableFee(tmsReceivableFee); } /** * 修改应收费用[批量] * * @param tmsReceivableFees 应收费用 * @return 结果 */ @Override public int updateTmsReceivableFeeBatch(List tmsReceivableFees){ return tmsReceivableFeeMapper.updateTmsReceivableFeeBatch(tmsReceivableFees); } @Override public int closeTmsReceivableFeeById(Integer id) { TmsReceivableFee tmsReceivableFee = selectTmsReceivableFeeById(id); if(tmsReceivableFee != null){ tmsReceivableFee.setStatus(2); updateTmsReceivableFee(tmsReceivableFee); tmsDispatchOrderMapper.update(new LambdaUpdateWrapper() .eq(TmsDispatchOrder::getId,tmsReceivableFee.getDispatchId()) .set(TmsDispatchOrder::getAccountsReceivableStatus,0) ); }else{ throw new RuntimeException("数据不存在"); } return 1; } /** * 删除应收费用对象 * * @param ids 需要删除的数据ID * @return 结果 */ @Override public int deleteTmsReceivableFeeByIds(String ids) { return deleteTmsReceivableFeeByIds(Convert.toIntArray(ids)); } /** * 删除应收费用对象 * * * @param ids 需要删除的数据ID * @return 结果 */ @Override public int deleteTmsReceivableFeeByIds(Integer[] ids) { return tmsReceivableFeeMapper.deleteTmsReceivableFeeByIds(ids); } @Override public TmsArBill initArGenerate(Integer[] ids) { List tmsReceivableFees = tmsReceivableFeeMapper. selectBatchIds(Arrays.asList(ids)); // 判断是否有不同的客户 boolean hasDifferentCustomers = tmsReceivableFees.stream() .map(TmsReceivableFee::getCustomerId) .distinct() .count() > 1; if (hasDifferentCustomers) { throw new RuntimeException("所选费用包含不同客户,无法合并生成应收账单"); } TmsArBill tmsArBill = new TmsArBill(); tmsArBill.setDispatchCount(tmsReceivableFees.size()); String s = sysConfigService.selectConfigByKey("sys.hk.rmb.rate"); BigDecimal exchangeRate = new BigDecimal(s); tmsArBill.setSettleRate(exchangeRate); TmsReceivableFee tmsReceivableFee = tmsReceivableFees.get(0); tmsArBill.setCustomerId(tmsReceivableFee.getCustomerId()); tmsArBill.setCustomerName(tmsReceivableFee.getCustomerName()); tmsArBill.setBillName(DateUtils.dateTime()+tmsArBill.getCustomerName()+"账单"); tmsArBill.setSettleAmount(BigDecimal.ZERO); tmsReceivableFees.forEach(item ->{ List tmsReceivableFeeItems = tmsReceivableFeeItemMapper.selectTmsReceivableFeeItemList(new TmsReceivableFeeItem() {{ setHeadId(item.getId()); }}); item.setItems(tmsReceivableFeeItems); BigDecimal rmbAmount = item.getReceivableHKBAmount() .multiply(exchangeRate) .setScale(2, RoundingMode.HALF_UP); item.setReceivableRmbSumAmount( item.getReceivableRMBAmount().add(rmbAmount)); tmsArBill.setSettleAmount(tmsArBill.getSettleAmount().add(item.getReceivableRmbSumAmount())); }); tmsArBill.setTmsReceivableFees(tmsReceivableFees); tmsArBill.setActualSettlementAmount(tmsArBill.getSettleAmount()); return tmsArBill; } @Override public AjaxResult arGenerate(Integer[] ids) { TmsArBill tmsArBill = initArGenerate(ids); tmsArBill.setSystemNo(systemDataNoService.getNoByKey(SystemDataNoEnum.ZD)); tmsArBill.setSettledAmount(BigDecimal.ZERO); tmsArBill.setInvoiceStatus(0); tmsArBill.setStatus(0); tmsArBill.setCreateBy(SecurityUtils.getUsername()); tmsArBillMapper.insertTmsArBill(tmsArBill); tmsReceivableFeeMapper.update(new LambdaUpdateWrapper() .set(TmsReceivableFee::getStatus,1) .set( TmsReceivableFee::getBillRelationId,tmsArBill.getId()) .set(TmsReceivableFee::getBillRelationNo ,tmsArBill.getSystemNo()) .in(TmsReceivableFee::getId, ids) ); List tmsReceivableFees = tmsArBill.getTmsReceivableFees(); List rmb = tmsReceivableFees.stream().map(item -> { TmsArBillItem billItem = new TmsArBillItem(); billItem.setBillId(tmsArBill.getId()); billItem.setProjectName(item.getProjectName()); billItem.setDispatchNo(item.getDispatchNo()); billItem.setOrderTime(item.getCreateTime()); billItem.setEstimateAmount(item. getReceivableRmbSumAmount()); billItem.setCurrency("RMB"); billItem.setArFeeId(item.getId()); billItem.setStatus(0); 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 tmsReceivableFees) { java.util.Map requestBody = new java.util.HashMap<>(); try { // 构建请求体 String apiUrl = url+"/addBill"; // 构建bill部分 java.util.Map 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> feesList = new java.util.ArrayList<>(); for (int i = 0; i < tmsReceivableFees.size(); i++) { TmsReceivableFee fee = tmsReceivableFees.get(i); java.util.Map 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> feeDetailsList = new java.util.ArrayList<>(); List items = fee.getItems(); for (int j = 0; j < items.size(); j++) { TmsReceivableFeeItem item = items.get(j); java.util.Map 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 entity = new HttpEntity<>(JSON.toJSONString(requestBody), headers); // 发送API请求 ResponseEntity response = restTemplate.exchange(apiUrl, HttpMethod.POST, entity, String.class); logger.info("推送数据到外部系统成功,响应: {}", response.getBody()); } catch (Exception e) { logger.error("推送数据到外部系统失败,账单ID: {}, 客户: {}", tmsArBill.getId(), tmsArBill.getCustomerName(), e); logger.debug("推送失败的请求数据: {}", JSON.toJSONString(requestBody)); // 推送失败不影响主流程,记录日志即可 } } /** * 删除应收费用信息 * * @param id 应收费用ID * @return 结果 */ @Override public int deleteTmsReceivableFeeById(Integer id) { return tmsReceivableFeeMapper.deleteTmsReceivableFeeById(id); } }