未结算报表zip导出
This commit is contained in:
parent
e298c589cf
commit
88d1c12a64
|
|
@ -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<ItemInfo> items;
|
||||
|
||||
private List<SltAgreementInfo> list;
|
||||
|
||||
private String taskId;
|
||||
|
||||
@Data
|
||||
|
|
|
|||
|
|
@ -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<String, DownloadProgress> 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<SltAgreementInfo> 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<SltAgreementInfo> 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<SltLeaseInfo> lease = Convert.toList(SltLeaseInfo.class, leaseList);
|
||||
//
|
||||
// //丢失费用明细
|
||||
// BigDecimal totalCostLose = BigDecimal.valueOf(0.00);
|
||||
// List<SltAgreementInfo> 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<SltLeaseInfo> lose = Convert.toList(SltLeaseInfo.class, loseList);
|
||||
//
|
||||
// //维修费用明细
|
||||
// BigDecimal totalCostRepair = BigDecimal.valueOf(0.00);
|
||||
// List<SltAgreementInfo> repairList = new ArrayList<>();
|
||||
//
|
||||
//
|
||||
// List<TmTask> taskRepairList = taskMapper.getTaskIdList(info);
|
||||
// List<TmTask> 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<SltLeaseInfo> repair = Convert.toList(SltLeaseInfo.class, repairList);
|
||||
//
|
||||
// //报废费用明细
|
||||
// BigDecimal totalCostScrap = BigDecimal.valueOf(0.00);
|
||||
// List<SltAgreementInfo> scrapList = new ArrayList<>();
|
||||
//
|
||||
// List<TmTask> taskScrapList = taskMapper.getTaskIdList(info);
|
||||
//
|
||||
// List<TmTask> 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<SltLeaseInfo> scrap = Convert.toList(SltLeaseInfo.class, scrapList);
|
||||
//
|
||||
// //减免费用明细
|
||||
// BigDecimal totalCostReduction = BigDecimal.valueOf(0.00);
|
||||
// List<SltAgreementReduce> 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<SltLeaseInfo> reduction = Convert.toList(SltLeaseInfo.class, reductionList);
|
||||
//
|
||||
//
|
||||
// List<Map<String, Object>> resultsLease = new ArrayList<>();
|
||||
// List<Map<String, Object>> resultsLose = new ArrayList<>();
|
||||
// List<Map<String, Object>> resultsRepair = new ArrayList<>();
|
||||
// List<Map<String, Object>> resultsScrap = new ArrayList<>();
|
||||
// List<Map<String, Object>> resultsReduction = new ArrayList<>();
|
||||
// if (lease!= null) {
|
||||
// for (SltLeaseInfo bean : lease) {
|
||||
// Map<String, Object> maps = outReceiveDetailsBeanToMap(bean, 1, 1);
|
||||
// resultsLease.add(maps);
|
||||
// }
|
||||
// }
|
||||
// if (lose!= null) {
|
||||
// for (SltLeaseInfo bean : lose) {
|
||||
// Map<String, Object> maps = outReceiveDetailsBeanToMap(bean, 2, 1);
|
||||
// resultsLose.add(maps);
|
||||
// }
|
||||
// }
|
||||
// if (repair!= null) {
|
||||
// for (SltLeaseInfo bean : repair) {
|
||||
// Map<String, Object> maps = outReceiveDetailsBeanToMap(bean, 3, 1);
|
||||
// resultsRepair.add(maps);
|
||||
// }
|
||||
// }
|
||||
// if (scrap!= null) {
|
||||
// for (SltLeaseInfo bean : scrap) {
|
||||
// Map<String, Object> maps = outReceiveDetailsBeanToMap(bean, 4, 1);
|
||||
// resultsScrap.add(maps);
|
||||
// }
|
||||
// }
|
||||
// if (reduction!= null) {
|
||||
// for (SltLeaseInfo bean : reduction) {
|
||||
// Map<String, Object> maps = outReceiveDetailsBeanToMap(bean, 5, 1);
|
||||
// resultsReduction.add(maps);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// List<String> headersLease = receiveDetailsHeader(1,1);
|
||||
// List<String> headersLose = receiveDetailsHeader(2,1);
|
||||
// List<String> headersRepair = receiveDetailsHeader(3,1);
|
||||
// List<String> headersScrap = receiveDetailsHeader(4,1);
|
||||
// List<String> 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<Map<String, String>> exportUnsettled(HttpServletResponse response, @RequestBody List<SltAgreementInfo> 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<String, String> 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<ExportProgressManager.ExportProgress> 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<Map<String, String>> 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<String, String> 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<SltAgreementInfo> 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<SltAgreementInfo> 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<SltAgreementInfo> 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<SltLeaseInfo> lease = Convert.toList(SltLeaseInfo.class, leaseList);
|
||||
|
||||
//丢失费用明细
|
||||
BigDecimal totalCostLose = BigDecimal.valueOf(0.00);
|
||||
List<SltAgreementInfo> 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<SltLeaseInfo> lose = Convert.toList(SltLeaseInfo.class, loseList);
|
||||
|
||||
//维修费用明细
|
||||
BigDecimal totalCostRepair = BigDecimal.valueOf(0.00);
|
||||
List<SltAgreementInfo> repairList = new ArrayList<>();
|
||||
|
||||
|
||||
List<TmTask> taskRepairList = taskMapper.getTaskIdList(info);
|
||||
List<TmTask> 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<SltLeaseInfo> repair = Convert.toList(SltLeaseInfo.class, repairList);
|
||||
|
||||
//报废费用明细
|
||||
BigDecimal totalCostScrap = BigDecimal.valueOf(0.00);
|
||||
List<SltAgreementInfo> scrapList = new ArrayList<>();
|
||||
|
||||
List<TmTask> taskScrapList = taskMapper.getTaskIdList(info);
|
||||
|
||||
List<TmTask> 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<SltLeaseInfo> scrap = Convert.toList(SltLeaseInfo.class, scrapList);
|
||||
|
||||
//减免费用明细
|
||||
BigDecimal totalCostReduction = BigDecimal.valueOf(0.00);
|
||||
List<SltAgreementReduce> 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<SltLeaseInfo> reduction = Convert.toList(SltLeaseInfo.class, reductionList);
|
||||
|
||||
|
||||
List<Map<String, Object>> resultsLease = new ArrayList<>();
|
||||
List<Map<String, Object>> resultsLose = new ArrayList<>();
|
||||
List<Map<String, Object>> resultsRepair = new ArrayList<>();
|
||||
List<Map<String, Object>> resultsScrap = new ArrayList<>();
|
||||
List<Map<String, Object>> resultsReduction = new ArrayList<>();
|
||||
if (lease!= null) {
|
||||
for (SltLeaseInfo bean : lease) {
|
||||
Map<String, Object> maps = outReceiveDetailsBeanToMap(bean, 1, 1);
|
||||
resultsLease.add(maps);
|
||||
}
|
||||
}
|
||||
if (lose!= null) {
|
||||
for (SltLeaseInfo bean : lose) {
|
||||
Map<String, Object> maps = outReceiveDetailsBeanToMap(bean, 2, 1);
|
||||
resultsLose.add(maps);
|
||||
}
|
||||
}
|
||||
if (repair!= null) {
|
||||
for (SltLeaseInfo bean : repair) {
|
||||
Map<String, Object> maps = outReceiveDetailsBeanToMap(bean, 3, 1);
|
||||
resultsRepair.add(maps);
|
||||
}
|
||||
}
|
||||
if (scrap!= null) {
|
||||
for (SltLeaseInfo bean : scrap) {
|
||||
Map<String, Object> maps = outReceiveDetailsBeanToMap(bean, 4, 1);
|
||||
resultsScrap.add(maps);
|
||||
}
|
||||
}
|
||||
if (reduction!= null) {
|
||||
for (SltLeaseInfo bean : reduction) {
|
||||
Map<String, Object> maps = outReceiveDetailsBeanToMap(bean, 5, 1);
|
||||
resultsReduction.add(maps);
|
||||
}
|
||||
}
|
||||
|
||||
List<String> headersLease = receiveDetailsHeader(1,1);
|
||||
List<String> headersLose = receiveDetailsHeader(2,1);
|
||||
List<String> headersRepair = receiveDetailsHeader(3,1);
|
||||
List<String> headersScrap = receiveDetailsHeader(4,1);
|
||||
List<String> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue