diff --git a/nflg-mobilebroken-admin/pom.xml b/nflg-mobilebroken-admin/pom.xml index 3b4d44b6..c8932790 100644 --- a/nflg-mobilebroken-admin/pom.xml +++ b/nflg-mobilebroken-admin/pom.xml @@ -120,6 +120,11 @@ tinypinyin 2.0.3 + + org.apache.poi + poi-ooxml + 5.2.3 + diff --git a/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/controller/TicketController.java b/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/controller/TicketController.java index 867d0c40..0f3f1aa6 100644 --- a/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/controller/TicketController.java +++ b/nflg-mobilebroken-admin/src/main/java/com/nflg/mobilebroken/admin/controller/TicketController.java @@ -23,6 +23,12 @@ import com.nflg.mobilebroken.repository.entity.*; import com.nflg.mobilebroken.repository.service.*; import com.nflg.mobilebroken.starter.annotation.MethodInfoMark; import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.RegionUtil; +import org.apache.poi.util.IOUtils; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.springframework.core.io.ClassPathResource; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -39,7 +45,9 @@ import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; +import java.net.URL; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.time.Instant; @@ -842,4 +850,309 @@ public class TicketController extends ControllerBase { List datas = ticketService.exportSearch(request); EecExcelUtil.export("工单报表", "sheet1", Convert.toList(AdminTicketReportVO.class, datas), response); } + + /** + * 导出工单详情为excel + * @param ticketId 工单编号 + */ + @GetMapping("/exportTicketExcel") + @ApiMark(moduleName = "工单管理", apiName = "导出工单详情为excel") + public void exportExcel(HttpServletResponse response,@Valid @RequestParam @NotNull Integer ticketId) throws Exception { + Ticket ticket = ticketService.getById(ticketId); + VUtils.trueThrowBusinessError(Objects.isNull(ticket)).throwMessage("工单不存在"); + Workbook workbook = new XSSFWorkbook(); + Sheet sheet = workbook.createSheet("Sheet1"); + sheet.setDefaultColumnWidth(8); + sheet.setDefaultRowHeightInPoints(20); + CellStyle centerStyle = getCenterStyle(workbook); + CellStyle normalStyle = getNormalStyle(workbook); + ClassPathResource resource = new ClassPathResource("images/logo.png"); + InputStream inputStream = resource.getInputStream(); + float height = 20; + byte[] imageBytes = IOUtils.toByteArray(inputStream); + int pictureIdx = workbook.addPicture(imageBytes, Workbook.PICTURE_TYPE_PNG); + inputStream.close(); + // 创建绘图对象 + Drawing drawing = sheet.createDrawingPatriarch(); + // 创建锚点,用于定位图片位置 + ClientAnchor anchor = workbook.getCreationHelper().createClientAnchor(); + anchor.setCol1(0); // 起始列(从 0 开始) + anchor.setRow1(0); // 起始行(从 0 开始) + anchor.setCol2(3); // 结束列 + anchor.setRow2(1); // 结束行 + // 插入图片 + drawing.createPicture(anchor, pictureIdx); + //标题行 + Row row0 = sheet.createRow(0); + row0.setHeightInPoints(25); + row0.createCell(0).setCellStyle(centerStyle); + addMergedRegion(sheet,0, 0, 0, 2); + addMergedRegion(sheet,0, 0, 3, 11); + createCell(row0, 3, getTitleStyle(workbook), "CSF系统问题解决方案"); + //第一行 + Row row1 = sheet.createRow(1); + row1.setHeightInPoints(height); + createCell(row1, 0, centerStyle, "主题"); + addMergedRegion(sheet,1, 1, 1, 4); + createCell(row1, 1, normalStyle, ticket.getTitle()); + createCell(row1, 5, centerStyle, "事故等级"); + createCell(row1, 6, centerStyle, "一般" + (Objects.equals(ticket.getAccidentLevel(), Byte.valueOf("0")) ? "\u2611" : "\u2610")); + createCell(row1, 7, normalStyle, ""); + createCell(row1, 8, centerStyle, "较严重" + (Objects.equals(ticket.getAccidentLevel(), Byte.valueOf("1")) ? "\u2611" : "\u2610")); + createCell(row1, 9, normalStyle, ""); + createCell(row1, 10, centerStyle, "严重" + (Objects.equals(ticket.getAccidentLevel(), Byte.valueOf("2")) ? "\u2611" : "\u2610")); + createCell(row1, 11, normalStyle, ""); + //第二行 + Row row2 = sheet.createRow(2); + row2.setHeightInPoints(height); + createCell(row2, 0, centerStyle, "工单编号"); + addMergedRegion(sheet,2, 2, 1, 4); + createCell(row2, 1, normalStyle, ticket.getNo()); + createCell(row2, 5, centerStyle, "设备编号"); + addMergedRegion(sheet,2, 2, 6, 11); + createCell(row2, 6, normalStyle, ticket.getDeviceNo()); + //第三行 + Row row3 = sheet.createRow(3); + createCell(row3, 0, centerStyle, "提交人"); + addMergedRegion(sheet,3, 3, 1, 4); + AppUser user = appUserService.getById(ticket.getUserId()); + createCell(row3, 1, normalStyle, user.getName()); + createCell(row3, 5, centerStyle, "反馈日期"); + addMergedRegion(sheet,3, 3, 6, 11); + createCell(row3, 6, normalStyle, DateTimeUtil.format(ticket.getCreateTime())); + //第四行 + Row row4 = sheet.createRow(4); + row4.setHeightInPoints(height); + createCell(row4, 0, centerStyle, "设备地点"); + addMergedRegion(sheet,4, 4, 1, 4); + createCell(row4, 1, normalStyle, ticket.getDeviceAddress()); + createCell(row4, 5, centerStyle, "处理人员"); + addMergedRegion(sheet,4, 4, 6, 11); + createCell(row4, 6, normalStyle, ticket.getHandleName()); + //第五行 + Row row5 = sheet.createRow(5); + row5.setHeightInPoints(height); + createCell(row5, 0, centerStyle, "问题类型"); + addMergedRegion(sheet,5, 5, 1, 4); + createCell(row5, 1, normalStyle, ticket.getQuestion()); + createCell(row5, 5, centerStyle, "问题部位"); + addMergedRegion(sheet,5, 5, 6, 11); + createCell(row5, 6, normalStyle, ticket.getComponent()); + //第六行 + Row row6 = sheet.createRow(6); + row6.setHeightInPoints(height); + createCell(row6, 0, centerStyle, "问题描述"); + addMergedRegion(sheet,6, 6, 1, 11); + createCell(row6, 1, normalStyle, ticket.getDescription()); + //第七行 + Row row7 = sheet.createRow(7); + row7.setHeightInPoints(height); + List images = new ArrayList<>(); + if (StrUtil.isNotBlank(ticket.getAttachments())) { + StrUtil.split(ticket.getAttachments(), ",").forEach(item -> { + if (item.endsWith(".jpg") || item.endsWith(".png") || item.endsWith(".jpeg")) { + images.add(new FileInfo(item.substring(item.lastIndexOf("/") + 1), urlEncode(item))); + } + }); + } + if (StrUtil.isNotBlank(ticket.getImages())) { + StrUtil.split(ticket.getImages(), ",").forEach(item -> { + images.add(new FileInfo(item.substring(item.lastIndexOf("/") + 1), item)); + }); + } + int rowIndex = 6; + int rows = images.size() % 2 == 0 ? images.size() / 2 : images.size() / 2 + 1; + if (rows > 1) { + addMergedRegion(sheet,rowIndex + 1, rowIndex + rows, 0, 0); + } + createCell(row7, 0, centerStyle, "案例照片"); + if (rows == 0) { + addMergedRegion(sheet,rowIndex + 1, rowIndex + 1, 1, 5); + addMergedRegion(sheet,rowIndex + 1, rowIndex + 1, 6, 11); + rowIndex++; + } else { + for (int i = 0; i < images.size(); i++) { + if (i % 2 == 0) { + rowIndex++; + bindPic(images.get(i).getUrl(), workbook, sheet, rowIndex, 1, 6); + } else { + bindPic(images.get(i).getUrl(), workbook, sheet, rowIndex, 6, 12); + } + } + if (images.size() % 2 != 0) { + addMergedRegion(sheet,rowIndex, rowIndex, 6, 11); + } + } + rowIndex++; + Row row8 = sheet.createRow(rowIndex); + row8.setHeightInPoints(height); + addMergedRegion(sheet,rowIndex, rowIndex, 0, 11); + createCell(row8, 0, normalStyle, "根本原因分析"); + rowIndex++; + Row row9 = sheet.createRow(rowIndex); + row9.setHeightInPoints(height); + addMergedRegion(sheet,rowIndex, rowIndex, 0, 11); + createCell(row9, 0, normalStyle, ticket.getReason()); + //解决方案 + List solutions = ticketSolutionService.lambdaQuery().eq(TicketSolution::getTicketId,ticketId).orderByAsc(TicketSolution::getId).list(); + if (CollectionUtil.isNotEmpty(solutions)) { + Map> map=solutions.stream().collect(Collectors.groupingBy(TicketSolution::getDictionaryItemName, LinkedHashMap::new, Collectors.toList())); + for (Map.Entry> item : map.entrySet()) { + rowIndex++; + Row row10 = sheet.createRow(rowIndex); + row10.setHeightInPoints(height); + createCell(row10, 0, centerStyle, "No."); + addMergedRegion(sheet,rowIndex, rowIndex, 1, 6); + createCell(row10, 1, centerStyle, item.getKey()); + createCell(row10, 7, centerStyle, "负责人"); + createCell(row10, 8, centerStyle, "计划日期"); + createCell(row10, 9, centerStyle, "确认日期"); + addMergedRegion(sheet,rowIndex, rowIndex, 10, 11); + createCell(row10, 10, centerStyle, "备注"); + for (int i = 0; i < item.getValue().size(); i++) { + rowIndex++; + TicketSolution solution = item.getValue().get(i); + Row row11 = sheet.createRow(rowIndex); + row11.setHeightInPoints(height); + createCell(row11, 0, centerStyle, String.valueOf(i + 1)); + addMergedRegion(sheet,rowIndex, rowIndex, 1, 6); + createCell(row11, 1, normalStyle, solution.getDescription()); + createCell(row11, 7, centerStyle, solution.getSuperintendent()); + createCell(row11, 8, centerStyle, solution.getScheduleDate()); + createCell(row11, 9, centerStyle, solution.getConfirmedDate()); + addMergedRegion(sheet,rowIndex, rowIndex, 10, 11); + createCell(row11, 10, normalStyle, solution.getRemark()); + } + } + } + //解决方案审核 + List reviewDepartments = ticketSolutionAuditService.getByTicket(ticketId) + .stream() + .filter(vo -> StrUtil.isNotBlank(vo.getUserName())) + .collect(Collectors.toList()); + if (CollectionUtil.isNotEmpty(reviewDepartments)) { + rowIndex++; + Row row12 = sheet.createRow(rowIndex); + row12.setHeightInPoints(height); + addMergedRegion(sheet,rowIndex, rowIndex, 0, 11); + createCell(row12, 0, normalStyle, "相关部门审核确认"); + rowIndex++; + Row row13 = sheet.createRow(rowIndex); + row13.setHeightInPoints(height); + Row row14 = sheet.createRow(rowIndex + 1); + row14.setHeightInPoints(height); + for (int i = 0; i < reviewDepartments.size(); i++) { + SolutionReviewDepartmentVO reviewDepartment = reviewDepartments.get(i); + addMergedRegion(sheet,rowIndex, rowIndex, i * 2, i * 2 + 1); + createCell(row13, i * 2, centerStyle, reviewDepartment.getDeptName() + "(" + reviewDepartment.getUserName() + ")"); + addMergedRegion(sheet,rowIndex + 1, rowIndex + 1, i * 2, i * 2 + 1); + createCell(row14, i * 2, centerStyle, Objects.isNull(reviewDepartment.getState()) ? "" : (Objects.equals(reviewDepartment.getState(), 1) ? "通过" : "不通过")); + } + } + response.setContentType("application/vnd.ms-excel"); + response.setHeader("Content-Disposition", "attachment;filename=ticket.xlsx"); + try (OutputStream outputStream = response.getOutputStream()) { + workbook.write(outputStream); + } finally { + workbook.close(); + } + } + + private void addMergedRegion(Sheet sheet,int firstRow, int lastRow, int firstCol, int lastCol){ + CellRangeAddress region = new CellRangeAddress(firstRow, lastRow, firstCol, lastCol); + sheet.addMergedRegion(region); + RegionUtil.setBorderBottom(BorderStyle.THIN, region, sheet); + RegionUtil.setBorderTop(BorderStyle.THIN, region, sheet); + RegionUtil.setBorderLeft(BorderStyle.THIN, region, sheet); + RegionUtil.setBorderRight(BorderStyle.THIN, region, sheet); + } + + private void createCell(Row row,int index,CellStyle style,String value){ + Cell cell =row.createCell(index); + cell.setCellValue(value); + cell.setCellStyle(style); + } + + private void bindPic(String url, Workbook workbook,Sheet sheet, int rowIndex, int colStart, int colEnd){ + try { + byte[] imageBytes; + try (InputStream inputStream = new URL(url).openStream()) { + imageBytes = IOUtils.toByteArray(inputStream); + } + int pictureIdx = workbook.addPicture(imageBytes, Workbook.PICTURE_TYPE_PNG); + Drawing drawing = sheet.createDrawingPatriarch(); + addMergedRegion(sheet,rowIndex,rowIndex,colStart,colEnd-1); + ClientAnchor anchor = workbook.getCreationHelper().createClientAnchor(); + anchor.setCol1(colStart); + anchor.setRow1(rowIndex); + anchor.setCol2(colEnd); + anchor.setRow2(rowIndex+1); + drawing.createPicture(anchor, pictureIdx); + Row row = sheet.getRow(rowIndex); + if (row == null) { + row = sheet.createRow(rowIndex); + } + row.setHeightInPoints(200); + }catch (Exception ex){ + log.error("图片加载失败",ex); + } + } + + private CellStyle getNormalStyle(Workbook workbook){ + CellStyle centerStyle = workbook.createCellStyle(); + centerStyle.setAlignment(HorizontalAlignment.LEFT); + centerStyle.setVerticalAlignment(VerticalAlignment.CENTER); + Font normalFont = workbook.createFont(); + normalFont.setFontName("DejaVu Sans"); // 设置字体名称 + normalFont.setFontHeightInPoints((short) 8); // 设置字体大小(单位为点) + centerStyle.setFont(normalFont); + centerStyle.setBorderTop(BorderStyle.THIN); // 上边框 + centerStyle.setBorderBottom(BorderStyle.THIN); // 下边框 + centerStyle.setBorderLeft(BorderStyle.THIN); // 左边框 + centerStyle.setBorderRight(BorderStyle.THIN); // 右边框 + centerStyle.setTopBorderColor(IndexedColors.BLACK.getIndex()); // 上边框颜色 + centerStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex()); // 下边框颜色 + centerStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex()); // 左边框颜色 + centerStyle.setRightBorderColor(IndexedColors.BLACK.getIndex()); // 右边框颜色 + return centerStyle; + } + + private CellStyle getCenterStyle(Workbook workbook){ + CellStyle centerStyle = workbook.createCellStyle(); + centerStyle.setAlignment(HorizontalAlignment.CENTER); + centerStyle.setVerticalAlignment(VerticalAlignment.CENTER); + Font normalFont = workbook.createFont(); + normalFont.setFontName("DejaVu Sans"); // 设置字体名称 + normalFont.setFontHeightInPoints((short) 8); // 设置字体大小(单位为点) + centerStyle.setFont(normalFont); + centerStyle.setBorderTop(BorderStyle.THIN); // 上边框 + centerStyle.setBorderBottom(BorderStyle.THIN); // 下边框 + centerStyle.setBorderLeft(BorderStyle.THIN); // 左边框 + centerStyle.setBorderRight(BorderStyle.THIN); // 右边框 + centerStyle.setTopBorderColor(IndexedColors.BLACK.getIndex()); // 上边框颜色 + centerStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex()); // 下边框颜色 + centerStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex()); // 左边框颜色 + centerStyle.setRightBorderColor(IndexedColors.BLACK.getIndex()); // 右边框颜色 + return centerStyle; + } + + private CellStyle getTitleStyle(Workbook workbook){ + CellStyle titleStyle = workbook.createCellStyle(); + titleStyle.setAlignment(HorizontalAlignment.CENTER); + titleStyle.setVerticalAlignment(VerticalAlignment.CENTER); + Font fontTitle = workbook.createFont(); + fontTitle.setFontName("DejaVu Sans"); // 设置字体名称 + fontTitle.setFontHeightInPoints((short) 15); // 设置字体大小(单位为点) + fontTitle.setBold(true); + titleStyle.setFont(fontTitle); + titleStyle.setBorderTop(BorderStyle.THIN); // 上边框 + titleStyle.setBorderBottom(BorderStyle.THIN); // 下边框 + titleStyle.setBorderLeft(BorderStyle.THIN); // 左边框 + titleStyle.setBorderRight(BorderStyle.THIN); // 右边框 + titleStyle.setTopBorderColor(IndexedColors.BLACK.getIndex()); // 上边框颜色 + titleStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex()); // 下边框颜色 + titleStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex()); // 左边框颜色 + titleStyle.setRightBorderColor(IndexedColors.BLACK.getIndex()); // 右边框颜色 + return titleStyle; + } } \ No newline at end of file diff --git a/nflg-mobilebroken-admin/src/main/resources/images/logo.png b/nflg-mobilebroken-admin/src/main/resources/images/logo.png new file mode 100644 index 00000000..34ae65a8 Binary files /dev/null and b/nflg-mobilebroken-admin/src/main/resources/images/logo.png differ