diff --git a/src/main/java/com/ycwl/basic/service/impl/VideoReviewServiceImpl.java b/src/main/java/com/ycwl/basic/service/impl/VideoReviewServiceImpl.java index dcd79c43..de5a6c10 100644 --- a/src/main/java/com/ycwl/basic/service/impl/VideoReviewServiceImpl.java +++ b/src/main/java/com/ycwl/basic/service/impl/VideoReviewServiceImpl.java @@ -10,6 +10,7 @@ import com.ycwl.basic.exception.BizException; import com.ycwl.basic.mapper.VideoMapper; import com.ycwl.basic.mapper.VideoReviewMapper; import com.ycwl.basic.model.pc.video.entity.VideoEntity; +import com.ycwl.basic.repository.DeviceRepository; import com.ycwl.basic.model.pc.videoreview.dto.VideoReviewAddReqDTO; import com.ycwl.basic.model.pc.videoreview.dto.VideoReviewListReqDTO; import com.ycwl.basic.model.pc.videoreview.dto.VideoReviewRespDTO; @@ -45,6 +46,9 @@ public class VideoReviewServiceImpl implements VideoReviewService { @Autowired private VideoMapper videoMapper; + @Autowired + private DeviceRepository deviceRepository; + private final ObjectMapper objectMapper = new ObjectMapper(); @Override @@ -153,11 +157,37 @@ public class VideoReviewServiceImpl implements VideoReviewService { reqDTO.setPageSize(Integer.MAX_VALUE); List list = videoReviewMapper.selectReviewList(reqDTO); - // 2. 创建Excel工作簿 + // 2. 收集所有机位ID并批量查询机位名称 + Set allDeviceIds = new LinkedHashSet<>(); + for (VideoReviewRespDTO review : list) { + Map> cameraRating = review.getCameraPositionRating(); + if (cameraRating != null && !cameraRating.isEmpty()) { + // 收集机位ID (按顺序) + for (String deviceIdStr : cameraRating.keySet()) { + try { + allDeviceIds.add(Long.valueOf(deviceIdStr)); + } catch (NumberFormatException e) { + log.warn("无效的机位ID: {}", deviceIdStr); + } + } + } + } + + // 批量查询机位名称 + Map deviceNames = new HashMap<>(); + if (!allDeviceIds.isEmpty()) { + deviceNames = deviceRepository.batchGetDeviceNames(new ArrayList<>(allDeviceIds)); + } + + // 对机位ID按ID排序,保证表头顺序一致 + List sortedDeviceIds = new ArrayList<>(allDeviceIds); + sortedDeviceIds.sort(Long::compareTo); + + // 3. 创建Excel工作簿 Workbook workbook = new XSSFWorkbook(); Sheet sheet = workbook.createSheet("视频评价数据"); - // 3. 创建标题行样式 + // 4. 创建标题行样式 CellStyle headerStyle = workbook.createCellStyle(); Font headerFont = workbook.createFont(); headerFont.setBold(true); @@ -165,53 +195,110 @@ public class VideoReviewServiceImpl implements VideoReviewService { headerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); - // 4. 创建标题行 + // 5. 创建单元格自动换行样式 + CellStyle wrapStyle = workbook.createCellStyle(); + wrapStyle.setWrapText(true); + wrapStyle.setVerticalAlignment(VerticalAlignment.TOP); + + // 6. 生成动态表头 - 使用机位名称作为表头 Row headerRow = sheet.createRow(0); - String[] headers = {"评价ID", "视频ID", "景区ID", "景区名称", "评价人ID", "评价人名称", - "评分", "文字评价", "机位评价", "创建时间", "更新时间"}; - for (int i = 0; i < headers.length; i++) { + List headerList = new ArrayList<>(); + headerList.add("评价ID"); + headerList.add("视频ID"); + headerList.add("视频模板名称"); + headerList.add("景区名称"); + headerList.add("评价人名称"); + headerList.add("评分"); + headerList.add("文字评价"); + + // 添加机位列 - 表头直接使用机位名称 + Map finalDeviceNames = deviceNames; + for (Long deviceId : sortedDeviceIds) { + String deviceName = finalDeviceNames.getOrDefault(deviceId, "未知设备(ID:" + deviceId + ")"); + headerList.add(deviceName); + } + + headerList.add("创建时间"); + headerList.add("更新时间"); + + // 设置表头 + for (int i = 0; i < headerList.size(); i++) { Cell cell = headerRow.createCell(i); - cell.setCellValue(headers[i]); + cell.setCellValue(headerList.get(i)); cell.setCellStyle(headerStyle); } - // 5. 填充数据 + // 7. 填充数据 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); int rowNum = 1; + for (VideoReviewRespDTO review : list) { Row row = sheet.createRow(rowNum++); - row.createCell(0).setCellValue(review.getId()); - row.createCell(1).setCellValue(review.getVideoId()); - row.createCell(2).setCellValue(review.getScenicId()); - row.createCell(3).setCellValue(review.getScenicName()); - row.createCell(4).setCellValue(review.getCreator()); - row.createCell(5).setCellValue(review.getCreatorName()); - row.createCell(6).setCellValue(review.getRating()); - row.createCell(7).setCellValue(review.getContent()); + int colIndex = 0; - // 机位评价JSON转字符串 - try { - String cameraRatingJson = review.getCameraPositionRating() != null ? - objectMapper.writeValueAsString(review.getCameraPositionRating()) : ""; - row.createCell(8).setCellValue(cameraRatingJson); - } catch (Exception e) { - row.createCell(8).setCellValue(""); + // 基础信息列 + row.createCell(colIndex++).setCellValue(review.getId()); + row.createCell(colIndex++).setCellValue(review.getVideoId()); + row.createCell(colIndex++).setCellValue(review.getTemplateName() != null ? review.getTemplateName() : ""); + row.createCell(colIndex++).setCellValue(review.getScenicName()); + row.createCell(colIndex++).setCellValue(review.getCreatorName()); + row.createCell(colIndex++).setCellValue(review.getRating()); + row.createCell(colIndex++).setCellValue(review.getContent()); + + // 机位评价列 - 按表头顺序填充 + Map> cameraRating = review.getCameraPositionRating(); + for (Long deviceId : sortedDeviceIds) { + String deviceIdStr = String.valueOf(deviceId); + Map dimensions = null; + + if (cameraRating != null && cameraRating.containsKey(deviceIdStr)) { + dimensions = cameraRating.get(deviceIdStr); + } + + // 构建单元格内容: 只显示评分维度(不再重复机位名称) + StringBuilder cellContent = new StringBuilder(); + + if (dimensions != null && !dimensions.isEmpty()) { + // 按维度名排序,保证一致性 + List> sortedDimensions = dimensions.entrySet().stream() + .sorted(Map.Entry.comparingByKey()) + .collect(Collectors.toList()); + + boolean first = true; + for (Map.Entry dimEntry : sortedDimensions) { + if (!first) { + cellContent.append("\n"); + } + cellContent.append(dimEntry.getKey()).append(":").append(dimEntry.getValue()); + first = false; + } + } + + Cell cell = row.createCell(colIndex++); + cell.setCellValue(cellContent.toString()); + cell.setCellStyle(wrapStyle); } - row.createCell(9).setCellValue(review.getCreateTime() != null ? sdf.format(review.getCreateTime()) : ""); - row.createCell(10).setCellValue(review.getUpdateTime() != null ? sdf.format(review.getUpdateTime()) : ""); + // 时间列 + row.createCell(colIndex++).setCellValue(review.getCreateTime() != null ? sdf.format(review.getCreateTime()) : ""); + row.createCell(colIndex).setCellValue(review.getUpdateTime() != null ? sdf.format(review.getUpdateTime()) : ""); } - // 6. 自动调整列宽 - for (int i = 0; i < headers.length; i++) { + // 8. 自动调整列宽 + for (int i = 0; i < headerList.size(); i++) { sheet.autoSizeColumn(i); + // 对于机位列,设置最小宽度以便换行内容显示完整 + if (i >= 7 && i < 7 + sortedDeviceIds.size()) { + int currentWidth = sheet.getColumnWidth(i); + sheet.setColumnWidth(i, Math.max(currentWidth, 5000)); // 最小25个字符宽度 + } } - // 7. 写入输出流 + // 9. 写入输出流 workbook.write(outputStream); workbook.close(); - log.info("导出视频评价数据成功,共{}条", list.size()); + log.info("导出视频评价数据成功,共{}条,机位数:{}", list.size(), sortedDeviceIds.size()); } /** diff --git a/src/main/resources/mapper/VideoReviewMapper.xml b/src/main/resources/mapper/VideoReviewMapper.xml index e08d2c2a..dda53b37 100644 --- a/src/main/resources/mapper/VideoReviewMapper.xml +++ b/src/main/resources/mapper/VideoReviewMapper.xml @@ -7,6 +7,8 @@ + + @@ -33,10 +35,13 @@ vr.create_time, vr.update_time, v.video_url, + v.template_id, + t.name AS template_name, s.name AS scenic_name, u.name AS creator_name FROM video_review vr LEFT JOIN video v ON vr.video_id = v.id + LEFT JOIN template t ON v.template_id = t.id LEFT JOIN scenic s ON vr.scenic_id = s.id LEFT JOIN admin_user u ON vr.creator = u.id