fix: [课程新增]处理ppt

This commit is contained in:
ovo 2025-05-11 19:15:00 +08:00
parent c07b0cac0d
commit 86f1df5e08
17 changed files with 202 additions and 81 deletions

11
pom.xml
View File

@ -407,7 +407,7 @@
<dependency> <dependency>
<groupId>org.apache.poi</groupId> <groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId> <artifactId>poi-ooxml</artifactId>
<version>5.2.3</version> <version>5.4.1</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>org.apache.logging.log4j</groupId> <groupId>org.apache.logging.log4j</groupId>
@ -418,7 +418,7 @@
<dependency> <dependency>
<groupId>org.apache.poi</groupId> <groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId> <artifactId>poi-scratchpad</artifactId>
<version>5.2.3</version> <version>5.4.1</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>org.apache.logging.log4j</groupId> <groupId>org.apache.logging.log4j</groupId>
@ -426,6 +426,13 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.27.1</version>
</dependency>
<!-- 图片处理库 --> <!-- 图片处理库 -->
<dependency> <dependency>
<groupId>commons-io</groupId> <groupId>commons-io</groupId>

View File

@ -8,6 +8,7 @@ import com.guwan.backend.common.Result;
import com.guwan.backend.mongodb.*; import com.guwan.backend.mongodb.*;
import com.guwan.backend.pojo.entity.BookContent; import com.guwan.backend.pojo.entity.BookContent;
import com.guwan.backend.util.MinioUtil; import com.guwan.backend.util.MinioUtil;
import com.guwan.backend.util.PPTUtil;
import dev.langchain4j.community.model.dashscope.QwenChatModel; import dev.langchain4j.community.model.dashscope.QwenChatModel;
import dev.langchain4j.community.model.dashscope.QwenStreamingChatModel; import dev.langchain4j.community.model.dashscope.QwenStreamingChatModel;
import dev.langchain4j.model.chat.response.ChatResponse; import dev.langchain4j.model.chat.response.ChatResponse;
@ -65,6 +66,7 @@ public class CommonController {
private final QwenStreamingChatModel qwenStreamingChatModel; private final QwenStreamingChatModel qwenStreamingChatModel;
private final TestDateRepository testDateRepository; private final TestDateRepository testDateRepository;
private final TestDateDao testDateDao; private final TestDateDao testDateDao;
private final PPTUtil pptUtil;
@PostMapping("/uploadFile") @PostMapping("/uploadFile")
public Result<String> uploadFile(String bucketName, MultipartFile file){ public Result<String> uploadFile(String bucketName, MultipartFile file){
@ -461,6 +463,16 @@ public class CommonController {
ppt.close(); ppt.close();
} }
@GetMapping("/PPTToImageConverterMinio")
public void PPTToImageConverterMinio() throws IOException {
List<String> list = pptUtil.PPTToImageConverter();
for (String s : list) {
System.out.println(s);
}
}
@GetMapping("/videoDuration") @GetMapping("/videoDuration")
public void VideoDuration() throws IOException { public void VideoDuration() throws IOException {

View File

@ -6,7 +6,9 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.guwan.backend.common.Result; import com.guwan.backend.common.Result;
import com.guwan.backend.common.SearchResult; import com.guwan.backend.common.SearchResult;
import com.guwan.backend.mybatis.query.LambdaQueryWrapperX;
import com.guwan.backend.pojo.dto.BSCategory; import com.guwan.backend.pojo.dto.BSCategory;
import com.guwan.backend.pojo.dto.course.InsertCourseDTO;
import com.guwan.backend.pojo.entity.Course; import com.guwan.backend.pojo.entity.Course;
import com.guwan.backend.pojo.entity.Teacher; import com.guwan.backend.pojo.entity.Teacher;
import com.guwan.backend.pojo.response.CourseByAdminVO; import com.guwan.backend.pojo.response.CourseByAdminVO;
@ -131,7 +133,7 @@ public class CourseController {
@RequestParam(required = false) String courseName) { @RequestParam(required = false) String courseName) {
Page<Course> page = new Page<>(pageNumber, size); Page<Course> page = new Page<>(pageNumber, size);
LambdaQueryWrapper<Course> lambdaQueryWrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<Course> lambdaQueryWrapper = new LambdaQueryWrapperX<Course>().likeIfPresent(Course::getCategoryName, courseName);
Page<Course> resultPage = this.courseService.page(page, lambdaQueryWrapper); Page<Course> resultPage = this.courseService.page(page, lambdaQueryWrapper);
@ -159,5 +161,12 @@ public class CourseController {
return SearchResult.success(courseByAdminVOS, resultPage.getTotal()); return SearchResult.success(courseByAdminVOS, resultPage.getTotal());
} }
@PostMapping("/addCourse")
public Result addCourse(@RequestBody InsertCourseDTO insertCourseDTO) {
System.out.println("insertCourseDTO = " + insertCourseDTO);
return Result.success();
}
} }

View File

@ -31,4 +31,14 @@ public class MinioController {
return Result.success(url); return Result.success(url);
} }
@PostMapping("/courseSourceUpload")
public Result courseSourceUpload(@RequestPart("file") MultipartFile file) {
String bucketName = "file";
String folder = "courseSource";
String fileName = minioUtil.uploadFile(bucketName, file, folder);
String fileUrl = minioUtil.getFileUrl(bucketName, fileName);
String url = minioUtil.getUrl(fileUrl);
return Result.success(url);
}
} }

View File

@ -1,6 +1,5 @@
package com.guwan.backend.controller; package com.guwan.backend.controller;
import com.guwan.backend.common.Result; import com.guwan.backend.common.Result;
import com.guwan.backend.pojo.entity.Teacher; import com.guwan.backend.pojo.entity.Teacher;
import com.guwan.backend.service.TeacherService; import com.guwan.backend.service.TeacherService;

View File

@ -27,13 +27,13 @@ public class SectionVOFactory {
BeanUtils.copyProperties(section, video); BeanUtils.copyProperties(section, video);
video.setType("video"); video.setType("video");
video.setDuration(section.getDuration() + ":00"); video.setDuration(section.getDuration() + ":00");
video.setUrl(section.getUrl()); //video.setUrl(section.getUrl());
return video; return video;
case "pdf": case "pdf":
PdfSectionVO pdf = new PdfSectionVO(); PdfSectionVO pdf = new PdfSectionVO();
BeanUtils.copyProperties(section, pdf); BeanUtils.copyProperties(section, pdf);
pdf.setType("pdf"); pdf.setType("pdf");
pdf.setUrl(section.getUrl()); //pdf.setUrl(section.getUrl());
pdf.setDownloadable(true); pdf.setDownloadable(true);
return pdf; return pdf;
case "ppt": case "ppt":
@ -53,7 +53,7 @@ public class SectionVOFactory {
.collect(Collectors.toList()); .collect(Collectors.toList());
ppt.setSlides(slideVOS); ppt.setSlides(slideVOS);
ppt.setDownUrl(section.getUrl()); //ppt.setUrl(section.getUrl());
ppt.setPreViewUrl(KKviewUrlUtil.toKKViewUrl(section.getUrl())); ppt.setPreViewUrl(KKviewUrlUtil.toKKViewUrl(section.getUrl()));
return ppt; return ppt;
default: default:

View File

@ -1,62 +0,0 @@
package com.guwan.backend.pojo.dto;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 课程表
* @TableName courses
*/
@TableName(value ="courses")
@Data
public class Courses implements Serializable {
/**
* 课程ID
*/
@TableId(type = IdType.AUTO)
private Integer id;
/**
* 课程标题
*/
private String title;
/**
* 分类编码
*/
private String categoryId;
/**
* 分类名称
*/
private String categoryName;
/**
* 封面图片URL
*/
private String coverImg;
/**
* 学习人数
*/
private Integer studentCount;
/**
* 评分
*/
private BigDecimal rating;
/**
* 价格
*/
private BigDecimal price;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,20 @@
package com.guwan.backend.pojo.dto.course;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.guwan.backend.pojo.response.courseDetail.ChapterVO;
import lombok.Data;
import java.util.List;
@Data
public class InsertCourseDTO {
private String title;
private String description;
private String categoryId;
private String levelId;
private String typeId;
private String teacherId;
private String coverImg;
@JsonProperty("chapters")
private List<ChapterVO> chapterVOS;
}

View File

@ -7,6 +7,7 @@ public class BaseSectionVO {
private String id; private String id;
private String title; private String title;
private String type; // video / pdf / ppt private String type; // video / pdf / ppt
private String url;
private boolean isFree; private boolean isFree;
private boolean completed; private boolean completed;
} }

View File

@ -24,6 +24,8 @@ public class CourseDetailVO {
private String teacherAvatar; private String teacherAvatar;
private String categoryName;
private String levelName; private String levelName;
private String typeName; private String typeName;

View File

@ -9,6 +9,5 @@ import lombok.ToString;
@ToString(callSuper = true) @ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class PdfSectionVO extends BaseSectionVO { public class PdfSectionVO extends BaseSectionVO {
private String url;
private boolean downloadable; private boolean downloadable;
} }

View File

@ -10,7 +10,7 @@ import java.util.List;
@ToString(callSuper = true) @ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class PptSectionVO extends BaseSectionVO { public class PptSectionVO extends BaseSectionVO {
private String downUrl; private String url;
private String preViewUrl; private String preViewUrl;
private List<SlideVO> slides; private List<SlideVO> slides;
} }

View File

@ -10,5 +10,4 @@ import lombok.ToString;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class VideoSectionVO extends BaseSectionVO { public class VideoSectionVO extends BaseSectionVO {
private String duration; private String duration;
private String url;
} }

View File

@ -89,8 +89,6 @@ public class ChallengesServiceImpl extends ServiceImpl<ChallengesMapper, Challen
} }
} }
TestCaseVO testCaseVO = new TestCaseVO(); TestCaseVO testCaseVO = new TestCaseVO();
testCaseVO.setInput(inputBuilder.toString()); testCaseVO.setInput(inputBuilder.toString());
testCaseVO.setOutput(testCase.getExpectedOutput()); testCaseVO.setOutput(testCase.getExpectedOutput());
@ -245,7 +243,6 @@ public class ChallengesServiceImpl extends ServiceImpl<ChallengesMapper, Challen
return output.toString().trim(); return output.toString().trim();
} }
} }

View File

@ -14,7 +14,6 @@ import com.guwan.backend.service.CourseService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -45,6 +44,8 @@ public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course>
private final UserMapper userMapper; private final UserMapper userMapper;
private final BSCategoryMapper bsCategoryMapper;
private final CourseLevelMapper courseLevelMapper; private final CourseLevelMapper courseLevelMapper;
private final CourseTypeMapper courseTypeMapper; private final CourseTypeMapper courseTypeMapper;
@ -66,6 +67,7 @@ public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course>
CourseDetailVO courseDetailVO = new CourseDetailVO(); CourseDetailVO courseDetailVO = new CourseDetailVO();
courseDetailVO.setTitle(course.getTitle()); courseDetailVO.setTitle(course.getTitle());
courseDetailVO.setDescription(course.getDescription()); courseDetailVO.setDescription(course.getDescription());
courseDetailVO.setCategoryName(bsCategoryMapper.selectById(course.getCategoryId()).getChineseName());
courseDetailVO.setLevelName(courseLevelMapper.selectById(course.getLevelId()).getChineseName()); courseDetailVO.setLevelName(courseLevelMapper.selectById(course.getLevelId()).getChineseName());
courseDetailVO.setTypeName(courseTypeMapper.selectById(course.getTypeId()).getChineseName()); courseDetailVO.setTypeName(courseTypeMapper.selectById(course.getTypeId()).getChineseName());
@ -168,12 +170,10 @@ public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course>
courseDetailVO.setReviewVOS(reviewVOList); courseDetailVO.setReviewVOS(reviewVOList);
courseDetailVO.setRatingDistribution(ratingDistributions.stream().map(RatingDistribution::getCount).collect(Collectors.toList())); courseDetailVO.setRatingDistribution(ratingDistributions.stream().map(RatingDistribution::getCount).collect(Collectors.toList()));
System.out.println("courseDetailVO = " + courseDetailVO); System.out.println("courseDetailVO = " + courseDetailVO);
return courseDetailVO; return courseDetailVO;
} }
public int getStudentCount(String courseId) { public int getStudentCount(String courseId) {

View File

@ -12,9 +12,8 @@ import org.apache.commons.io.FilenameUtils;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.io.ByteArrayInputStream; import java.io.*;
import java.io.IOException; import java.nio.file.Files;
import java.io.InputStream;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.Base64; import java.util.Base64;
@ -94,6 +93,29 @@ public class MinioUtil {
} }
} }
public String uploadFile(String bucketName, File file, String folder) {
createBucket(bucketName);
try (FileInputStream inputStream = new FileInputStream(file)) {
String fileName = generateFileName(file.getName());
fileName = folder + "/" + fileName;
minioClient.putObject(
PutObjectArgs.builder()
.bucket(bucketName)
.object(fileName)
.stream(inputStream, file.length(), -1)
.contentType(Files.probeContentType(file.toPath())) // 自动获取 contentType
.build()
);
return fileName;
} catch (Exception e) {
log.error("上传文件失败", e);
throw new RuntimeException("上传文件失败", e);
}
}
/** /**
* 上传Base64图片 * 上传Base64图片
*/ */

View File

@ -0,0 +1,106 @@
package com.guwan.backend.util;
import lombok.RequiredArgsConstructor;
import org.apache.poi.openxml4j.util.ZipSecureFile;
import org.apache.poi.xslf.usermodel.XMLSlideShow;
import org.apache.poi.xslf.usermodel.XSLFSlide;
import org.springframework.stereotype.Component;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
@Component
@RequiredArgsConstructor
public class PPTUtil {
private final MinioUtil minioUtil;
public List<String> PPTToImageConverter() throws IOException {
List<String> slideList = new ArrayList<>();
String pptFile = "http://localhost:9000/file/courseSource/748831eb-e145-45e6-a4db-07662b747b1f.pptx"; // PPT 文件路径
String outputDir = "output_images"; // 输出目录
ZipSecureFile.setMinInflateRatio(0);
// 从网络加载 PPT 文件
URL url = new URL(pptFile);
URLConnection connection = url.openConnection();
InputStream inputStream = connection.getInputStream();
XMLSlideShow ppt = new XMLSlideShow(inputStream);
inputStream.close();
// 创建输出目录
File dir = new File(outputDir);
if (!dir.exists()) {
dir.mkdirs();
}
// 获取幻灯片
int slideNumber = 1;
for (XSLFSlide slide : ppt.getSlides()) {
Dimension pgsize = ppt.getPageSize();
BufferedImage img = new BufferedImage(pgsize.width, pgsize.height, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = img.createGraphics();
// 设置背景为白色
graphics.setPaint(Color.WHITE);
graphics.fill(new Rectangle2D.Float(0, 0, pgsize.width, pgsize.height));
// 渲染幻灯片
slide.draw(graphics);
// 输出为图片
File outputFile = new File(outputDir + "/slide_" + slideNumber + ".png");
ImageIO.write(img, "png", outputFile);
String uploadResult = minioUtil.uploadFile("photo", outputFile, "ppt");
System.out.println("Saved slide: " + outputFile.getAbsolutePath());
slideNumber++;
// 删除本地文件
boolean deleted = outputFile.delete();
if (deleted) {
System.out.println("Deleted file: " + outputFile.getAbsolutePath());
} else {
System.out.println("Failed to delete file: " + outputFile.getAbsolutePath());
}
slideList.add(uploadResult);
}
ppt.close();
deleteDirectory(new File(outputDir));
return slideList;
}
private void deleteDirectory(File dir) {
if (dir.isDirectory()) {
// 列出文件夹中的文件
File[] files = dir.listFiles();
if (files != null) {
for (File file : files) {
// 递归删除文件或目录
deleteDirectory(file);
}
}
}
// 删除目录本身
boolean deleted = dir.delete();
if (deleted) {
System.out.println("Deleted directory: " + dir.getAbsolutePath());
} else {
System.out.println("Failed to delete directory: " + dir.getAbsolutePath());
}
}
}