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"); + } +}