diff --git a/bonus-modules/bonus-material/src/main/java/com/bonus/material/basic/domain/report/DownloadRequest.java b/bonus-modules/bonus-material/src/main/java/com/bonus/material/basic/domain/report/DownloadRequest.java index 99fb6bfd..013465ec 100644 --- a/bonus-modules/bonus-material/src/main/java/com/bonus/material/basic/domain/report/DownloadRequest.java +++ b/bonus-modules/bonus-material/src/main/java/com/bonus/material/basic/domain/report/DownloadRequest.java @@ -1,7 +1,9 @@ package com.bonus.material.basic.domain.report; +import com.bonus.material.settlement.domain.SltAgreementInfo; import io.swagger.annotations.ApiModelProperty; import lombok.Data; +import org.springframework.web.bind.annotation.RequestBody; import java.math.BigDecimal; import java.util.List; @@ -11,6 +13,8 @@ public class DownloadRequest { private String zipName; private List items; + private List list; + private String taskId; @Data diff --git a/bonus-modules/bonus-material/src/main/java/com/bonus/material/settlement/controller/SltAgreementInfoController.java b/bonus-modules/bonus-material/src/main/java/com/bonus/material/settlement/controller/SltAgreementInfoController.java index 8b250add..485d912e 100644 --- a/bonus-modules/bonus-material/src/main/java/com/bonus/material/settlement/controller/SltAgreementInfoController.java +++ b/bonus-modules/bonus-material/src/main/java/com/bonus/material/settlement/controller/SltAgreementInfoController.java @@ -3,10 +3,15 @@ package com.bonus.material.settlement.controller; import java.io.*; import java.math.BigDecimal; import java.math.RoundingMode; +import java.net.HttpURLConnection; +import java.net.URL; import java.net.URLEncoder; import java.text.SimpleDateFormat; +import java.time.LocalDate; import java.util.*; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; @@ -21,13 +26,17 @@ import com.bonus.common.biz.config.PoiOutPage; import com.bonus.common.biz.constant.GlobalConstants; import com.bonus.common.biz.utils.RequestContext; +import com.bonus.common.biz.utils.StringHelper; import com.bonus.common.core.exception.ServiceException; import com.bonus.common.core.utils.ServletUtils; import com.bonus.common.log.enums.OperaType; import com.bonus.common.log.enums.OperatorType; import com.bonus.material.basic.domain.BmProject; +import com.bonus.material.basic.domain.dto.DownloadProgress; +import com.bonus.material.basic.domain.report.DownloadRequest; import com.bonus.material.common.annotation.PreventRepeatSubmit; import com.bonus.material.common.domain.dto.SelectDto; +import com.bonus.material.common.utils.DocxUtil; import com.bonus.material.part.domain.PartLeaseInfo; import com.bonus.material.settlement.domain.*; import com.bonus.material.settlement.domain.dto.ExportProgressManager; @@ -42,12 +51,18 @@ import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.compress.archivers.zip.Zip64Mode; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.apache.poi.hssf.usermodel.*; import org.apache.poi.ss.formula.functions.T; +import org.apache.poi.util.IOUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import com.bonus.common.log.annotation.SysLog; @@ -60,6 +75,7 @@ import com.bonus.common.core.utils.poi.ExcelUtil; import com.bonus.common.core.web.page.TableDataInfo; import java.util.ArrayList; +import java.util.zip.Deflater; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -93,6 +109,7 @@ public class SltAgreementInfoController extends BaseController { @Autowired private ExportProgressManager exportProgressManager; + private static final Map downloadProgressMap = new ConcurrentHashMap<>(); /** * 查询结算信息列表 */ @@ -2028,236 +2045,6 @@ public class SltAgreementInfoController extends BaseController { } -// /** -// * 一键批量导出未结算报表(zip) -// */ -// @ApiOperation(value = "一键批量导出未结算报表") -// @PreventRepeatSubmit -// @SysLog(title = "结算信息", businessType = OperaType.EXPORT, logType = 1,module = "结算管理->一键批量导出未结算报表") -// @PostMapping("/exportUnsettled") -// public void exportUnsettled(HttpServletResponse response, @RequestBody List list) throws Exception { -// // 创建临时文件夹 -// String tempDir = System.getProperty("java.io.tmpdir") + File.separator + UUID.randomUUID(); -// new File(tempDir).mkdirs(); -// -// try { -// for(SltAgreementInfo info : list){ -// if(info.getAgreementId() == null){ -// continue; -// } -// //根据协议id获取结算单位和结算工程 -// SltAgreementInfo agreementInfo = sltAgreementInfoMapper.getUnitAndProjectByAgreementId(info.getAgreementId()); -// // 生成文件名 -// String fileName = ""; -// String unitName = ""; -// String projectName = ""; -// // 清理非法字符(Windows文件名不允许: \ / : * ? " < > |) -// unitName = cleanFileName(agreementInfo.getUnitName()); -// projectName = cleanFileName(agreementInfo.getProjectName()); -// String agreementCode = cleanFileName(agreementInfo.getAgreementCode()); -// -// // 构建安全的文件名(限制总长度不超过150字符) -// String rawFileName = agreementCode + "-" + unitName + "-" + projectName + "_结算单.xls"; -// fileName = rawFileName.length() > 150 ? -// rawFileName.substring(0, 150) + ".xls" : rawFileName; -// -// //租赁费用明细 -// BigDecimal totalCostLease = BigDecimal.valueOf(0.00); -// List leaseList = new ArrayList<>(); -// leaseList = sltAgreementInfoMapper.getLeaseList(info); -// -// for (SltAgreementInfo bean : leaseList) { -// if (null == bean.getLeasePrice()) { -// bean.setLeasePrice(BigDecimal.valueOf(0.00)); -// } -// if (null == bean.getNum()) { -// bean.setNum(BigDecimal.valueOf(0L)); -// } -// if (null == bean.getLeaseDays()) { -// bean.setLeaseDay(0L); -// } -// BigDecimal leasePrice = bean.getLeasePrice(); -// BigDecimal num = bean.getNum(); -// BigDecimal leaseDays = new BigDecimal(bean.getLeaseDays()); -// BigDecimal costs = leasePrice.multiply(num).multiply(leaseDays); -// if(costs!=null){ -// totalCostLease = totalCostLease.add(costs); -// } -// bean.setCosts(costs); -// } -// List lease = Convert.toList(SltLeaseInfo.class, leaseList); -// -// //丢失费用明细 -// BigDecimal totalCostLose = BigDecimal.valueOf(0.00); -// List loseList = new ArrayList<>(); -// -// loseList = sltAgreementInfoMapper.getLoseList(info); -// -// for (SltAgreementInfo bean : loseList) { -// if (null == bean.getBuyPrice()) { -// bean.setBuyPrice(BigDecimal.valueOf(0.00)); -// } -// if (null == bean.getNum()) { -// bean.setNum(BigDecimal.valueOf(0L)); -// } -// BigDecimal buyPrice = bean.getBuyPrice(); -// BigDecimal num = bean.getNum(); -// // 原价 x 数量 -// BigDecimal costs = buyPrice.multiply(num); -// if(costs!=null){ -// totalCostLose = totalCostLose.add(costs); -// } -// //计算租赁费用 -// bean.setCosts(costs); -// } -// List lose = Convert.toList(SltLeaseInfo.class, loseList); -// -// //维修费用明细 -// BigDecimal totalCostRepair = BigDecimal.valueOf(0.00); -// List repairList = new ArrayList<>(); -// -// -// List taskRepairList = taskMapper.getTaskIdList(info); -// List taskRepairList2 = new ArrayList<>(); -// taskRepairList2 = checkTeamAgreementInfo(info); -// if (null != taskRepairList && !taskRepairList.isEmpty()) { -// if (null != taskRepairList2 && !taskRepairList2.isEmpty()) { -// taskRepairList.addAll(taskRepairList2); -// } -// repairList = sltAgreementInfoMapper.getRepairDetailsList(info, taskRepairList); -// } -// -// for (SltAgreementInfo bean : repairList) { -// if (bean.getCosts()!=null && (bean.getPartType().equals("收费"))) { -// totalCostRepair = totalCostRepair.add(bean.getCosts()); -// } -// } -// List repair = Convert.toList(SltLeaseInfo.class, repairList); -// -// //报废费用明细 -// BigDecimal totalCostScrap = BigDecimal.valueOf(0.00); -// List scrapList = new ArrayList<>(); -// -// List taskScrapList = taskMapper.getTaskIdList(info); -// -// List taskScrapList2 = new ArrayList<>(); -// taskScrapList2 = checkTeamAgreementInfo(info); -// -// if (null != taskScrapList && !taskScrapList.isEmpty()) { -// -// if (null != taskScrapList2 && !taskScrapList2.isEmpty()) { -// taskScrapList.addAll(taskScrapList2); -// } -// scrapList = sltAgreementInfoMapper.getScrapDetailsList(info, taskScrapList); -// } -// -// -// for (SltAgreementInfo bean : scrapList) { -// if (bean.getCosts()!=null && (bean.getPartType().equals("收费"))) { -// totalCostScrap = totalCostScrap.add(bean.getCosts()); -// } -// } -// List scrap = Convert.toList(SltLeaseInfo.class, scrapList); -// -// //减免费用明细 -// BigDecimal totalCostReduction = BigDecimal.valueOf(0.00); -// List reductionList = new ArrayList<>(); -// -// if (info.getAgreementId() != null){ -// SltAgreementReduce bean =new SltAgreementReduce(); -// bean.setAgreementId(info.getAgreementId()); -// reductionList = sltAgreementRecudceMapper.getReductionList(bean); -// } -// -// for (SltAgreementReduce reduction : reductionList) { -// if(reduction.getLeaseMoney()!=null){ -// totalCostReduction = totalCostReduction.add(reduction.getLeaseMoney()); -// } -// } -// List reduction = Convert.toList(SltLeaseInfo.class, reductionList); -// -// -// List> resultsLease = new ArrayList<>(); -// List> resultsLose = new ArrayList<>(); -// List> resultsRepair = new ArrayList<>(); -// List> resultsScrap = new ArrayList<>(); -// List> resultsReduction = new ArrayList<>(); -// if (lease!= null) { -// for (SltLeaseInfo bean : lease) { -// Map maps = outReceiveDetailsBeanToMap(bean, 1, 1); -// resultsLease.add(maps); -// } -// } -// if (lose!= null) { -// for (SltLeaseInfo bean : lose) { -// Map maps = outReceiveDetailsBeanToMap(bean, 2, 1); -// resultsLose.add(maps); -// } -// } -// if (repair!= null) { -// for (SltLeaseInfo bean : repair) { -// Map maps = outReceiveDetailsBeanToMap(bean, 3, 1); -// resultsRepair.add(maps); -// } -// } -// if (scrap!= null) { -// for (SltLeaseInfo bean : scrap) { -// Map maps = outReceiveDetailsBeanToMap(bean, 4, 1); -// resultsScrap.add(maps); -// } -// } -// if (reduction!= null) { -// for (SltLeaseInfo bean : reduction) { -// Map maps = outReceiveDetailsBeanToMap(bean, 5, 1); -// resultsReduction.add(maps); -// } -// } -// -// List headersLease = receiveDetailsHeader(1,1); -// List headersLose = receiveDetailsHeader(2,1); -// List headersRepair = receiveDetailsHeader(3,1); -// List headersScrap = receiveDetailsHeader(4,1); -// List headersReduction = receiveDetailsHeader(5,1); -// -//// fileName = agreementInfo.getAgreementCode() + "-" + unitName + "-" + projectName+ "_结算单.xls" ; -// // 导出单个Excel文件 -// String filePath = tempDir + File.separator + fileName; -// try (FileOutputStream fos = new FileOutputStream(filePath)) { -// HSSFWorkbook workbook = PoiOutPage.excelForcheckAll(resultsLease,resultsLose,resultsRepair,resultsScrap,resultsReduction, headersLease,headersLose,headersRepair,headersScrap,headersReduction, "结算明细",projectName,unitName,totalCostLease,totalCostLose,totalCostRepair,totalCostScrap,totalCostReduction); -// workbook.write(fos); -// } -// -// } -// // 创建压缩包 -// String zipFileName = "结算单_" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + ".zip"; -// response.setContentType("application/zip"); -// response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(zipFileName, "UTF-8")); -// -// try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) { -// File[] files = new File(tempDir).listFiles(); -// if (files != null) { -// for (File file : files) { -// zipOut.putNextEntry(new ZipEntry(file.getName())); -// try (FileInputStream fis = new FileInputStream(file)) { -// byte[] buffer = new byte[1024]; -// int len; -// while ((len = fis.read(buffer)) > 0) { -// zipOut.write(buffer, 0, len); -// } -// } -// zipOut.closeEntry(); -// } -// } -// } -// } catch (Exception e) { -// log.error("一键批量导出未结算报表失败", e); -// } finally { -// // 删除临时文件 -// FileUtils.deleteDirectory(new File(tempDir)); -// } -// } - - /************************************************************************/ /** * 一键批量导出未结算报表(zip) */ @@ -2265,120 +2052,12 @@ public class SltAgreementInfoController extends BaseController { @PreventRepeatSubmit @SysLog(title = "结算信息", businessType = OperaType.EXPORT, logType = 1,module = "结算管理->一键批量导出未结算报表") @PostMapping("/exportUnsettled") - public ResponseEntity> exportUnsettled(HttpServletResponse response, @RequestBody List list) throws Exception { - -// 生成任务ID - String taskId = UUID.randomUUID().toString(); - - // 初始化进度 - exportProgressManager.initProgress(taskId, list.size()); - - // 异步执行导出任务 - CompletableFuture.runAsync(() -> { - try { - executeExport(taskId, list); - } catch (Exception e) { - log.error("导出任务执行失败, taskId: {}", taskId, e); - exportProgressManager.errorProgress(taskId, "导出失败: " + e.getMessage()); - } - }); - - // 立即返回任务ID - Map result = new HashMap<>(); - result.put("taskId", taskId); - result.put("message", "导出任务已开始,请通过taskId查询进度"); - result.put("total", String.valueOf(list.size())); - return ResponseEntity.ok(result); - } - - - @ApiOperation(value = "查询导出进度") - @PostMapping("/exZipProgress") - public ResponseEntity getExportProgress(@RequestBody SltAgreementInfo info) { - String taskId = info.getTaskId(); - ExportProgressManager.ExportProgress progress = exportProgressManager.getProgress(taskId); - if (progress == null) { - return ResponseEntity.notFound().build(); - } - return ResponseEntity.ok(progress); - } - - @ApiOperation(value = "下载导出文件") - @PostMapping("/dlExProgress") - public void downloadExportFile(@RequestBody SltAgreementInfo info, HttpServletResponse response) { - try { - ExportProgressManager.ExportProgress progress = exportProgressManager.getProgress(info.getTaskId()); - if (progress == null || !"completed".equals(progress.getStatus())) { - response.setStatus(HttpStatus.NOT_FOUND.value()); - response.getWriter().write("文件不存在或导出未完成"); - return; - } - - File file = new File(progress.getFileUrl()); - if (!file.exists()) { - response.setStatus(HttpStatus.NOT_FOUND.value()); - response.getWriter().write("文件不存在"); - return; - } - - String fileName = "结算单_" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + ".zip"; - response.setContentType("application/zip"); - response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8")); - - try (FileInputStream fis = new FileInputStream(file); - OutputStream os = response.getOutputStream()) { - byte[] buffer = new byte[1024]; - int len; - while ((len = fis.read(buffer)) > 0) { - os.write(buffer, 0, len); - } - } - - } catch (Exception e) { - log.error("下载文件失败, taskId: {}", info.getTaskId(), e); - try { - response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); - response.getWriter().write("下载失败: " + e.getMessage()); - } catch (IOException ex) { - log.error("写入响应失败", ex); - } - } - } - - @ApiOperation(value = "清理导出任务") - @DeleteMapping("/cleanExport/{taskId}") - public ResponseEntity> cleanExportTask(@PathVariable String taskId) { - try { - ExportProgressManager.ExportProgress progress = exportProgressManager.getProgress(taskId); - if (progress != null && progress.getFileUrl() != null) { - File file = new File(progress.getFileUrl()); - if (file.exists()) { - file.delete(); - } - } - exportProgressManager.removeProgress(taskId); - - Map result = new HashMap<>(); - result.put("message", "任务清理成功"); - return ResponseEntity.ok(result); - } catch (Exception e) { - log.error("清理任务失败, taskId: {}", taskId, e); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); - } - } - - - /** - * 执行导出任务的核心方法 - */ - private void executeExport(String taskId, List list) throws Exception { - String tempDir = System.getProperty("java.io.tmpdir") + File.separator + "export_" + taskId; - String zipFilePath = System.getProperty("java.io.tmpdir") + File.separator + "export_" + taskId + ".zip"; - + public void exportUnsettled(HttpServletResponse response, @RequestBody List list) throws Exception { + // 创建临时文件夹 + String tempDir = System.getProperty("java.io.tmpdir") + File.separator + UUID.randomUUID(); new File(tempDir).mkdirs(); try { - int processed = 0; for(SltAgreementInfo info : list){ if(info.getAgreementId() == null){ continue; @@ -2565,33 +2244,315 @@ public class SltAgreementInfoController extends BaseController { workbook.write(fos); } - processed++; - exportProgressManager.updateProgress(taskId, 1); - - // 每处理完一个文件,短暂休息以避免资源过度占用 - Thread.sleep(100); } - // 创建压缩包 - createZipFile(tempDir, zipFilePath); + String zipFileName = "结算单_" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + ".zip"; + response.setContentType("application/zip"); + response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(zipFileName, "UTF-8")); - // 标记任务完成 - exportProgressManager.completeProgress(taskId, zipFilePath); - - } catch (Exception e) { - exportProgressManager.errorProgress(taskId, "导出过程发生错误: " + e.getMessage()); - throw e; - } finally { - // 清理临时文件夹,但保留zip文件 - try { - FileUtils.deleteDirectory(new File(tempDir)); - } catch (IOException e) { - log.warn("清理临时文件夹失败: {}", e.getMessage()); + try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) { + File[] files = new File(tempDir).listFiles(); + if (files != null) { + for (File file : files) { + zipOut.putNextEntry(new ZipEntry(file.getName())); + try (FileInputStream fis = new FileInputStream(file)) { + byte[] buffer = new byte[1024]; + int len; + while ((len = fis.read(buffer)) > 0) { + zipOut.write(buffer, 0, len); + } + } + zipOut.closeEntry(); + } + } } + } catch (Exception e) { + log.error("一键批量导出未结算报表失败", e); + } finally { + // 删除临时文件 + FileUtils.deleteDirectory(new File(tempDir)); } } + @ApiOperation(value = "下载导出文件") + @PostMapping("/dlExProgress") + public void downloadExportFile(@RequestBody DownloadRequest request,HttpServletResponse response) throws IOException { + String taskId = request.getTaskId() != null ? request.getTaskId() : UUID.randomUUID().toString(); + String zipName = request.getZipName() != null ? request.getZipName() : "未结算批量明细导出_" + LocalDate.now(); + + // 初始化进度 + DownloadProgress progress = new DownloadProgress(); + progress.setTotalFiles(request.getList().size()); + progress.setStatus("processing"); + progress.setStartTime(System.currentTimeMillis()); + downloadProgressMap.put(taskId, progress); + + try { + String encoded = java.net.URLEncoder.encode(zipName + ".zip", "UTF-8"); + response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename*=UTF-8''" + encoded); + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + + // 设置流式传输相关头部 + response.setHeader("Transfer-Encoding", "chunked"); + response.setHeader("X-Content-Type-Options", "nosniff"); + + // 使用低压缩级别提高速度 + try (ZipArchiveOutputStream zipOut = new ZipArchiveOutputStream(response.getOutputStream())) { + zipOut.setLevel(Deflater.BEST_SPEED); + zipOut.setEncoding("UTF-8"); + zipOut.setUseZip64(Zip64Mode.AsNeeded); + + AtomicInteger fileCount = new AtomicInteger(0); + + + for(SltAgreementInfo info : request.getList()){ + if(info.getAgreementId() == null){ + continue; + } + + // 更新进度 + progress.setCurrentFileName("正在生成: " + info.getAgreementCode()); + progress.setCurrentFile(fileCount.incrementAndGet()); + progress.setPercentage((int)((fileCount.get() * 100.0) / progress.getTotalFiles())); + updateProgress(taskId, progress); + + + //根据协议id获取结算单位和结算工程 + SltAgreementInfo agreementInfo = sltAgreementInfoMapper.getUnitAndProjectByAgreementId(info.getAgreementId()); + // 生成文件名 + String fileName = ""; + String unitName = ""; + String projectName = ""; + // 清理非法字符(Windows文件名不允许: \ / : * ? " < > |) + unitName = cleanFileName(agreementInfo.getUnitName()); + projectName = cleanFileName(agreementInfo.getProjectName()); + String agreementCode = cleanFileName(agreementInfo.getAgreementCode()); + + // 构建安全的文件名(限制总长度不超过150字符) + String rawFileName = agreementCode + "-" + unitName + "-" + projectName + "_结算单.xls"; + fileName = rawFileName.length() > 150 ? + rawFileName.substring(0, 150) + ".xls" : rawFileName; + + //租赁费用明细 + BigDecimal totalCostLease = BigDecimal.valueOf(0.00); + List leaseList = new ArrayList<>(); + leaseList = sltAgreementInfoMapper.getLeaseList(info); + + for (SltAgreementInfo bean : leaseList) { + if (null == bean.getLeasePrice()) { + bean.setLeasePrice(BigDecimal.valueOf(0.00)); + } + if (null == bean.getNum()) { + bean.setNum(BigDecimal.valueOf(0L)); + } + if (null == bean.getLeaseDays()) { + bean.setLeaseDay(0L); + } + BigDecimal leasePrice = bean.getLeasePrice(); + BigDecimal num = bean.getNum(); + BigDecimal leaseDays = new BigDecimal(bean.getLeaseDays()); + BigDecimal costs = leasePrice.multiply(num).multiply(leaseDays); + if(costs!=null){ + totalCostLease = totalCostLease.add(costs); + } + bean.setCosts(costs); + } + List lease = Convert.toList(SltLeaseInfo.class, leaseList); + + //丢失费用明细 + BigDecimal totalCostLose = BigDecimal.valueOf(0.00); + List loseList = new ArrayList<>(); + + loseList = sltAgreementInfoMapper.getLoseList(info); + + for (SltAgreementInfo bean : loseList) { + if (null == bean.getBuyPrice()) { + bean.setBuyPrice(BigDecimal.valueOf(0.00)); + } + if (null == bean.getNum()) { + bean.setNum(BigDecimal.valueOf(0L)); + } + BigDecimal buyPrice = bean.getBuyPrice(); + BigDecimal num = bean.getNum(); + // 原价 x 数量 + BigDecimal costs = buyPrice.multiply(num); + if(costs!=null){ + totalCostLose = totalCostLose.add(costs); + } + //计算租赁费用 + bean.setCosts(costs); + } + List lose = Convert.toList(SltLeaseInfo.class, loseList); + + //维修费用明细 + BigDecimal totalCostRepair = BigDecimal.valueOf(0.00); + List repairList = new ArrayList<>(); + + + List taskRepairList = taskMapper.getTaskIdList(info); + List taskRepairList2 = new ArrayList<>(); + taskRepairList2 = checkTeamAgreementInfo(info); + if (null != taskRepairList && !taskRepairList.isEmpty()) { + if (null != taskRepairList2 && !taskRepairList2.isEmpty()) { + taskRepairList.addAll(taskRepairList2); + } + repairList = sltAgreementInfoMapper.getRepairDetailsList(info, taskRepairList); + } + + for (SltAgreementInfo bean : repairList) { + if (bean.getCosts()!=null && (bean.getPartType().equals("收费"))) { + totalCostRepair = totalCostRepair.add(bean.getCosts()); + } + } + List repair = Convert.toList(SltLeaseInfo.class, repairList); + + //报废费用明细 + BigDecimal totalCostScrap = BigDecimal.valueOf(0.00); + List scrapList = new ArrayList<>(); + + List taskScrapList = taskMapper.getTaskIdList(info); + + List taskScrapList2 = new ArrayList<>(); + taskScrapList2 = checkTeamAgreementInfo(info); + + if (null != taskScrapList && !taskScrapList.isEmpty()) { + + if (null != taskScrapList2 && !taskScrapList2.isEmpty()) { + taskScrapList.addAll(taskScrapList2); + } + scrapList = sltAgreementInfoMapper.getScrapDetailsList(info, taskScrapList); + } + + + for (SltAgreementInfo bean : scrapList) { + if (bean.getCosts()!=null && (bean.getPartType().equals("收费"))) { + totalCostScrap = totalCostScrap.add(bean.getCosts()); + } + } + List scrap = Convert.toList(SltLeaseInfo.class, scrapList); + + //减免费用明细 + BigDecimal totalCostReduction = BigDecimal.valueOf(0.00); + List reductionList = new ArrayList<>(); + + if (info.getAgreementId() != null){ + SltAgreementReduce bean =new SltAgreementReduce(); + bean.setAgreementId(info.getAgreementId()); + reductionList = sltAgreementRecudceMapper.getReductionList(bean); + } + + for (SltAgreementReduce reduction : reductionList) { + if(reduction.getLeaseMoney()!=null){ + totalCostReduction = totalCostReduction.add(reduction.getLeaseMoney()); + } + } + List reduction = Convert.toList(SltLeaseInfo.class, reductionList); + + + List> resultsLease = new ArrayList<>(); + List> resultsLose = new ArrayList<>(); + List> resultsRepair = new ArrayList<>(); + List> resultsScrap = new ArrayList<>(); + List> resultsReduction = new ArrayList<>(); + if (lease!= null) { + for (SltLeaseInfo bean : lease) { + Map maps = outReceiveDetailsBeanToMap(bean, 1, 1); + resultsLease.add(maps); + } + } + if (lose!= null) { + for (SltLeaseInfo bean : lose) { + Map maps = outReceiveDetailsBeanToMap(bean, 2, 1); + resultsLose.add(maps); + } + } + if (repair!= null) { + for (SltLeaseInfo bean : repair) { + Map maps = outReceiveDetailsBeanToMap(bean, 3, 1); + resultsRepair.add(maps); + } + } + if (scrap!= null) { + for (SltLeaseInfo bean : scrap) { + Map maps = outReceiveDetailsBeanToMap(bean, 4, 1); + resultsScrap.add(maps); + } + } + if (reduction!= null) { + for (SltLeaseInfo bean : reduction) { + Map maps = outReceiveDetailsBeanToMap(bean, 5, 1); + resultsReduction.add(maps); + } + } + + List headersLease = receiveDetailsHeader(1,1); + List headersLose = receiveDetailsHeader(2,1); + List headersRepair = receiveDetailsHeader(3,1); + List headersScrap = receiveDetailsHeader(4,1); + List headersReduction = receiveDetailsHeader(5,1); + +// fileName = agreementInfo.getAgreementCode() + "-" + unitName + "-" + projectName+ "_结算单.xls" ; + // 导出单个Excel文件 + + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + // 生成Excel工作簿 + HSSFWorkbook workbook = PoiOutPage.excelForcheckAll( + resultsLease, resultsLose, resultsRepair, resultsScrap, resultsReduction, + headersLease, headersLose, headersRepair, headersScrap, headersReduction, + "结算明细", projectName, unitName, + totalCostLease, totalCostLose, totalCostRepair, totalCostScrap, totalCostReduction); + + workbook.write(baos); + workbook.close(); + + // 将Excel字节流直接添加到ZIP + addToZipStream(zipOut, fileName, baos.toByteArray()); + + // 定期刷新缓冲区 + if (fileCount.get() % 5 == 0) { + zipOut.flush(); + response.flushBuffer(); + } + } + + } + + + + + // 更新进度为完成 + progress.setStatus("completed"); + progress.setPercentage(100); + progress.setEndTime(System.currentTimeMillis()); + progress.setCurrentFileName("下载完成"); + updateProgress(taskId, progress); + + zipOut.finish(); + + } catch (IOException e) { + progress.setStatus("error"); + progress.setErrorMessage(e.getMessage()); + updateProgress(taskId, progress); + throw e; + } + + } finally { + // 清理进度信息(延迟清理,以便前端可以获取最终状态) + new Timer().schedule(new TimerTask() { + @Override + public void run() { + downloadProgressMap.remove(taskId); + } + }, 300000); // 5分钟后清理 + } + } + + + + + + /** * 创建压缩包 */ @@ -2617,9 +2578,31 @@ public class SltAgreementInfoController extends BaseController { + private void addToZipStream(ZipArchiveOutputStream zipOut, String path, byte[] bytes) throws IOException { + if (bytes == null || bytes.length == 0) return; + + ZipArchiveEntry entry = new ZipArchiveEntry(path); + entry.setSize(bytes.length); + zipOut.putArchiveEntry(entry); + + try (ByteArrayInputStream in = new ByteArrayInputStream(bytes)) { + IOUtils.copy(in, zipOut); + } + + zipOut.closeArchiveEntry(); + zipOut.flush(); + } + /** + * 更新进度信息 + */ + private void updateProgress(String taskId, DownloadProgress progress) { + if (taskId != null && progress != null) { + downloadProgressMap.put(taskId, progress); + } + }