feature: [课程] 修改和删除课程
This commit is contained in:
parent
520207c8d2
commit
16eb04344b
9
pom.xml
9
pom.xml
|
@ -142,8 +142,6 @@
|
||||||
<version>2.15.3</version> <!-- 请使用最新的版本 -->
|
<version>2.15.3</version> <!-- 请使用最新的版本 -->
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba</groupId>
|
<groupId>com.alibaba</groupId>
|
||||||
<artifactId>fastjson</artifactId>
|
<artifactId>fastjson</artifactId>
|
||||||
|
@ -228,7 +226,6 @@
|
||||||
<version>2.3.0</version>
|
<version>2.3.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.arcsoft.face</groupId>
|
<groupId>com.arcsoft.face</groupId>
|
||||||
<artifactId>arcsoft-sdk-face</artifactId>
|
<artifactId>arcsoft-sdk-face</artifactId>
|
||||||
|
@ -525,6 +522,12 @@
|
||||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>ws.schild</groupId>
|
||||||
|
<artifactId>jave-all-deps</artifactId>
|
||||||
|
<version>3.5.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,8 @@ public class SecurityConstants {
|
||||||
|
|
||||||
"/bs/courses/queryByPage",
|
"/bs/courses/queryByPage",
|
||||||
|
|
||||||
|
"/captcha/verify", //验证码认证接口
|
||||||
|
|
||||||
"/challenge",
|
"/challenge",
|
||||||
"/ws/**",
|
"/ws/**",
|
||||||
"/faceTest", "/compareFaces",
|
"/faceTest", "/compareFaces",
|
||||||
|
|
|
@ -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<String, String> keyMap = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
keyMap.put("99c6323b7b65100d4f9ff07b036e57fa", "12e9e88d5cb19d63d44f8d95a21b69b6");
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/verify")
|
||||||
|
public ResponseEntity<?> login(@RequestParam Map<String, String> 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<String, String> 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<String, Object> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,8 @@ package com.guwan.backend.controller;
|
||||||
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
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.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;
|
||||||
|
@ -18,6 +20,7 @@ import com.guwan.backend.service.*;
|
||||||
import com.guwan.backend.util.MinioUtil;
|
import com.guwan.backend.util.MinioUtil;
|
||||||
import com.guwan.backend.util.PPTUtil;
|
import com.guwan.backend.util.PPTUtil;
|
||||||
import com.guwan.backend.util.UUIDUtil;
|
import com.guwan.backend.util.UUIDUtil;
|
||||||
|
import com.guwan.backend.util.VideoDurationUtils;
|
||||||
import jakarta.transaction.Transactional;
|
import jakarta.transaction.Transactional;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
@ -211,6 +214,104 @@ public class CourseController {
|
||||||
section.setType(sectionVO.getType());
|
section.setType(sectionVO.getType());
|
||||||
section.setUrl(sectionVO.getUrl());
|
section.setUrl(sectionVO.getUrl());
|
||||||
section.setOrderNumber(j + 1);
|
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")){
|
||||||
|
try {
|
||||||
|
List<String> 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("/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<Course>().eq(Course::getId, courseDTOId));
|
||||||
|
|
||||||
|
List<ChapterVO> chapterVOS = insertCourseDTO.getChapterVOS();
|
||||||
|
//全删 TODO 优化
|
||||||
|
List<String> chapterIds = null;
|
||||||
|
if (!chapterVOS.isEmpty()) {
|
||||||
|
chapterIds = chapterVOS.stream().map(ChapterVO::getId).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
chapterService.remove(new LambdaQueryWrapper<Chapter>().eq(Chapter::getCourseId, courseDTOId));
|
||||||
|
|
||||||
|
if (chapterIds != null && !chapterIds.isEmpty()) {
|
||||||
|
List<Section> sectionList = sectionService.list(new LambdaQueryWrapper<Section>().in(Section::getChapterId, chapterIds));
|
||||||
|
|
||||||
|
List<String> sectionIds = null;
|
||||||
|
if (!sectionList.isEmpty()) {
|
||||||
|
sectionIds = sectionList.stream().map(Section::getId).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
sectionService.remove(new LambdaQueryWrapper<Section>().in(Section::getChapterId, chapterIds));
|
||||||
|
|
||||||
|
if (sectionIds != null && !sectionIds.isEmpty()) {
|
||||||
|
slideService.remove(new LambdaQueryWrapper<Slide>().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<BaseSectionVO> 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);
|
sectionService.save(section);
|
||||||
|
|
||||||
if (Objects.equals(sectionVO.getType(), "ppt")) {
|
if (Objects.equals(sectionVO.getType(), "ppt")) {
|
||||||
|
@ -239,5 +340,15 @@ public class CourseController {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@PostMapping("/deleteCourse")
|
||||||
|
public Result deleteCourse(@RequestParam String courseId) {
|
||||||
|
|
||||||
|
courseService.remove(new LambdaQueryWrapper<Course>().eq(Course::getId, courseId));
|
||||||
|
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,16 +4,19 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import com.guwan.backend.pojo.response.courseDetail.ChapterVO;
|
import com.guwan.backend.pojo.response.courseDetail.ChapterVO;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class InsertCourseDTO {
|
public class InsertCourseDTO {
|
||||||
|
private String id;
|
||||||
private String title;
|
private String title;
|
||||||
private String description;
|
private String description;
|
||||||
private String categoryId;
|
private String categoryId;
|
||||||
private String levelId;
|
private String levelId;
|
||||||
private String typeId;
|
private String typeId;
|
||||||
private String teacherId;
|
private String teacherId;
|
||||||
|
private BigDecimal price;
|
||||||
private String coverImg;
|
private String coverImg;
|
||||||
@JsonProperty("chapters")
|
@JsonProperty("chapters")
|
||||||
private List<ChapterVO> chapterVOS;
|
private List<ChapterVO> chapterVOS;
|
||||||
|
|
|
@ -11,6 +11,7 @@ import java.util.List;
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public class CourseDetailVO {
|
public class CourseDetailVO {
|
||||||
|
private String id;
|
||||||
/**
|
/**
|
||||||
* 课程标题
|
* 课程标题
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -72,6 +72,7 @@ public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course>
|
||||||
System.out.println("course = " + course);
|
System.out.println("course = " + course);
|
||||||
|
|
||||||
CourseDetailVO courseDetailVO = new CourseDetailVO();
|
CourseDetailVO courseDetailVO = new CourseDetailVO();
|
||||||
|
courseDetailVO.setId(course.getId());
|
||||||
courseDetailVO.setTitle(course.getTitle());
|
courseDetailVO.setTitle(course.getTitle());
|
||||||
courseDetailVO.setDescription(course.getDescription());
|
courseDetailVO.setDescription(course.getDescription());
|
||||||
courseDetailVO.setCategoryName(bsCategoryMapper.selectById(course.getCategoryId()).getChineseName());
|
courseDetailVO.setCategoryName(bsCategoryMapper.selectById(course.getCategoryId()).getChineseName());
|
||||||
|
@ -118,9 +119,10 @@ public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course>
|
||||||
List<Chapter> chapters = chapterMapper.selectList(new LambdaQueryWrapper<Chapter>().eq(Chapter::getCourseId, courseId));
|
List<Chapter> chapters = chapterMapper.selectList(new LambdaQueryWrapper<Chapter>().eq(Chapter::getCourseId, courseId));
|
||||||
|
|
||||||
List<String> chapterIds = chapters.stream().map(Chapter::getId).toList();
|
List<String> chapterIds = chapters.stream().map(Chapter::getId).toList();
|
||||||
//TODO
|
// 注意!当集合为 空或null 时, sql会拼接为:WHERE (字段名 IN ()), 执行时报错
|
||||||
List<Section> sections = sectionMapper.selectList(new LambdaQueryWrapper<Section>().in(Section::getChapterId, chapterIds));
|
|
||||||
|
|
||||||
|
if (!chapterIds.isEmpty()) {
|
||||||
|
List<Section> sections = sectionMapper.selectList(new LambdaQueryWrapper<Section>().in(Section::getChapterId, chapterIds));
|
||||||
int videoCount = 0;
|
int videoCount = 0;
|
||||||
|
|
||||||
int docCount = 0;
|
int docCount = 0;
|
||||||
|
@ -142,6 +144,7 @@ public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course>
|
||||||
courseDetailVO.setDocCount(docCount);
|
courseDetailVO.setDocCount(docCount);
|
||||||
|
|
||||||
courseDetailVO.setTotalDuration(totalTimeOfMinute / 60);
|
courseDetailVO.setTotalDuration(totalTimeOfMinute / 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
UserCourseRegistration userCourseRegistration = userCourseRegistrationMapper.selectOne(new LambdaQueryWrapper<UserCourseRegistration>()
|
UserCourseRegistration userCourseRegistration = userCourseRegistrationMapper.selectOne(new LambdaQueryWrapper<UserCourseRegistration>()
|
||||||
|
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue