diff --git a/pom.xml b/pom.xml
index fb3534b..f48bd55 100644
--- a/pom.xml
+++ b/pom.xml
@@ -142,8 +142,6 @@
2.15.3
-
-
com.alibaba
fastjson
@@ -228,7 +226,6 @@
2.3.0
-
com.arcsoft.face
arcsoft-sdk-face
@@ -525,6 +522,12 @@
spring-boot-starter-data-jpa
+
+ ws.schild
+ jave-all-deps
+ 3.5.0
+
+
diff --git a/src/main/java/com/guwan/backend/constant/SecurityConstants.java b/src/main/java/com/guwan/backend/constant/SecurityConstants.java
index e2bc233..3faae17 100644
--- a/src/main/java/com/guwan/backend/constant/SecurityConstants.java
+++ b/src/main/java/com/guwan/backend/constant/SecurityConstants.java
@@ -21,6 +21,8 @@ public class SecurityConstants {
"/bs/courses/queryByPage",
+ "/captcha/verify", //验证码认证接口
+
"/challenge",
"/ws/**",
"/faceTest", "/compareFaces",
diff --git a/src/main/java/com/guwan/backend/controller/CaptchaController.java b/src/main/java/com/guwan/backend/controller/CaptchaController.java
new file mode 100644
index 0000000..ee3bdc7
--- /dev/null
+++ b/src/main/java/com/guwan/backend/controller/CaptchaController.java
@@ -0,0 +1,83 @@
+package com.guwan.backend.controller;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.http.ResponseEntity;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.util.UriComponentsBuilder;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.HexFormat;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/captcha")
+public class CaptchaController {
+
+ private static final Map keyMap = new HashMap<>();
+
+ static {
+ keyMap.put("99c6323b7b65100d4f9ff07b036e57fa", "12e9e88d5cb19d63d44f8d95a21b69b6");
+ }
+
+ @GetMapping("/verify")
+ public ResponseEntity> login(@RequestParam Map params) {
+ String captchaId = params.get("captcha_id");
+ String lotNumber = params.get("lot_number");
+
+ String prikey = keyMap.get(captchaId);
+
+ if (prikey == null) {
+ return ResponseEntity.ok(Map.of("result", "fail", "reason", "id is not in id pools"));
+ }
+
+ String signToken = generateSignToken(lotNumber, prikey);
+
+ // 构建新的查询参数
+ MultiValueMap queryParams = new LinkedMultiValueMap<>();
+ queryParams.setAll(params);
+ queryParams.add("sign_token", signToken);
+
+ // 调用 Geetest 接口
+ try {
+ RestTemplate restTemplate = new RestTemplate();
+ URI uri = UriComponentsBuilder.fromHttpUrl("http://gcaptcha4.geetest.com/validate")
+ .queryParams(queryParams)
+ .build()
+ .toUri();
+ //这个url的响应是text/javascript;charset=UTF-8
+ // 不是application/json,RestTemplate 默认的 JSON 转换器(Jackson)不会处理它,就会抛出异常。
+
+ //Map response = restTemplate.getForObject(uri, Map.class);
+
+ String responseStr = restTemplate.getForObject(uri, String.class);
+ ObjectMapper mapper = new ObjectMapper();
+ Map responseMap = mapper.readValue(responseStr, new TypeReference<>() {});
+ return ResponseEntity.ok(responseMap);
+ } catch (Exception e) {
+ return ResponseEntity.ok(Map.of("result", "success", "reason", "request geetest api fail"));
+ }
+ }
+
+ private String generateSignToken(String lotNumber, String privateKey) {
+ try {
+ Mac sha256Hmac = Mac.getInstance("HmacSHA256");
+ SecretKeySpec secretKey = new SecretKeySpec(privateKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
+ sha256Hmac.init(secretKey);
+ byte[] hashBytes = sha256Hmac.doFinal(lotNumber.getBytes(StandardCharsets.UTF_8));
+ return HexFormat.of().formatHex(hashBytes); // Java 17+,或者使用 Apache Commons Codec
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to generate sign token", e);
+ }
+ }
+}
diff --git a/src/main/java/com/guwan/backend/controller/CourseController.java b/src/main/java/com/guwan/backend/controller/CourseController.java
index d2310f2..025933f 100644
--- a/src/main/java/com/guwan/backend/controller/CourseController.java
+++ b/src/main/java/com/guwan/backend/controller/CourseController.java
@@ -3,6 +3,8 @@ package com.guwan.backend.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.guwan.backend.common.Result;
import com.guwan.backend.common.SearchResult;
@@ -18,6 +20,7 @@ import com.guwan.backend.service.*;
import com.guwan.backend.util.MinioUtil;
import com.guwan.backend.util.PPTUtil;
import com.guwan.backend.util.UUIDUtil;
+import com.guwan.backend.util.VideoDurationUtils;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
@@ -211,6 +214,12 @@ public class CourseController {
section.setType(sectionVO.getType());
section.setUrl(sectionVO.getUrl());
section.setOrderNumber(j + 1);
+
+ if (Objects.equals(sectionVO.getType(), "video")){
+ long videoDuration = VideoDurationUtils.getVideoDuration(sectionVO.getUrl());
+ section.setDuration((int) videoDuration);
+ }
+
sectionService.save(section);
if (Objects.equals(sectionVO.getType(), "ppt")){
@@ -238,6 +247,108 @@ public class CourseController {
return Result.success();
}
+ @Transactional
+ @PostMapping("/updateCourse")
+ public Result updateCourse(@RequestBody InsertCourseDTO insertCourseDTO) {
+ System.out.println("insertCourseDTO = " + insertCourseDTO);
+
+ String courseDTOId = insertCourseDTO.getId();
+
+ Course course = courseService.getById(courseDTOId);
+
+ BeanUtils.copyProperties(insertCourseDTO, course);
+ course.setId(courseDTOId);
+ courseService.update(course, new LambdaUpdateWrapper().eq(Course::getId, courseDTOId));
+
+ List chapterVOS = insertCourseDTO.getChapterVOS();
+ //全删 TODO 优化
+ List chapterIds = null;
+ if (!chapterVOS.isEmpty()) {
+ chapterIds = chapterVOS.stream().map(ChapterVO::getId).toList();
+ }
+
+ chapterService.remove(new LambdaQueryWrapper().eq(Chapter::getCourseId, courseDTOId));
+
+ if (chapterIds != null && !chapterIds.isEmpty()) {
+ List sectionList = sectionService.list(new LambdaQueryWrapper().in(Section::getChapterId, chapterIds));
+
+ List sectionIds = null;
+ if (!sectionList.isEmpty()) {
+ sectionIds = sectionList.stream().map(Section::getId).toList();
+ }
+
+ sectionService.remove(new LambdaQueryWrapper().in(Section::getChapterId, chapterIds));
+
+ if (sectionIds != null && !sectionIds.isEmpty()) {
+ slideService.remove(new LambdaQueryWrapper().in(Slide::getSectionId, sectionIds));
+ }
+ }
+
+ for (int i = 0; i < chapterVOS.size(); i++) {
+ ChapterVO chapterVO = chapterVOS.get(i);
+ Chapter chapter = new Chapter();
+ String chapterId = UUIDUtil.uuid();
+ chapter.setId(chapterId);
+ chapter.setCourseId(courseDTOId);
+ chapter.setTitle(chapterVO.getTitle());
+ chapter.setOrderNumber(i);
+ chapterService.save(chapter);
+
+ List sectionVOS = chapterVO.getSectionVOS();
+
+ for (int j = 0; j < sectionVOS.size(); j++) {
+ BaseSectionVO sectionVO = sectionVOS.get(j);
+ String sectionId = UUIDUtil.uuid();
+ Section section = new Section();
+ section.setId(sectionId);
+ section.setChapterId(chapterId);
+ section.setTitle(sectionVO.getTitle());
+ section.setType(sectionVO.getType());
+ section.setUrl(sectionVO.getUrl());
+ section.setOrderNumber(j + 1);
+ //根据url 得到视频时长
+ if (Objects.equals(sectionVO.getType(), "video")){
+ long videoDuration = VideoDurationUtils.getVideoDuration(sectionVO.getUrl());
+ section.setDuration((int) videoDuration);
+ }
+
+ sectionService.save(section);
+
+ if (Objects.equals(sectionVO.getType(), "ppt")) {
+ try {
+ List urlList = pptUtil.PPTToImageConverter(sectionVO.getUrl());
+
+ for (int k = 0; k < urlList.size(); k++) {
+ Slide slide = new Slide();
+ slide.setId(UUIDUtil.uuid());
+ slide.setSectionId(sectionId);
+ slide.setUrl(minioUtil.getUrl(minioUtil.getFileUrl("photo", urlList.get(k))));
+ slide.setOrderNumber(k + 1);
+ slideService.save(slide);
+ }
+
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ }
+
+ }
+
+ return Result.success();
+ }
+
+
+
+ @Transactional
+ @PostMapping("/deleteCourse")
+ public Result deleteCourse(@RequestParam String courseId) {
+
+ courseService.remove(new LambdaQueryWrapper().eq(Course::getId, courseId));
+
+ return Result.success();
+ }
}
diff --git a/src/main/java/com/guwan/backend/pojo/dto/course/InsertCourseDTO.java b/src/main/java/com/guwan/backend/pojo/dto/course/InsertCourseDTO.java
index e1a7ab9..2395358 100644
--- a/src/main/java/com/guwan/backend/pojo/dto/course/InsertCourseDTO.java
+++ b/src/main/java/com/guwan/backend/pojo/dto/course/InsertCourseDTO.java
@@ -4,16 +4,19 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import com.guwan.backend.pojo.response.courseDetail.ChapterVO;
import lombok.Data;
+import java.math.BigDecimal;
import java.util.List;
@Data
public class InsertCourseDTO {
+ private String id;
private String title;
private String description;
private String categoryId;
private String levelId;
private String typeId;
private String teacherId;
+ private BigDecimal price;
private String coverImg;
@JsonProperty("chapters")
private List chapterVOS;
diff --git a/src/main/java/com/guwan/backend/pojo/response/courseDetail/CourseDetailVO.java b/src/main/java/com/guwan/backend/pojo/response/courseDetail/CourseDetailVO.java
index dbaa34c..054cace 100644
--- a/src/main/java/com/guwan/backend/pojo/response/courseDetail/CourseDetailVO.java
+++ b/src/main/java/com/guwan/backend/pojo/response/courseDetail/CourseDetailVO.java
@@ -11,6 +11,7 @@ import java.util.List;
@AllArgsConstructor
@NoArgsConstructor
public class CourseDetailVO {
+ private String id;
/**
* 课程标题
*/
diff --git a/src/main/java/com/guwan/backend/service/impl/CourseServiceImpl.java b/src/main/java/com/guwan/backend/service/impl/CourseServiceImpl.java
index d071b4e..c7f5781 100644
--- a/src/main/java/com/guwan/backend/service/impl/CourseServiceImpl.java
+++ b/src/main/java/com/guwan/backend/service/impl/CourseServiceImpl.java
@@ -72,6 +72,7 @@ public class CourseServiceImpl extends ServiceImpl
System.out.println("course = " + course);
CourseDetailVO courseDetailVO = new CourseDetailVO();
+ courseDetailVO.setId(course.getId());
courseDetailVO.setTitle(course.getTitle());
courseDetailVO.setDescription(course.getDescription());
courseDetailVO.setCategoryName(bsCategoryMapper.selectById(course.getCategoryId()).getChineseName());
@@ -118,31 +119,33 @@ public class CourseServiceImpl extends ServiceImpl
List chapters = chapterMapper.selectList(new LambdaQueryWrapper().eq(Chapter::getCourseId, courseId));
List chapterIds = chapters.stream().map(Chapter::getId).toList();
- //TODO
- List sections = sectionMapper.selectList(new LambdaQueryWrapper().in(Section::getChapterId, chapterIds));
+ // 注意!当集合为 空或null 时, sql会拼接为:WHERE (字段名 IN ()), 执行时报错
- int videoCount = 0;
+ if (!chapterIds.isEmpty()) {
+ List sections = sectionMapper.selectList(new LambdaQueryWrapper().in(Section::getChapterId, chapterIds));
+ int videoCount = 0;
- int docCount = 0;
+ int docCount = 0;
- int totalTimeOfMinute = 0;
+ int totalTimeOfMinute = 0;
- for (Section section : sections) {
- if (Objects.equals("video", section.getType())){
- videoCount ++;
- totalTimeOfMinute += section.getDuration();
+ for (Section section : sections) {
+ if (Objects.equals("video", section.getType())){
+ videoCount ++;
+ totalTimeOfMinute += section.getDuration();
- }else {
- docCount ++;
+ }else {
+ docCount ++;
+ }
}
+
+ courseDetailVO.setVideoCount(videoCount);
+
+ courseDetailVO.setDocCount(docCount);
+
+ courseDetailVO.setTotalDuration(totalTimeOfMinute / 60);
}
- courseDetailVO.setVideoCount(videoCount);
-
- courseDetailVO.setDocCount(docCount);
-
- courseDetailVO.setTotalDuration(totalTimeOfMinute / 60);
-
UserCourseRegistration userCourseRegistration = userCourseRegistrationMapper.selectOne(new LambdaQueryWrapper()
.eq(UserCourseRegistration::getCourseId, courseId)
diff --git a/src/main/java/com/guwan/backend/util/VideoDurationUtils.java b/src/main/java/com/guwan/backend/util/VideoDurationUtils.java
new file mode 100644
index 0000000..266e5d7
--- /dev/null
+++ b/src/main/java/com/guwan/backend/util/VideoDurationUtils.java
@@ -0,0 +1,31 @@
+package com.guwan.backend.util;
+
+import com.guwan.backend.core.exception.ServiceException;
+import ws.schild.jave.EncoderException;
+import ws.schild.jave.MultimediaObject;
+
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+public class VideoDurationUtils {
+
+ /**
+ * 获取视频时长
+ */
+ public static long getVideoDuration(String videoFilePath) {
+ try {
+ URL url = new URL(videoFilePath);
+ MultimediaObject multimediaObject = new MultimediaObject(url);
+ long duration = (multimediaObject.getInfo().getDuration()) / 1000 / 60;
+ System.out.println(duration + "分钟");
+ return duration;
+ } catch (EncoderException | MalformedURLException e) {
+ throw new ServiceException("" + e);
+ }
+ }
+
+ public static void main(String[] args) {
+ getVideoDuration("http://localhost:9000/file/courseSource/4b0445fb-7d47-45ea-a8f8-fa9f2fa108bd.mp4");
+ }
+}