设计导出
This commit is contained in:
@ -1,8 +1,15 @@
|
||||
package org.dromara.design.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.alibaba.excel.ExcelWriter;
|
||||
import com.alibaba.excel.write.metadata.WriteSheet;
|
||||
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
|
||||
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.poi.ss.usermodel.IndexedColors;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
||||
import org.dromara.common.log.annotation.Log;
|
||||
@ -10,17 +17,17 @@ import org.dromara.common.log.enums.BusinessType;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.web.core.BaseController;
|
||||
import org.dromara.design.domain.bo.ImportExcelFileReq;
|
||||
import org.dromara.design.domain.bo.ObtainAllVersionNumbersReq;
|
||||
import org.dromara.design.domain.bo.CoryObtainTheListReq;
|
||||
import org.dromara.design.domain.bo.SheetListReq;
|
||||
import org.dromara.design.domain.bo.*;
|
||||
import org.dromara.design.domain.vo.*;
|
||||
import org.dromara.design.exportUtil.bill.*;
|
||||
import org.dromara.design.service.IBusBillofquantitiesVersionsService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 工程量清单版本
|
||||
@ -103,6 +110,74 @@ public class BusBillofquantitiesVersionsController extends BaseController {
|
||||
return R.ok(busBillofquantitiesVersionsService.obtainAllClassification(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出工程量清单版本列表
|
||||
*/
|
||||
@Log(title = "工程量清单版本", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(String versions,Long projectId, HttpServletResponse response) throws IOException {
|
||||
Map<String, List<BillOfQuantitiesExport>> sheetDataMap = busBillofquantitiesVersionsService.export(versions,projectId);
|
||||
|
||||
if (sheetDataMap.isEmpty()) {
|
||||
response.getWriter().write("无数据可导出");
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 设置响应头(同上)
|
||||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
String fileName = URLEncoder.encode( "工程量清单", "UTF-8").replaceAll("\\+", "%20");
|
||||
response.setHeader("Content-disposition", "attachment;filename*=UTF-8''" + fileName + ".xlsx");
|
||||
|
||||
// 3. 构建Excel写入器
|
||||
ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).build();
|
||||
try {
|
||||
// 4. 遍历每个Sheet分组,创建Sheet并写入数据
|
||||
for (Map.Entry<String, List<BillOfQuantitiesExport>> entry : sheetDataMap.entrySet()) {
|
||||
String sheetName = entry.getKey();
|
||||
List<BillOfQuantitiesExport> dataList = entry.getValue();
|
||||
|
||||
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
|
||||
// 背景设置为红色
|
||||
headWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
|
||||
|
||||
HorizontalCellStyleStrategy horizontalCellStyleStrategy =
|
||||
new HorizontalCellStyleStrategy(headWriteCellStyle, new WriteCellStyle());
|
||||
|
||||
// 定义Sheet(同时注册标题处理器和冻结窗格处理器)
|
||||
WriteSheet writeSheet = EasyExcel.writerSheet(sheetName)
|
||||
.registerWriteHandler(new FreezePaneWriteHandler()) // 冻结前2行
|
||||
.registerWriteHandler(new ColumnWidthWriteHandler()) // 调整列宽
|
||||
.registerWriteHandler(new CustomRowStyleHandler()) // 新增:边框和标题样式
|
||||
.registerWriteHandler(horizontalCellStyleStrategy)
|
||||
.head(head(sheetName))
|
||||
.build();
|
||||
|
||||
// 写入数据
|
||||
excelWriter.write(dataList, writeSheet);
|
||||
}
|
||||
} finally {
|
||||
excelWriter.finish();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private List<List<String>> head(String sheetName) {
|
||||
List<List<String>> list = new ArrayList<List<String>>();
|
||||
List<String> list1 = Arrays.asList(sheetName,"编号");
|
||||
List<String> list2 = Arrays.asList(sheetName, "名称");
|
||||
List<String> list3 = Arrays.asList(sheetName, "规格");
|
||||
List<String> list4 = Arrays.asList(sheetName, "单位");
|
||||
List<String> list5 = Arrays.asList(sheetName, "数量");
|
||||
List<String> list6 = Arrays.asList(sheetName, "备注");
|
||||
list.add(list1);
|
||||
list.add(list2);
|
||||
list.add(list3);
|
||||
list.add(list4);
|
||||
list.add(list5);
|
||||
list.add(list6);
|
||||
return list;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 导入物资设备清单
|
||||
|
||||
@ -7,6 +7,7 @@ import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.*;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import org.dromara.design.domain.bo.DesCollectFileBo;
|
||||
import org.dromara.design.domain.dto.ExportDto;
|
||||
import org.dromara.design.domain.vo.DesCollectFileVo;
|
||||
import org.dromara.design.service.IDesCollectFileService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
@ -117,4 +118,11 @@ public class DesCollectFileController extends BaseController {
|
||||
@NotNull(message = "请先选择项目")Long projectId) {
|
||||
return toAjax(desCollectFileService.addFile(file, catalogueId, projectId));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@PostMapping("/exportZip")
|
||||
public void exportZip(ExportDto dto, HttpServletResponse response) throws Exception {
|
||||
desCollectFileService.exportAsZip(dto, response);
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,6 +22,7 @@ import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -143,4 +144,10 @@ public class DesConstructionSchedulePlanController extends BaseController {
|
||||
@PathVariable Long[] ids) {
|
||||
return toAjax(desConstructionSchedulePlanService.deleteByIds(List.of(ids)));
|
||||
}
|
||||
|
||||
@PostMapping("/exportSchedule")
|
||||
public void exportSchedule(HttpServletResponse response, Long projectId) throws IOException {
|
||||
desConstructionSchedulePlanService.exportSchedule(response,projectId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.*;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import org.dromara.design.domain.dto.ExportDto;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
||||
@ -128,4 +129,9 @@ public class DesPrelimSchemeController extends BaseController {
|
||||
return toAjax(desPrelimSchemeService.updateFile(file, projectId,id));
|
||||
}
|
||||
|
||||
@PostMapping("/exportZipWithStatus")
|
||||
public void exportZipWithStatus(ExportDto dto, HttpServletResponse response) throws Exception {
|
||||
desPrelimSchemeService.exportAsZipWithStatusPrefix(dto, response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.*;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import org.dromara.design.domain.dto.ExportDto;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.dromara.common.idempotent.annotation.RepeatSubmit;
|
||||
@ -126,4 +127,10 @@ public class DesSchemeController extends BaseController {
|
||||
public R<Void> updateFile(MultipartFile file, Long projectId, @NotNull(message = "主键不能为空")@PathVariable Long id) {
|
||||
return toAjax(desSchemeService.updateFile(file, projectId,id));
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/exportZipWithStatus")
|
||||
public void exportZipWithStatus(ExportDto dto, HttpServletResponse response) throws Exception {
|
||||
desSchemeService.exportAsZipWithStatusPrefix(dto, response);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
package org.dromara.design.domain.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ExportDto {
|
||||
private Long projectId;
|
||||
private List<Long> ids;
|
||||
}
|
||||
@ -1,5 +1,7 @@
|
||||
package org.dromara.design.domain.vo;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelIgnore;
|
||||
import com.alibaba.excel.annotation.format.DateTimeFormat;
|
||||
import org.dromara.design.domain.DesSubcontract;
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
@ -31,13 +33,13 @@ public class DesSubcontractVo implements Serializable {
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@ExcelProperty(value = "主键ID")
|
||||
@ExcelIgnore
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 项目id
|
||||
*/
|
||||
@ExcelProperty(value = "项目id")
|
||||
@ExcelIgnore
|
||||
private Long projectId;
|
||||
|
||||
/**
|
||||
@ -49,7 +51,13 @@ public class DesSubcontractVo implements Serializable {
|
||||
/**
|
||||
* 分包要求
|
||||
*/
|
||||
@ExcelProperty(value = "说明")
|
||||
private String requirement;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@DateTimeFormat("yyyy-MM-dd")
|
||||
@ExcelProperty(value = "创建时间")
|
||||
private Date createTime;
|
||||
}
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
package org.dromara.design.exportUtil.bill;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.alibaba.excel.annotation.format.NumberFormat;
|
||||
import com.alibaba.excel.annotation.write.style.HeadStyle;
|
||||
import com.alibaba.excel.enums.poi.FillPatternTypeEnum;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Data
|
||||
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 9)
|
||||
public class BillOfQuantitiesExport {
|
||||
@ExcelProperty(value = "编号", index = 0)
|
||||
private String num;
|
||||
|
||||
@ExcelProperty(value = "名称及规格", index = 1)
|
||||
private String name;
|
||||
|
||||
@ExcelProperty(value = "规格", index = 2)
|
||||
private String specification;
|
||||
|
||||
@ExcelProperty(value = "单位", index = 3)
|
||||
@NumberFormat("#") // 若单位为文本,可去掉此注解
|
||||
private String unit;
|
||||
|
||||
@ExcelProperty(value = "数量", index = 4)
|
||||
@NumberFormat("#,##0.00") // 数量格式化(保留两位小数)
|
||||
private BigDecimal quantity;
|
||||
|
||||
@ExcelProperty(value = "备注", index = 5)
|
||||
private String remark;
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package org.dromara.design.exportUtil.bill;
|
||||
|
||||
import com.alibaba.excel.write.handler.SheetWriteHandler;
|
||||
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
|
||||
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
|
||||
/**
|
||||
* 调整指定列的宽度(第二列和第五列)
|
||||
*/
|
||||
public class ColumnWidthWriteHandler implements SheetWriteHandler {
|
||||
|
||||
@Override
|
||||
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
|
||||
Sheet sheet = writeSheetHolder.getSheet();
|
||||
|
||||
// 设置列宽(单位:字符数,1字符≈256个单位,这里按实际需求调整)
|
||||
// 第二列(索引1):例如设置宽度为30个字符
|
||||
sheet.setColumnWidth(1, 30 * 256);
|
||||
// 第五列(索引4):例如设置宽度为15个字符
|
||||
sheet.setColumnWidth(4, 15 * 256);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
package org.dromara.design.exportUtil.bill;
|
||||
|
||||
import com.alibaba.excel.write.handler.RowWriteHandler;
|
||||
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
|
||||
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
|
||||
import org.apache.poi.ss.usermodel.*;
|
||||
|
||||
/**
|
||||
* 行样式处理器:
|
||||
* 1. 第二行(索引1)及以后所有行添加边框
|
||||
* 2. 第二行(索引1)的第二列(索引1)标题栏加粗加大
|
||||
*/
|
||||
import com.alibaba.excel.write.handler.RowWriteHandler;
|
||||
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
|
||||
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
|
||||
import org.apache.poi.ss.usermodel.*;
|
||||
|
||||
public class CustomRowStyleHandler implements RowWriteHandler {
|
||||
|
||||
@Override
|
||||
public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer relativeRowIndex, Boolean isHead) {
|
||||
// 关键修改:通过 writeSheetHolder 获取 Workbook(兼容所有版本)
|
||||
Workbook workbook = writeSheetHolder.getParentWriteWorkbookHolder().getWorkbook();
|
||||
int rowIndex = row.getRowNum();
|
||||
|
||||
// 1. 第二行(索引1)及以后的行添加边框
|
||||
if (rowIndex >= 1) {
|
||||
CellStyle borderStyle = ExcelStyleUtils.createBorderStyle(workbook);
|
||||
|
||||
for (int i = 0; i <= 5; i++) {
|
||||
Cell cell = row.getCell(i);
|
||||
if (cell == null) {
|
||||
cell = row.createCell(i);
|
||||
}
|
||||
cell.setCellStyle(borderStyle);
|
||||
}
|
||||
|
||||
// 2. 第二行(索引1)的第二列(索引1)标题栏加粗加大
|
||||
if (rowIndex == 1) {
|
||||
for (int i = 0; i <= 5; i++) {
|
||||
Cell secondColumnCell = row.getCell(i);
|
||||
if (secondColumnCell != null) {
|
||||
CellStyle headerStyle = ExcelStyleUtils.createSecondColumnHeaderStyle(workbook);
|
||||
secondColumnCell.setCellStyle(headerStyle);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
package org.dromara.design.exportUtil.bill;
|
||||
|
||||
import org.apache.poi.ss.usermodel.*;
|
||||
|
||||
/**
|
||||
* 样式工具类(定义边框、字体等样式)
|
||||
*/
|
||||
public class ExcelStyleUtils {
|
||||
// 边框样式(细实线)
|
||||
public static final BorderStyle BORDER_STYLE = BorderStyle.THIN;
|
||||
// 边框颜色(黑色)
|
||||
public static final short BORDER_COLOR = IndexedColors.BLACK.getIndex();
|
||||
|
||||
/**
|
||||
* 创建带边框的单元格样式
|
||||
*/
|
||||
public static CellStyle createBorderStyle(Workbook workbook) {
|
||||
CellStyle style = workbook.createCellStyle();
|
||||
// 上下左右边框
|
||||
style.setBorderTop(BORDER_STYLE);
|
||||
style.setBorderBottom(BORDER_STYLE);
|
||||
style.setBorderLeft(BORDER_STYLE);
|
||||
style.setBorderRight(BORDER_STYLE);
|
||||
// 边框颜色
|
||||
style.setTopBorderColor(BORDER_COLOR);
|
||||
style.setBottomBorderColor(BORDER_COLOR);
|
||||
style.setLeftBorderColor(BORDER_COLOR);
|
||||
style.setRightBorderColor(BORDER_COLOR);
|
||||
// 单元格内容居中(可选)
|
||||
style.setAlignment(HorizontalAlignment.CENTER);
|
||||
style.setVerticalAlignment(VerticalAlignment.CENTER);
|
||||
return style;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建第二列标题栏样式(加粗、字号加大)
|
||||
*/
|
||||
public static CellStyle createSecondColumnHeaderStyle(Workbook workbook) {
|
||||
CellStyle style = createBorderStyle(workbook); // 继承边框样式
|
||||
Font font = workbook.createFont();
|
||||
font.setBold(true); // 加粗
|
||||
font.setFontHeightInPoints((short) 12); // 字号加大(默认11,这里设12)
|
||||
style.setFont(font);
|
||||
return style;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建第一行(合并标题行)的白色背景样式
|
||||
*/
|
||||
public static CellStyle createFirstRowWhiteStyle(Workbook workbook) {
|
||||
CellStyle style = workbook.createCellStyle();
|
||||
// 设置白色背景
|
||||
style.setFillForegroundColor(IndexedColors.WHITE.getIndex());
|
||||
style.setFillPattern(FillPatternType.SOLID_FOREGROUND); // 纯色填充
|
||||
return style;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package org.dromara.design.exportUtil.bill;
|
||||
|
||||
import com.alibaba.excel.write.handler.SheetWriteHandler;
|
||||
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
|
||||
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
|
||||
/**
|
||||
* 冻结前3行的处理器(滚动时前3行固定)
|
||||
*/
|
||||
public class FreezePaneWriteHandler implements SheetWriteHandler {
|
||||
|
||||
@Override
|
||||
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
|
||||
Sheet sheet = writeSheetHolder.getSheet();
|
||||
// 参数说明:冻结0列,冻结2行,从第0列、第2行开始滚动
|
||||
// 前2行(索引0和1)将固定在顶部
|
||||
sheet.createFreezePane(0, 2, 0, 2);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
package org.dromara.design.exportUtil.bill;
|
||||
|
||||
import com.alibaba.excel.write.handler.SheetWriteHandler;
|
||||
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
|
||||
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.ss.util.CellRangeAddress;
|
||||
|
||||
public class SheetHeaderWriteHandler implements SheetWriteHandler {
|
||||
|
||||
private final String sheetName;
|
||||
|
||||
public SheetHeaderWriteHandler( String sheetName) {
|
||||
this.sheetName = sheetName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
|
||||
Sheet sheet = writeSheetHolder.getSheet();
|
||||
Workbook workbook = writeWorkbookHolder.getWorkbook();
|
||||
|
||||
// 第一行(索引0):合并标题 + 白色背景
|
||||
Row titleRow = sheet.createRow(0);
|
||||
Cell titleCell = titleRow.createCell(0);
|
||||
titleCell.setCellValue(sheetName);
|
||||
// 应用白色背景样式
|
||||
titleCell.setCellStyle(ExcelStyleUtils.createFirstRowWhiteStyle(workbook));
|
||||
// 合并第一行的0-5列
|
||||
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 5));
|
||||
|
||||
// 第2行(索引1):字段标题栏(手动指定,覆盖实体类默认表头)
|
||||
Row headerRow = sheet.createRow(1);
|
||||
String[] headers = {"编号", "名称", "规格", "单位", "数量", "备注"};
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
headerRow.createCell(i).setCellValue(headers[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package org.dromara.design.exportUtil.plan;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelIgnore;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.alibaba.excel.annotation.format.DateTimeFormat;
|
||||
import com.alibaba.excel.converters.localdate.LocalDateStringConverter;
|
||||
import lombok.Data;
|
||||
import org.dromara.common.excel.annotation.ExcelDictFormat;
|
||||
import org.dromara.common.excel.convert.ExcelDictConvert;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Data
|
||||
public class ConstructionScheduleExport {
|
||||
@ExcelProperty(value = "编号", index = 0)
|
||||
private String levelCode; // 层级编号(如1、1.1、1.1.1)
|
||||
|
||||
@ExcelProperty(value = "节点名称", index = 1)
|
||||
private String nodeName;
|
||||
|
||||
|
||||
@DateTimeFormat("yyyy-MM-dd")
|
||||
@ExcelProperty(value = "预计开始时间", index = 2)
|
||||
private LocalDate planStartDate;
|
||||
|
||||
@DateTimeFormat("yyyy-MM-dd")
|
||||
@ExcelProperty(value = "预计结束时间", index = 3)
|
||||
private LocalDate planEndDate;
|
||||
|
||||
@DateTimeFormat("yyyy-MM-dd")
|
||||
@ExcelProperty(value = "实际开始时间", index = 4)
|
||||
private LocalDate practicalStartDate;
|
||||
|
||||
@DateTimeFormat("yyyy-MM-dd")
|
||||
@ExcelProperty(value = "实际结束时间", index = 5)
|
||||
private LocalDate practicalEndDate;
|
||||
|
||||
@ExcelProperty(value = "状态", index = 6,converter = ExcelDictConvert.class)
|
||||
@ExcelDictFormat(dictType = "project_construction_status")
|
||||
private String status;
|
||||
|
||||
// 用于构建层级关系的临时字段(不导出)
|
||||
@ExcelIgnore
|
||||
private Long id;
|
||||
|
||||
@ExcelIgnore
|
||||
private Long parentId;
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package org.dromara.design.exportUtil.plan;
|
||||
|
||||
import com.alibaba.excel.write.handler.SheetWriteHandler;
|
||||
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
|
||||
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.ss.util.CellRangeAddress;
|
||||
|
||||
public class ScheduleHeaderWriteHandler implements SheetWriteHandler {
|
||||
@Override
|
||||
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
|
||||
Sheet sheet = writeSheetHolder.getSheet();
|
||||
|
||||
// 第一行直接作为标题栏
|
||||
Row titleRow = sheet.createRow(0);
|
||||
String[] titles = {"编号", "节点名称", "预计开始时间", "预计结束时间", "实际开始时间", "实际结束时间", "状态"};
|
||||
for (int i = 0; i < titles.length; i++) {
|
||||
Cell cell = titleRow.createCell(i);
|
||||
cell.setCellValue(titles[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,14 +3,15 @@ package org.dromara.design.service;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.design.domain.BusBillofquantities;
|
||||
import org.dromara.design.domain.BusBillofquantitiesVersions;
|
||||
import org.dromara.design.domain.bo.*;
|
||||
import org.dromara.design.domain.vo.*;
|
||||
import org.dromara.design.exportUtil.bill.BillOfQuantitiesExport;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 工程量清单版本Service接口
|
||||
@ -100,4 +101,7 @@ public interface IBusBillofquantitiesVersionsService extends IService<BusBillofq
|
||||
List<BusBillofquantitiesMaterialTotalVo> queryMaterialTotalListByProject(Long projectId);
|
||||
|
||||
List<BusBillofquantitiesVo> obtainAllClassification(ObtainAllVersionNumbersReq bo);
|
||||
|
||||
|
||||
Map<String, List<BillOfQuantitiesExport>> export(String versions,Long projectId);
|
||||
}
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
package org.dromara.design.service;
|
||||
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.dromara.design.domain.DesCollectFile;
|
||||
import org.dromara.design.domain.bo.DesCollectFileBo;
|
||||
import org.dromara.design.domain.dto.ExportDto;
|
||||
import org.dromara.design.domain.vo.DesCollectFileVo;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@ -79,5 +81,6 @@ public interface IDesCollectFileService extends IService<DesCollectFile>{
|
||||
Boolean addFile(MultipartFile file, Long catalogueId, Long projectId);
|
||||
|
||||
|
||||
void exportAsZip(ExportDto dto, HttpServletResponse response) throws Exception;
|
||||
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ import org.dromara.design.domain.dto.constructionscheduleplan.*;
|
||||
import org.dromara.design.domain.vo.DesConstructionSchedulePlanVo;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@ -114,4 +115,7 @@ public interface IDesConstructionSchedulePlanService extends IService<DesConstru
|
||||
* @return 实体列表
|
||||
*/
|
||||
List<DesConstructionSchedulePlan> convertToEntities(List<DesConstructionSchedulePlanExcelDto> excelList);
|
||||
|
||||
|
||||
void exportSchedule(HttpServletResponse response, Long projectId) throws IOException;
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package org.dromara.design.service;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.dromara.design.domain.dto.ExportDto;
|
||||
import org.dromara.design.domain.vo.DesPrelimSchemeVo;
|
||||
import org.dromara.design.domain.bo.DesPrelimSchemeBo;
|
||||
import org.dromara.design.domain.DesPrelimScheme;
|
||||
@ -8,7 +9,6 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.Collection;
|
||||
@ -81,4 +81,7 @@ public interface IDesPrelimSchemeService extends IService<DesPrelimScheme>{
|
||||
* 修改文件
|
||||
*/
|
||||
Boolean updateFile(MultipartFile file, Long projectId, Long id);
|
||||
|
||||
|
||||
void exportAsZipWithStatusPrefix(ExportDto dto, HttpServletResponse response) throws Exception;
|
||||
}
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
package org.dromara.design.service;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import org.dromara.common.core.domain.R;
|
||||
import org.dromara.design.domain.dto.ExportDto;
|
||||
import org.dromara.design.domain.vo.DesSchemeVo;
|
||||
import org.dromara.design.domain.bo.DesSchemeBo;
|
||||
import org.dromara.design.domain.DesScheme;
|
||||
@ -82,4 +84,7 @@ public interface IDesSchemeService extends IService<DesScheme>{
|
||||
* 修改文件
|
||||
*/
|
||||
Boolean updateFile(MultipartFile file, Long projectId, Long id);
|
||||
|
||||
|
||||
void exportAsZipWithStatusPrefix(ExportDto dto, HttpServletResponse response) throws Exception;
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ package org.dromara.design.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.alibaba.fastjson2.util.BeanUtils;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
@ -28,12 +28,11 @@ import org.dromara.common.excel.coryUtils.ExcelReader;
|
||||
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||
import org.dromara.common.utils.BatchNumberGenerator;
|
||||
import org.dromara.common.utils.excel.ExcelDynamicReader;
|
||||
import org.dromara.design.domain.BusBillofquantities;
|
||||
import org.dromara.design.domain.BusBillofquantitiesVersions;
|
||||
import org.dromara.design.domain.bo.*;
|
||||
import org.dromara.design.domain.dto.MaterialsAndEquipmentExcelDto;
|
||||
import org.dromara.design.domain.vo.*;
|
||||
import org.dromara.design.exportUtil.bill.BillOfQuantitiesExport;
|
||||
import org.dromara.design.mapper.BusBillofquantitiesVersionsMapper;
|
||||
import org.dromara.design.service.IBusBillofquantitiesService;
|
||||
import org.dromara.design.service.IBusBillofquantitiesVersionsService;
|
||||
@ -363,6 +362,34 @@ public class BusBillofquantitiesVersionsServiceImpl extends ServiceImpl<BusBillo
|
||||
return BeanUtil.copyToList(busBillofquantities,BusBillofquantitiesVo.class);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Map<String, List<BillOfQuantitiesExport>> export(String versions,Long projectId) {
|
||||
// 1. 从数据库查询指定版本的所有数据
|
||||
List<BusBillofquantities> dbList = busBillofquantitiesService.list(Wrappers.<BusBillofquantities>lambdaQuery()
|
||||
.eq(BusBillofquantities::getVersions, versions)
|
||||
.eq(BusBillofquantities::getProjectId, projectId)
|
||||
);
|
||||
if (CollectionUtil.isEmpty(dbList)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
// 2. 转换为导出实体并按sheet分组
|
||||
Map<String, List<BillOfQuantitiesExport>> sheetMap = new HashMap<>();
|
||||
for (BusBillofquantities dbItem : dbList) {
|
||||
BillOfQuantitiesExport exportItem = new BillOfQuantitiesExport();
|
||||
BeanUtil.copyProperties(dbItem, exportItem);
|
||||
// 处理编号、名称等字段(若需格式化可在此补充)
|
||||
exportItem.setNum(dbItem.getNum());
|
||||
exportItem.setName(dbItem.getName());
|
||||
|
||||
// 按sheet分组
|
||||
String sheetName = dbItem.getSheet();
|
||||
sheetMap.computeIfAbsent(sheetName, k -> new ArrayList<>()).add(exportItem);
|
||||
}
|
||||
return sheetMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归构建树形结构
|
||||
*
|
||||
|
||||
@ -2,8 +2,11 @@ package org.dromara.design.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.dromara.common.core.domain.event.ProcessDeleteEvent;
|
||||
import org.dromara.common.core.domain.event.ProcessEvent;
|
||||
import org.dromara.common.core.domain.event.ProcessTaskEvent;
|
||||
@ -18,10 +21,13 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.design.domain.DesCollect;
|
||||
import org.dromara.design.domain.DesCollectCatalogue;
|
||||
import org.dromara.design.domain.DesCollectFile;
|
||||
import org.dromara.design.domain.bo.DesCollectFileBo;
|
||||
import org.dromara.design.domain.dto.ExportDto;
|
||||
import org.dromara.design.domain.vo.DesCollectFileVo;
|
||||
import org.dromara.design.mapper.DesCollectFileMapper;
|
||||
import org.dromara.design.service.IDesCollectCatalogueService;
|
||||
import org.dromara.design.service.IDesCollectFileService;
|
||||
import org.dromara.system.domain.vo.SysOssUploadVo;
|
||||
import org.dromara.system.domain.vo.SysOssVo;
|
||||
@ -31,10 +37,22 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Collection;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import static org.dromara.common.constant.MinioPathConstant.ContactNoticeTemplate;
|
||||
|
||||
@ -53,6 +71,8 @@ public class DesCollectFileServiceImpl extends ServiceImpl<DesCollectFileMapper,
|
||||
|
||||
private final ISysOssService ossService;
|
||||
|
||||
private final IDesCollectCatalogueService collectCatalogueService;
|
||||
|
||||
/**
|
||||
* 查询收资文件
|
||||
*
|
||||
@ -217,4 +237,144 @@ public class DesCollectFileServiceImpl extends ServiceImpl<DesCollectFileMapper,
|
||||
log.info("监听删除流程事件,上传资料审核任务执行了{}", processDeleteEvent.toString());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void exportAsZip(ExportDto dto, HttpServletResponse response) throws Exception {
|
||||
// 1. 查询所有收资文件
|
||||
List<DesCollectFile> files = baseMapper.selectList(Wrappers.<DesCollectFile>lambdaQuery()
|
||||
.eq(DesCollectFile::getProjectId, dto.getProjectId())
|
||||
.in(CollectionUtil.isNotEmpty(dto.getIds()),DesCollectFile::getId, dto.getIds())
|
||||
);
|
||||
if (files.isEmpty()) {
|
||||
throw new RuntimeException("没有可导出的收资文件");
|
||||
}
|
||||
|
||||
// 2. 提取所有catalogueId,查询对应的目录名称
|
||||
List<Long> catalogueIds = files.stream()
|
||||
.map(DesCollectFile::getCatalogueId)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
Map<Long, String> catalogueNameMap = getCatalogueNames(catalogueIds); // 获取名称映射
|
||||
|
||||
// 3. 按catalogueId分组
|
||||
Map<Long, List<DesCollectFile>> catalogueGroup = files.stream()
|
||||
.collect(Collectors.groupingBy(DesCollectFile::getCatalogueId));
|
||||
|
||||
// 4. 创建临时根目录
|
||||
File tempRootDir = File.createTempFile("collect_file_", "_temp");
|
||||
if (!tempRootDir.delete() || !tempRootDir.mkdirs()) {
|
||||
throw new RuntimeException("创建临时根目录失败");
|
||||
}
|
||||
|
||||
try {
|
||||
// 5. 按分组创建文件夹(用目录名称)并下载文件
|
||||
for (Map.Entry<Long, List<DesCollectFile>> entry : catalogueGroup.entrySet()) {
|
||||
Long catalogueId = entry.getKey();
|
||||
List<DesCollectFile> fileList = entry.getValue();
|
||||
|
||||
// 获取目录名称(若查询不到,用catalogueId作为默认名称)
|
||||
String catalogueName = catalogueNameMap.getOrDefault(catalogueId, String.valueOf(catalogueId));
|
||||
// 处理名称中的特殊字符(避免创建文件夹失败)
|
||||
String safeCatalogueName = catalogueName.replaceAll("[\\\\/:*?\"<>|]", "_");
|
||||
|
||||
// 创建以目录名称命名的文件夹
|
||||
File catalogueDir = new File(tempRootDir, safeCatalogueName);
|
||||
if (!catalogueDir.exists() && !catalogueDir.mkdirs()) {
|
||||
throw new RuntimeException("创建文件夹[" + safeCatalogueName + "]失败");
|
||||
}
|
||||
|
||||
// 下载文件(命名格式:审核状态-原文件名)
|
||||
for (DesCollectFile file : fileList) {
|
||||
String newFileName = BusinessStatusEnum.getByStatus(file.getStatus()).getDesc() + "-" + file.getFileName();
|
||||
File destFile = new File(catalogueDir, newFileName);
|
||||
downloadFile(file.getFileUrl(), destFile); // 复用之前的下载方法
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 压缩并导出(逻辑不变)
|
||||
response.setContentType("application/zip");
|
||||
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||
String zipFileName = URLEncoder.encode("收资文件汇总", StandardCharsets.UTF_8.name()) + ".zip";
|
||||
response.setHeader("Content-Disposition", "attachment;filename*=UTF-8''" + zipFileName);
|
||||
|
||||
try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
|
||||
zipDirectory(tempRootDir, tempRootDir.getName(), zos);
|
||||
}
|
||||
|
||||
} finally {
|
||||
FileUtils.deleteQuietly(tempRootDir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询catalogueId对应的目录名称(从数据库获取)
|
||||
* @param catalogueIds 多个catalogueId
|
||||
* @return key: catalogueId, value: 目录名称
|
||||
*/
|
||||
public Map<Long, String> getCatalogueNames(List<Long> catalogueIds) {
|
||||
if (catalogueIds.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
// 实际实现:从目录表查询,例如
|
||||
List<DesCollectCatalogue> desCollectCatalogues = collectCatalogueService.listByIds(catalogueIds);
|
||||
return desCollectCatalogues.stream()
|
||||
.collect(Collectors.toMap(DesCollectCatalogue::getId, DesCollectCatalogue::getCatalogueName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件(Java 21兼容,使用HttpClient替代URL)
|
||||
* @param fileUrl 文件URL(远程地址或本地路径)
|
||||
* @param destFile 目标文件
|
||||
*/
|
||||
private void downloadFile(String fileUrl, File destFile) throws Exception {
|
||||
|
||||
|
||||
// 远程URL文件,使用HttpClient下载
|
||||
HttpClient client = HttpClient.newBuilder()
|
||||
.connectTimeout(Duration.ofMinutes(5))
|
||||
.build();
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(fileUrl))
|
||||
.timeout(Duration.ofMinutes(10))
|
||||
.GET()
|
||||
.build();
|
||||
HttpResponse<Path> response = client.send(
|
||||
request,
|
||||
HttpResponse.BodyHandlers.ofFile(destFile.toPath())
|
||||
);
|
||||
if (response.statusCode() != 200) {
|
||||
throw new RuntimeException("文件下载失败:" + fileUrl + ",状态码:" + response.statusCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归压缩目录到ZIP流
|
||||
* @param sourceDir 源目录
|
||||
* @param baseName ZIP内的基础路径(避免包含系统临时目录路径)
|
||||
* @param zos ZIP输出流
|
||||
*/
|
||||
private void zipDirectory(File sourceDir, String baseName, ZipOutputStream zos) throws IOException {
|
||||
File[] files = sourceDir.listFiles();
|
||||
if (files == null) return;
|
||||
|
||||
for (File file : files) {
|
||||
String entryName = baseName + File.separator + file.getName();
|
||||
if (file.isDirectory()) {
|
||||
// 递归压缩子目录
|
||||
zipDirectory(file, entryName, zos);
|
||||
} else {
|
||||
// 压缩文件
|
||||
zos.putNextEntry(new ZipEntry(entryName));
|
||||
try (InputStream in = new FileInputStream(file)) {
|
||||
byte[] buffer = new byte[1024];
|
||||
int len;
|
||||
while ((len = in.read(buffer)) != -1) {
|
||||
zos.write(buffer, 0, len);
|
||||
}
|
||||
}
|
||||
zos.closeEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
package org.dromara.design.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
@ -14,15 +17,19 @@ import org.dromara.common.core.constant.HttpStatus;
|
||||
import org.dromara.common.core.exception.ServiceException;
|
||||
import org.dromara.common.core.utils.ObjectUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.excel.utils.ExcelUtil;
|
||||
import org.dromara.design.domain.DesConstructionSchedulePlan;
|
||||
import org.dromara.design.domain.bo.DesUserBo;
|
||||
import org.dromara.design.domain.dto.constructionscheduleplan.*;
|
||||
import org.dromara.design.domain.vo.DesConstructionSchedulePlanVo;
|
||||
import org.dromara.design.exportUtil.plan.ConstructionScheduleExport;
|
||||
import org.dromara.design.exportUtil.plan.ScheduleHeaderWriteHandler;
|
||||
import org.dromara.design.mapper.DesConstructionSchedulePlanMapper;
|
||||
import org.dromara.design.service.IDesConstructionSchedulePlanService;
|
||||
|
||||
import org.dromara.project.service.IBusProjectService;
|
||||
import org.dromara.system.domain.vo.SysDictDataVo;
|
||||
import org.dromara.system.domain.vo.SysUserExportVo;
|
||||
import org.dromara.system.service.ISysDictDataService;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -36,6 +43,7 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 施工进度计划Service业务层处理
|
||||
@ -624,4 +632,97 @@ public class DesConstructionSchedulePlanServiceImpl extends ServiceImpl<DesConst
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void exportSchedule(HttpServletResponse response, Long projectId) throws IOException {
|
||||
// 1. 构建导出数据(含层级编号)
|
||||
List<ConstructionScheduleExport> dataList = buildExportData(projectId);
|
||||
// 2. 设置响应头
|
||||
ExcelUtil.exportExcel(dataList, "用户数据", ConstructionScheduleExport.class, response);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public List<ConstructionScheduleExport> buildExportData(Long projectId) {
|
||||
// 1. 查询项目下所有里程碑节点
|
||||
List<DesConstructionSchedulePlan> allNodes = baseMapper.selectList(Wrappers.<DesConstructionSchedulePlan>lambdaQuery()
|
||||
.eq(DesConstructionSchedulePlan::getProjectId, projectId));
|
||||
if (CollectionUtil.isEmpty(allNodes)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 2. 转换为导出模型并缓存ID映射
|
||||
Map<Long, ConstructionScheduleExport> nodeMap = new HashMap<>();
|
||||
List<ConstructionScheduleExport> exportList = new ArrayList<>();
|
||||
for (DesConstructionSchedulePlan node : allNodes) {
|
||||
ConstructionScheduleExport export = new ConstructionScheduleExport();
|
||||
BeanUtils.copyProperties(node, export);
|
||||
nodeMap.put(node.getId(), export);
|
||||
exportList.add(export);
|
||||
}
|
||||
|
||||
// 3. 构建树形结构并生成层级编号
|
||||
List<ConstructionScheduleExport> rootNodes = new ArrayList<>();
|
||||
for (ConstructionScheduleExport export : exportList) {
|
||||
if (export.getParentId() == 0) { // 父ID为0的是根节点
|
||||
rootNodes.add(export);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 递归生成层级编号(根节点从1开始)
|
||||
int rootIndex = 1;
|
||||
for (ConstructionScheduleExport root : rootNodes) {
|
||||
root.setLevelCode(String.valueOf(rootIndex));
|
||||
buildChildrenCode(root, nodeMap);
|
||||
rootIndex++;
|
||||
}
|
||||
|
||||
// 5. 按层级顺序排序(确保1.1在1之后,1.1.1在1.1之后)
|
||||
return sortByLevelCode(exportList);
|
||||
}
|
||||
|
||||
// 递归为子节点生成编号(如父节点1 -> 子节点1.1、1.2)
|
||||
private void buildChildrenCode(ConstructionScheduleExport parent, Map<Long, ConstructionScheduleExport> nodeMap) {
|
||||
List<ConstructionScheduleExport> children = nodeMap.values().stream()
|
||||
.filter(node -> parent.getId().equals(node.getParentId()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
int childIndex = 1;
|
||||
for (ConstructionScheduleExport child : children) {
|
||||
child.setLevelCode(parent.getLevelCode() + "." + childIndex);
|
||||
buildChildrenCode(child, nodeMap); // 递归处理孙子节点
|
||||
childIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
// 按层级编号排序(字符串自然排序)
|
||||
private List<ConstructionScheduleExport> sortByLevelCode(List<ConstructionScheduleExport> list) {
|
||||
return list.stream()
|
||||
.sorted(Comparator.comparing(ConstructionScheduleExport::getLevelCode))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
package org.dromara.design.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.dromara.common.core.domain.event.ProcessDeleteEvent;
|
||||
import org.dromara.common.core.domain.event.ProcessEvent;
|
||||
import org.dromara.common.core.domain.event.ProcessTaskEvent;
|
||||
@ -15,13 +18,9 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.dromara.design.domain.DesScheme;
|
||||
import org.dromara.message.domain.MsgConfig;
|
||||
import org.dromara.message.domain.bo.MsgNoticeBo;
|
||||
import org.dromara.message.domain.dto.SendMsgDto;
|
||||
import org.dromara.design.domain.dto.ExportDto;
|
||||
import org.dromara.message.service.IMsgConfigService;
|
||||
import org.dromara.message.service.IMsgNoticeService;
|
||||
import org.dromara.system.domain.vo.SysOssUploadVo;
|
||||
import org.dromara.system.domain.vo.SysOssVo;
|
||||
import org.dromara.system.service.ISysOssService;
|
||||
import org.springframework.context.event.EventListener;
|
||||
@ -33,8 +32,18 @@ import org.dromara.design.mapper.DesPrelimSchemeMapper;
|
||||
import org.dromara.design.service.IDesPrelimSchemeService;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import static org.dromara.common.constant.MinioPathConstant.ContactNoticeTemplate;
|
||||
|
||||
@ -189,6 +198,105 @@ public class DesPrelimSchemeServiceImpl extends ServiceImpl<DesPrelimSchemeMappe
|
||||
return baseMapper.updateById(desPrelimScheme)>0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件名添加审核状态前缀后压缩为ZIP导出
|
||||
* @param response 响应对象
|
||||
*/
|
||||
@Override
|
||||
public void exportAsZipWithStatusPrefix(ExportDto dto, HttpServletResponse response) throws Exception {
|
||||
// 1. 查询数据
|
||||
List<DesPrelimScheme> schemes = baseMapper.selectList(Wrappers.<DesPrelimScheme>lambdaQuery()
|
||||
.eq(DesPrelimScheme::getProjectId, dto.getProjectId())
|
||||
.in(CollectionUtil.isNotEmpty(dto.getIds()), DesPrelimScheme::getId, dto.getIds())
|
||||
);
|
||||
if (schemes.isEmpty()) {
|
||||
throw new RuntimeException("没有可导出的初步设计方案文件");
|
||||
}
|
||||
|
||||
// 2. 创建临时目录(存放重命名后的文件)
|
||||
File tempDir = File.createTempFile("prelim_scheme_", "_temp");
|
||||
if (!tempDir.delete()) { // 删除临时文件,创建同名目录
|
||||
throw new RuntimeException("创建临时目录失败");
|
||||
}
|
||||
if (!tempDir.mkdirs()) {
|
||||
throw new RuntimeException("创建临时目录失败");
|
||||
}
|
||||
|
||||
try {
|
||||
// 3. 下载文件并以“审核状态-原文件名”命名
|
||||
for (DesPrelimScheme scheme : schemes) {
|
||||
String status = BusinessStatusEnum.getByStatus(scheme.getStatus()).getDesc(); // 审核状态(如draft、approved)
|
||||
String originalFileName = scheme.getFileName(); // 原文件名
|
||||
String newFileName = status + "-" + originalFileName; // 新文件名:状态-原文件名
|
||||
|
||||
// 临时文件路径
|
||||
File tempFile = new File(tempDir, newFileName);
|
||||
|
||||
// 下载文件到临时目录(根据实际存储类型实现,此处以URL为例)
|
||||
downloadFile(scheme.getFileUrl(), tempFile);
|
||||
}
|
||||
|
||||
// 4. 压缩临时目录中的所有文件为ZIP并写入响应
|
||||
response.setContentType("application/zip");
|
||||
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||
String zipFileName = URLEncoder.encode("初步设计方案", StandardCharsets.UTF_8.name()) + ".zip";
|
||||
response.setHeader("Content-Disposition", "attachment;filename*=UTF-8''" + zipFileName);
|
||||
|
||||
// 压缩文件到响应流
|
||||
try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
|
||||
File[] tempFiles = tempDir.listFiles();
|
||||
if (tempFiles != null) {
|
||||
for (File file : tempFiles) {
|
||||
// ZIP条目名称为新文件名(不含临时目录路径)
|
||||
zos.putNextEntry(new ZipEntry(file.getName()));
|
||||
try (InputStream in = new FileInputStream(file)) {
|
||||
byte[] buffer = new byte[1024];
|
||||
int len;
|
||||
while ((len = in.read(buffer)) != -1) {
|
||||
zos.write(buffer, 0, len);
|
||||
}
|
||||
}
|
||||
zos.closeEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
// 5. 清理临时目录
|
||||
FileUtils.deleteQuietly(tempDir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件到本地临时目录(根据实际存储类型调整,如OSS、本地文件系统)
|
||||
*/
|
||||
private void downloadFile(String fileUrl, File destFile) throws Exception {
|
||||
// 示例:从URL下载(若为OSS,使用OSS SDK的getObject方法)
|
||||
// 创建HTTP客户端(设置超时时间)
|
||||
HttpClient client = HttpClient.newBuilder()
|
||||
.connectTimeout(Duration.ofMinutes(5)) // 连接超时
|
||||
.build();
|
||||
|
||||
// 创建HTTP请求
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(fileUrl)) // 转换为URI(比URL更推荐)
|
||||
.timeout(Duration.ofMinutes(10)) // 响应超时
|
||||
.GET()
|
||||
.build();
|
||||
|
||||
// 发送请求并将响应体写入目标文件
|
||||
HttpResponse<Path> response = client.send(
|
||||
request,
|
||||
HttpResponse.BodyHandlers.ofFile(destFile.toPath()) // 直接写入文件路径
|
||||
);
|
||||
|
||||
// 检查响应状态(200表示成功)
|
||||
if (response.statusCode() != 200) {
|
||||
throw new RuntimeException("文件下载失败,URL: " + fileUrl + ",状态码: " + response.statusCode());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成,单任务完成等)
|
||||
* 正常使用只需#processEvent.flowCode=='leave1'
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
package org.dromara.design.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.dromara.common.core.domain.event.ProcessDeleteEvent;
|
||||
import org.dromara.common.core.domain.event.ProcessEvent;
|
||||
import org.dromara.common.core.domain.event.ProcessTaskEvent;
|
||||
@ -19,6 +22,7 @@ import org.dromara.common.oss.core.OssClient;
|
||||
import org.dromara.common.oss.factory.OssFactory;
|
||||
import org.dromara.design.domain.DesCollectFile;
|
||||
import org.dromara.design.domain.DesPrelimScheme;
|
||||
import org.dromara.design.domain.dto.ExportDto;
|
||||
import org.dromara.system.domain.vo.SysOssUploadVo;
|
||||
import org.dromara.system.domain.vo.SysOssVo;
|
||||
import org.dromara.system.service.ISysOssService;
|
||||
@ -31,10 +35,23 @@ import org.dromara.design.mapper.DesSchemeMapper;
|
||||
import org.dromara.design.service.IDesSchemeService;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Collection;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import static org.dromara.common.constant.MinioPathConstant.ContactNoticeTemplate;
|
||||
|
||||
@ -183,6 +200,101 @@ public class DesSchemeServiceImpl extends ServiceImpl<DesSchemeMapper, DesScheme
|
||||
return baseMapper.updateById(desScheme)>0;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void exportAsZipWithStatusPrefix(ExportDto dto, HttpServletResponse response) throws Exception {
|
||||
// 1. 查询数据
|
||||
List<DesScheme> schemes = baseMapper.selectList(Wrappers.<DesScheme>lambdaQuery()
|
||||
.eq(DesScheme::getProjectId, dto.getProjectId())
|
||||
.in(CollectionUtil.isNotEmpty(dto.getIds()), DesScheme::getId, dto.getIds())
|
||||
);
|
||||
if (schemes.isEmpty()) {
|
||||
throw new RuntimeException("没有可导出的初步设计方案文件");
|
||||
}
|
||||
|
||||
// 2. 创建临时目录(存放重命名后的文件)
|
||||
File tempDir = File.createTempFile("prelim_scheme_", "_temp");
|
||||
if (!tempDir.delete()) { // 删除临时文件,创建同名目录
|
||||
throw new RuntimeException("创建临时目录失败");
|
||||
}
|
||||
if (!tempDir.mkdirs()) {
|
||||
throw new RuntimeException("创建临时目录失败");
|
||||
}
|
||||
|
||||
try {
|
||||
// 3. 下载文件并以“审核状态-原文件名”命名
|
||||
for (DesScheme scheme : schemes) {
|
||||
String status = BusinessStatusEnum.getByStatus(scheme.getStatus()).getDesc(); // 审核状态(如draft、approved)
|
||||
String originalFileName = scheme.getFileName(); // 原文件名
|
||||
String newFileName = status + "-" + originalFileName; // 新文件名:状态-原文件名
|
||||
|
||||
// 临时文件路径
|
||||
File tempFile = new File(tempDir, newFileName);
|
||||
|
||||
// 下载文件到临时目录(根据实际存储类型实现,此处以URL为例)
|
||||
downloadFile(scheme.getFileUrl(), tempFile);
|
||||
}
|
||||
|
||||
// 4. 压缩临时目录中的所有文件为ZIP并写入响应
|
||||
response.setContentType("application/zip");
|
||||
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||
String zipFileName = URLEncoder.encode("初步设计方案", StandardCharsets.UTF_8.name()) + ".zip";
|
||||
response.setHeader("Content-Disposition", "attachment;filename*=UTF-8''" + zipFileName);
|
||||
|
||||
// 压缩文件到响应流
|
||||
try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
|
||||
File[] tempFiles = tempDir.listFiles();
|
||||
if (tempFiles != null) {
|
||||
for (File file : tempFiles) {
|
||||
// ZIP条目名称为新文件名(不含临时目录路径)
|
||||
zos.putNextEntry(new ZipEntry(file.getName()));
|
||||
try (InputStream in = new FileInputStream(file)) {
|
||||
byte[] buffer = new byte[1024];
|
||||
int len;
|
||||
while ((len = in.read(buffer)) != -1) {
|
||||
zos.write(buffer, 0, len);
|
||||
}
|
||||
}
|
||||
zos.closeEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
// 5. 清理临时目录
|
||||
FileUtils.deleteQuietly(tempDir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件到本地临时目录(根据实际存储类型调整,如OSS、本地文件系统)
|
||||
*/
|
||||
private void downloadFile(String fileUrl, File destFile) throws Exception {
|
||||
// 示例:从URL下载(若为OSS,使用OSS SDK的getObject方法)
|
||||
// 创建HTTP客户端(设置超时时间)
|
||||
HttpClient client = HttpClient.newBuilder()
|
||||
.connectTimeout(Duration.ofMinutes(5)) // 连接超时
|
||||
.build();
|
||||
|
||||
// 创建HTTP请求
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(fileUrl)) // 转换为URI(比URL更推荐)
|
||||
.timeout(Duration.ofMinutes(10)) // 响应超时
|
||||
.GET()
|
||||
.build();
|
||||
|
||||
// 发送请求并将响应体写入目标文件
|
||||
HttpResponse<Path> response = client.send(
|
||||
request,
|
||||
HttpResponse.BodyHandlers.ofFile(destFile.toPath()) // 直接写入文件路径
|
||||
);
|
||||
|
||||
// 检查响应状态(200表示成功)
|
||||
if (response.statusCode() != 200) {
|
||||
throw new RuntimeException("文件下载失败,URL: " + fileUrl + ",状态码: " + response.statusCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 总体流程监听(例如: 草稿,撤销,退回,作废,终止,已完成,单任务完成等)
|
||||
* 正常使用只需#processEvent.flowCode=='leave1'
|
||||
|
||||
Reference in New Issue
Block a user