From f9a5c61d903b8ae7e227c26559079e99faa63ea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9B=B9=E9=B9=8F=E9=A3=9E?= Date: Tue, 1 Apr 2025 08:47:13 +0800 Subject: [PATCH] =?UTF-8?q?refactor(ticket):=20=E4=BC=98=E5=8C=96=E5=B7=A5?= =?UTF-8?q?=E5=8D=95=E5=AE=A1=E6=A0=B8=E7=8A=B6=E6=80=81=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nflg-mobilebroken-admin/pom.xml | 5 + .../admin/controller/TicketController.java | 313 ++++++++++++++++++ .../src/main/resources/images/logo.png | Bin 0 -> 5988 bytes 3 files changed, 318 insertions(+) create mode 100644 nflg-mobilebroken-admin/src/main/resources/images/logo.png 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 0000000000000000000000000000000000000000..34ae65a8b916ed28c96e1b38dd81b33cddeb0273 GIT binary patch literal 5988 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