|
|
|
@ -26,12 +26,27 @@ import com.xujie.sys.modules.longchuang.service.ProductionPlanService; |
|
|
|
import com.xujie.sys.modules.oss.entity.SysOssEntity; |
|
|
|
import com.xujie.sys.modules.oss.service.SysOssService; |
|
|
|
import com.xujie.sys.modules.sys.entity.SysUserEntity; |
|
|
|
import jakarta.servlet.ServletOutputStream; |
|
|
|
import jakarta.servlet.http.HttpServletResponse; |
|
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
|
import org.bytedeco.ffmpeg.global.avcodec; |
|
|
|
import org.bytedeco.ffmpeg.global.avutil; |
|
|
|
import org.bytedeco.javacv.FFmpegFrameGrabber; |
|
|
|
import org.bytedeco.javacv.FFmpegFrameRecorder; |
|
|
|
import org.bytedeco.javacv.Frame; |
|
|
|
import org.apache.poi.ss.usermodel.Cell; |
|
|
|
import org.apache.poi.ss.usermodel.CellStyle; |
|
|
|
import org.apache.poi.ss.usermodel.BorderStyle; |
|
|
|
import org.apache.poi.ss.usermodel.FillPatternType; |
|
|
|
import org.apache.poi.ss.usermodel.Font; |
|
|
|
import org.apache.poi.ss.usermodel.HorizontalAlignment; |
|
|
|
import org.apache.poi.ss.usermodel.IndexedColors; |
|
|
|
import org.apache.poi.ss.usermodel.Row; |
|
|
|
import org.apache.poi.ss.usermodel.Sheet; |
|
|
|
import org.apache.poi.ss.usermodel.VerticalAlignment; |
|
|
|
import org.apache.poi.ss.usermodel.Workbook; |
|
|
|
import org.apache.poi.ss.util.CellRangeAddress; |
|
|
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook; |
|
|
|
import org.apache.shiro.SecurityUtils; |
|
|
|
import org.springframework.beans.factory.annotation.Autowired; |
|
|
|
import org.springframework.beans.factory.annotation.Value; |
|
|
|
@ -41,7 +56,10 @@ import org.springframework.util.StringUtils; |
|
|
|
import org.springframework.web.multipart.MultipartFile; |
|
|
|
|
|
|
|
import java.io.File; |
|
|
|
import java.io.IOException; |
|
|
|
import java.math.BigDecimal; |
|
|
|
import java.net.URLEncoder; |
|
|
|
import java.nio.charset.StandardCharsets; |
|
|
|
import java.time.LocalDate; |
|
|
|
import java.time.LocalDateTime; |
|
|
|
import java.time.format.DateTimeFormatter; |
|
|
|
@ -56,6 +74,7 @@ import java.util.Map; |
|
|
|
import java.util.Set; |
|
|
|
import java.util.UUID; |
|
|
|
import java.util.concurrent.ThreadLocalRandom; |
|
|
|
import java.util.function.BiFunction; |
|
|
|
import java.util.stream.Collectors; |
|
|
|
|
|
|
|
@Slf4j |
|
|
|
@ -110,6 +129,11 @@ public class ProductionPlanServiceImpl implements ProductionPlanService { |
|
|
|
return queryOrderPage(data, ORDER_TYPE_HOME_LIFT); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void exportHomeLiftOrder(ProductionPlanOrderQueryData data, HttpServletResponse response) { |
|
|
|
exportOrderExcel(data, ORDER_TYPE_HOME_LIFT, "家用梯销售订单", "家用梯销售订单", response); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
@Transactional(rollbackFor = Exception.class) |
|
|
|
public void saveHomeLiftOrder(ProductionPlanOrderSaveData data) { |
|
|
|
@ -139,6 +163,11 @@ public class ProductionPlanServiceImpl implements ProductionPlanService { |
|
|
|
return queryOrderPage(data, ORDER_TYPE_CABLE_COP); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void exportCableCopTask(ProductionPlanOrderQueryData data, HttpServletResponse response) { |
|
|
|
exportOrderExcel(data, ORDER_TYPE_CABLE_COP, "线缆COP任务单", "线缆COP任务单", response); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
@Transactional(rollbackFor = Exception.class) |
|
|
|
public void saveCableCopTask(ProductionPlanOrderSaveData data) { |
|
|
|
@ -168,6 +197,11 @@ public class ProductionPlanServiceImpl implements ProductionPlanService { |
|
|
|
return queryOrderPage(data, ORDER_TYPE_RENOVATION); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void exportRenovationOrder(ProductionPlanOrderQueryData data, HttpServletResponse response) { |
|
|
|
exportOrderExcel(data, ORDER_TYPE_RENOVATION, "改造项目订单", "改造项目订单", response); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
@Transactional(rollbackFor = Exception.class) |
|
|
|
public void saveRenovationOrder(ProductionPlanOrderSaveData data) { |
|
|
|
@ -197,6 +231,11 @@ public class ProductionPlanServiceImpl implements ProductionPlanService { |
|
|
|
return queryOrderPage(data, ORDER_TYPE_MACHINING); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void exportMachiningTask(ProductionPlanOrderQueryData data, HttpServletResponse response) { |
|
|
|
exportOrderExcel(data, ORDER_TYPE_MACHINING, "机加工任务单", "机加工任务单", response); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
@Transactional(rollbackFor = Exception.class) |
|
|
|
public void saveMachiningTask(ProductionPlanOrderSaveData data) { |
|
|
|
@ -578,6 +617,230 @@ public class ProductionPlanServiceImpl implements ProductionPlanService { |
|
|
|
return new PageUtils(page); |
|
|
|
} |
|
|
|
|
|
|
|
private void exportOrderExcel(ProductionPlanOrderQueryData data, |
|
|
|
String orderType, |
|
|
|
String fileNamePrefix, |
|
|
|
String sheetName, |
|
|
|
HttpServletResponse response) { |
|
|
|
List<ProductionPlanOrderRowData> orderRows = queryOrderListForExport(data, orderType); |
|
|
|
List<OrderExportColumn> baseColumns = buildExportBaseColumns(orderType); |
|
|
|
List<String> headerList = new ArrayList<>(); |
|
|
|
for (OrderExportColumn baseColumn : baseColumns) { |
|
|
|
headerList.add(baseColumn.getHeader()); |
|
|
|
} |
|
|
|
headerList.add("节点名称"); |
|
|
|
headerList.add("报工状态"); |
|
|
|
headerList.add("报工时间"); |
|
|
|
headerList.add("负责人"); |
|
|
|
headerList.add("报工备注"); |
|
|
|
|
|
|
|
try (XSSFWorkbook workbook = new XSSFWorkbook()) { |
|
|
|
Sheet sheet = workbook.createSheet(sheetName); |
|
|
|
CellStyle headerStyle = createExportHeaderStyle(workbook); |
|
|
|
CellStyle contentStyle = createExportContentStyle(workbook); |
|
|
|
|
|
|
|
Row headerRow = sheet.createRow(0); |
|
|
|
for (int i = 0; i < headerList.size(); i++) { |
|
|
|
createExportCell(headerRow, i, headerList.get(i), headerStyle); |
|
|
|
sheet.setColumnWidth(i, resolveExportColumnWidth(headerList.get(i)) * 256); |
|
|
|
} |
|
|
|
sheet.createFreezePane(0, 1); |
|
|
|
|
|
|
|
int rowIndex = 1; |
|
|
|
int orderIndex = 1; |
|
|
|
for (ProductionPlanOrderRowData order : orderRows) { |
|
|
|
List<ProductionPlanOrderNodeData> nodeList = normalizeExportNodeList(order == null ? null : order.getNodeList()); |
|
|
|
int startRowIndex = rowIndex; |
|
|
|
for (ProductionPlanOrderNodeData node : nodeList) { |
|
|
|
Row dataRow = sheet.createRow(rowIndex++); |
|
|
|
int colIndex = 0; |
|
|
|
for (OrderExportColumn baseColumn : baseColumns) { |
|
|
|
createExportCell(dataRow, colIndex++, baseColumn.resolve(orderIndex, order), contentStyle); |
|
|
|
} |
|
|
|
createExportCell(dataRow, colIndex++, safeExportText(node == null ? null : node.getNodeName()), contentStyle); |
|
|
|
createExportCell(dataRow, colIndex++, safeExportText(node == null ? null : node.getStatus()), contentStyle); |
|
|
|
createExportCell(dataRow, colIndex++, safeExportText(node == null ? null : node.getLastReportTime()), contentStyle); |
|
|
|
createExportCell(dataRow, colIndex++, safeExportText(node == null ? null : node.getAssigneeUserName()), contentStyle); |
|
|
|
createExportCell(dataRow, colIndex, safeExportText(node == null ? null : node.getLastReportRemark()), contentStyle); |
|
|
|
} |
|
|
|
int endRowIndex = rowIndex - 1; |
|
|
|
if (endRowIndex > startRowIndex) { |
|
|
|
for (int colIndex = 0; colIndex < baseColumns.size(); colIndex++) { |
|
|
|
sheet.addMergedRegion(new CellRangeAddress(startRowIndex, endRowIndex, colIndex, colIndex)); |
|
|
|
} |
|
|
|
} |
|
|
|
orderIndex++; |
|
|
|
} |
|
|
|
|
|
|
|
writeExportWorkbook(response, workbook, fileNamePrefix); |
|
|
|
} catch (IOException e) { |
|
|
|
log.error("生产计划导出失败,orderType={}, error={}", orderType, e.getMessage(), e); |
|
|
|
throw new XJException("导出失败,请稍后重试"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private List<ProductionPlanOrderRowData> queryOrderListForExport(ProductionPlanOrderQueryData data, String orderType) { |
|
|
|
ProductionPlanOrderQueryData query = data == null ? new ProductionPlanOrderQueryData() : data; |
|
|
|
query.setOrderType(orderType); |
|
|
|
List<ProductionPlanOrderRowData> rows = productionPlanMapper.queryOrderList(query); |
|
|
|
List<ProductionPlanOrderRowData> result = rows == null ? new ArrayList<>() : new ArrayList<>(rows); |
|
|
|
attachNodeList(result); |
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
private List<OrderExportColumn> buildExportBaseColumns(String orderType) { |
|
|
|
List<OrderExportColumn> columns = new ArrayList<>(); |
|
|
|
if (ORDER_TYPE_HOME_LIFT.equals(orderType) || ORDER_TYPE_RENOVATION.equals(orderType)) { |
|
|
|
columns.add(new OrderExportColumn("项目号", (index, row) -> safeExportText(row == null ? null : row.getProjectNo()))); |
|
|
|
columns.add(new OrderExportColumn("型号", (index, row) -> safeExportText(row == null ? null : row.getModelNo()))); |
|
|
|
columns.add(new OrderExportColumn("颜色", (index, row) -> safeExportText(row == null ? null : row.getColor()))); |
|
|
|
columns.add(new OrderExportColumn("层数", (index, row) -> safeExportText(row == null ? null : row.getFloorCount()))); |
|
|
|
columns.add(new OrderExportColumn("特殊要求", (index, row) -> safeExportText(row == null ? null : row.getSpecialRequirement()))); |
|
|
|
columns.add(new OrderExportColumn("计划发货日期", (index, row) -> safeExportText(row == null ? null : row.getPlanDeliveryDate()))); |
|
|
|
columns.add(new OrderExportColumn("当前节点", (index, row) -> safeExportText(row == null ? null : row.getCurrentNode()))); |
|
|
|
columns.add(new OrderExportColumn("节点进度", (index, row) -> formatExportNodeProgress(row))); |
|
|
|
columns.add(new OrderExportColumn("状态", (index, row) -> safeExportText(row == null ? null : row.getStatus()))); |
|
|
|
columns.add(new OrderExportColumn("完工日期", (index, row) -> safeExportText(row == null ? null : row.getFinishDate()))); |
|
|
|
return columns; |
|
|
|
} |
|
|
|
if (ORDER_TYPE_CABLE_COP.equals(orderType)) { |
|
|
|
columns.add(new OrderExportColumn("项目号", (index, row) -> safeExportText(row == null ? null : row.getProjectNo()))); |
|
|
|
columns.add(new OrderExportColumn("任务类型", (index, row) -> safeExportText(row == null ? null : row.getTaskType()))); |
|
|
|
columns.add(new OrderExportColumn("类别", (index, row) -> safeExportText(row == null ? null : row.getSourceProjectNo()))); |
|
|
|
columns.add(new OrderExportColumn("物料名称", (index, row) -> safeExportText(row == null ? null : row.getMaterialName()))); |
|
|
|
columns.add(new OrderExportColumn("规格", (index, row) -> safeExportText(row == null ? null : row.getMaterialSpec()))); |
|
|
|
columns.add(new OrderExportColumn("累计报工数量", (index, row) -> formatExportDecimal(row == null ? null : row.getReportQty()))); |
|
|
|
columns.add(new OrderExportColumn("计划完成日期", (index, row) -> safeExportText(row == null ? null : row.getPlanFinishDate()))); |
|
|
|
columns.add(new OrderExportColumn("当前节点", (index, row) -> safeExportText(row == null ? null : row.getCurrentNode()))); |
|
|
|
columns.add(new OrderExportColumn("节点进度", (index, row) -> formatExportNodeProgress(row))); |
|
|
|
columns.add(new OrderExportColumn("状态", (index, row) -> safeExportText(row == null ? null : row.getStatus()))); |
|
|
|
columns.add(new OrderExportColumn("完工日期", (index, row) -> safeExportText(row == null ? null : row.getFinishDate()))); |
|
|
|
return columns; |
|
|
|
} |
|
|
|
columns.add(new OrderExportColumn("物料号", (index, row) -> safeExportText(row == null ? null : row.getModelNo()))); |
|
|
|
columns.add(new OrderExportColumn("物料名称", (index, row) -> safeExportText(row == null ? null : row.getMaterialName()))); |
|
|
|
columns.add(new OrderExportColumn("规格", (index, row) -> safeExportText(row == null ? null : row.getMaterialSpec()))); |
|
|
|
columns.add(new OrderExportColumn("计划数量", (index, row) -> formatExportDecimal(row == null ? null : row.getTaskQty()))); |
|
|
|
columns.add(new OrderExportColumn("累计报工数量", (index, row) -> formatExportDecimal(row == null ? null : row.getReportQty()))); |
|
|
|
columns.add(new OrderExportColumn("计划完工日期", (index, row) -> safeExportText(row == null ? null : row.getPlanFinishDate()))); |
|
|
|
columns.add(new OrderExportColumn("当前节点", (index, row) -> safeExportText(row == null ? null : row.getCurrentNode()))); |
|
|
|
columns.add(new OrderExportColumn("节点进度", (index, row) -> formatExportNodeProgress(row))); |
|
|
|
columns.add(new OrderExportColumn("状态", (index, row) -> safeExportText(row == null ? null : row.getStatus()))); |
|
|
|
columns.add(new OrderExportColumn("完工日期", (index, row) -> safeExportText(row == null ? null : row.getFinishDate()))); |
|
|
|
return columns; |
|
|
|
} |
|
|
|
|
|
|
|
private List<ProductionPlanOrderNodeData> normalizeExportNodeList(List<ProductionPlanOrderNodeData> nodeList) { |
|
|
|
if (nodeList == null || nodeList.isEmpty()) { |
|
|
|
ProductionPlanOrderNodeData emptyNode = new ProductionPlanOrderNodeData(); |
|
|
|
emptyNode.setNodeName("-"); |
|
|
|
emptyNode.setStatus("-"); |
|
|
|
emptyNode.setLastReportTime(""); |
|
|
|
emptyNode.setAssigneeUserName(""); |
|
|
|
emptyNode.setLastReportRemark(""); |
|
|
|
return Collections.singletonList(emptyNode); |
|
|
|
} |
|
|
|
return nodeList; |
|
|
|
} |
|
|
|
|
|
|
|
private String formatExportNodeProgress(ProductionPlanOrderRowData row) { |
|
|
|
if (row == null) { |
|
|
|
return "0/0"; |
|
|
|
} |
|
|
|
Integer doneCount = row.getNodeDoneCount(); |
|
|
|
Integer totalCount = row.getNodeTotalCount(); |
|
|
|
int done = doneCount == null ? 0 : doneCount; |
|
|
|
int total = totalCount == null ? 0 : totalCount; |
|
|
|
if (total <= 0 && row.getNodeList() != null) { |
|
|
|
total = row.getNodeList().size(); |
|
|
|
done = (int) row.getNodeList().stream().filter(item -> STATUS_DONE.equals(item.getStatus())).count(); |
|
|
|
} |
|
|
|
return done + "/" + total; |
|
|
|
} |
|
|
|
|
|
|
|
private String formatExportDecimal(BigDecimal value) { |
|
|
|
if (value == null) { |
|
|
|
return ""; |
|
|
|
} |
|
|
|
BigDecimal normalized = value.stripTrailingZeros(); |
|
|
|
if (normalized.scale() < 0) { |
|
|
|
normalized = normalized.setScale(0); |
|
|
|
} |
|
|
|
return normalized.toPlainString(); |
|
|
|
} |
|
|
|
|
|
|
|
private String safeExportText(Object value) { |
|
|
|
return value == null ? "" : String.valueOf(value); |
|
|
|
} |
|
|
|
|
|
|
|
private CellStyle createExportHeaderStyle(Workbook workbook) { |
|
|
|
CellStyle style = workbook.createCellStyle(); |
|
|
|
applyExportBorder(style); |
|
|
|
style.setAlignment(HorizontalAlignment.CENTER); |
|
|
|
style.setVerticalAlignment(VerticalAlignment.CENTER); |
|
|
|
style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); |
|
|
|
style.setFillPattern(FillPatternType.SOLID_FOREGROUND); |
|
|
|
Font font = workbook.createFont(); |
|
|
|
font.setBold(true); |
|
|
|
style.setFont(font); |
|
|
|
return style; |
|
|
|
} |
|
|
|
|
|
|
|
private CellStyle createExportContentStyle(Workbook workbook) { |
|
|
|
CellStyle style = workbook.createCellStyle(); |
|
|
|
applyExportBorder(style); |
|
|
|
style.setAlignment(HorizontalAlignment.CENTER); |
|
|
|
style.setVerticalAlignment(VerticalAlignment.CENTER); |
|
|
|
style.setWrapText(true); |
|
|
|
return style; |
|
|
|
} |
|
|
|
|
|
|
|
private void applyExportBorder(CellStyle style) { |
|
|
|
style.setBorderTop(BorderStyle.THIN); |
|
|
|
style.setBorderBottom(BorderStyle.THIN); |
|
|
|
style.setBorderLeft(BorderStyle.THIN); |
|
|
|
style.setBorderRight(BorderStyle.THIN); |
|
|
|
} |
|
|
|
|
|
|
|
private void createExportCell(Row row, int colIndex, String value, CellStyle style) { |
|
|
|
Cell cell = row.createCell(colIndex); |
|
|
|
cell.setCellStyle(style); |
|
|
|
cell.setCellValue(value); |
|
|
|
} |
|
|
|
|
|
|
|
private int resolveExportColumnWidth(String header) { |
|
|
|
if (!StringUtils.hasText(header)) { |
|
|
|
return 14; |
|
|
|
} |
|
|
|
if (header.contains("备注") || header.contains("特殊要求")) { |
|
|
|
return 30; |
|
|
|
} |
|
|
|
if (header.contains("物料名称") || header.contains("规格") || header.contains("负责人")) { |
|
|
|
return 20; |
|
|
|
} |
|
|
|
if (header.contains("节点名称")) { |
|
|
|
return 18; |
|
|
|
} |
|
|
|
if (header.contains("日期") || header.contains("时间")) { |
|
|
|
return 18; |
|
|
|
} |
|
|
|
return 14; |
|
|
|
} |
|
|
|
|
|
|
|
private void writeExportWorkbook(HttpServletResponse response, XSSFWorkbook workbook, String fileNamePrefix) throws IOException { |
|
|
|
String fileName = fileNamePrefix + "_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + ".xlsx"; |
|
|
|
String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString()).replaceAll("\\+", "%20"); |
|
|
|
response.reset(); |
|
|
|
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); |
|
|
|
response.setCharacterEncoding("utf-8"); |
|
|
|
response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + encodedFileName); |
|
|
|
response.setHeader("Access-Control-Expose-Headers", "Content-Disposition"); |
|
|
|
try (ServletOutputStream outputStream = response.getOutputStream()) { |
|
|
|
workbook.write(outputStream); |
|
|
|
outputStream.flush(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private void attachNodeList(List<ProductionPlanOrderRowData> rows) { |
|
|
|
if (rows == null || rows.isEmpty()) { |
|
|
|
return; |
|
|
|
@ -1630,4 +1893,25 @@ public class ProductionPlanServiceImpl implements ProductionPlanService { |
|
|
|
} |
|
|
|
return NODE_REPORT_MODE_PARALLEL; |
|
|
|
} |
|
|
|
|
|
|
|
private static class OrderExportColumn { |
|
|
|
private final String header; |
|
|
|
private final BiFunction<Integer, ProductionPlanOrderRowData, String> valueResolver; |
|
|
|
|
|
|
|
private OrderExportColumn(String header, BiFunction<Integer, ProductionPlanOrderRowData, String> valueResolver) { |
|
|
|
this.header = header; |
|
|
|
this.valueResolver = valueResolver; |
|
|
|
} |
|
|
|
|
|
|
|
private String getHeader() { |
|
|
|
return header; |
|
|
|
} |
|
|
|
|
|
|
|
private String resolve(int rowIndex, ProductionPlanOrderRowData row) { |
|
|
|
if (valueResolver == null) { |
|
|
|
return ""; |
|
|
|
} |
|
|
|
return valueResolver.apply(rowIndex, row); |
|
|
|
} |
|
|
|
} |
|
|
|
} |