增加课时观看进度记录

This commit is contained in:
none 2023-03-20 17:52:24 +08:00
parent 84bd4fdb55
commit efb3a69498
8 changed files with 248 additions and 26 deletions

View File

@ -1,17 +1,18 @@
package xyz.playedu.api.controller.frontend;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import xyz.playedu.api.PlayEduFCtx;
import xyz.playedu.api.caches.UserCanSeeCourseCache;
import xyz.playedu.api.domain.Course;
import xyz.playedu.api.domain.CourseHour;
import xyz.playedu.api.domain.Resource;
import xyz.playedu.api.domain.*;
import xyz.playedu.api.exception.NotFoundException;
import xyz.playedu.api.exception.ServiceException;
import xyz.playedu.api.request.frontend.CourseHourRecordRequest;
import xyz.playedu.api.service.CourseHourService;
import xyz.playedu.api.service.CourseService;
import xyz.playedu.api.service.ResourceService;
import xyz.playedu.api.service.UserCourseHourRecordService;
import xyz.playedu.api.types.JsonResponse;
import java.util.HashMap;
@ -36,6 +37,9 @@ public class HourController {
@Autowired
private ResourceService resourceService;
@Autowired
private UserCourseHourRecordService userCourseHourRecordService;
@GetMapping("/{id}/play")
public JsonResponse play(@PathVariable(name = "courseId") Integer courseId, @PathVariable(name = "id") Integer id) throws NotFoundException, ServiceException {
Course course = courseService.findOrFail(courseId);
@ -52,10 +56,28 @@ public class HourController {
}
@PostMapping("/{id}/record")
public JsonResponse record(@PathVariable(name = "courseId") Integer courseId, @PathVariable(name = "id") Integer id) throws NotFoundException, ServiceException {
public JsonResponse record(@PathVariable(name = "courseId") Integer courseId, @PathVariable(name = "id") Integer id, @RequestBody @Validated CourseHourRecordRequest req) throws NotFoundException, ServiceException {
Integer duration = req.getDuration();
if (duration <= 0) {
return JsonResponse.error("duration参数错误");
}
User user = PlayEduFCtx.getUser();
// 线上课检测
Course course = courseService.findOrFail(courseId);
// 权限校验
userCanSeeCourseCache.check(user, course, true);
// 课时检测
CourseHour hour = hourService.findOrFail(id, courseId);
userCourseHourRecordService.storeOrUpdate(user.getId(), course.getId(), hour.getId(), duration, hour.getDuration());
return JsonResponse.success();
}
@PostMapping("/{id}/ping")
public JsonResponse ping(@PathVariable(name = "courseId") Integer courseId, @PathVariable(name = "id") Integer id) throws NotFoundException, ServiceException {
Course course = courseService.findOrFail(courseId);
userCanSeeCourseCache.check(PlayEduFCtx.getUser(), course, true);
CourseHour hour = hourService.findOrFail(id, courseId);
return JsonResponse.success();
}

View File

@ -0,0 +1,28 @@
package xyz.playedu.api.event;
import lombok.Getter;
import lombok.Setter;
import org.springframework.context.ApplicationEvent;
import java.util.Date;
/**
* @Author 杭州白书科技有限公司
* @create 2023/3/20 17:32
*/
@Getter
@Setter
public class UserCourseHourFinishedEvent extends ApplicationEvent {
private Integer userId;
private Integer courseId;
private Integer hourId;
private Date createdAt;
public UserCourseHourFinishedEvent(Object source, Integer userId, Integer courseId, Integer hourId) {
super(source);
this.userId = userId;
this.courseId = courseId;
this.hourId = hourId;
this.createdAt = new Date();
}
}

View File

@ -0,0 +1,36 @@
package xyz.playedu.api.listener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import xyz.playedu.api.event.UserCourseHourFinishedEvent;
import xyz.playedu.api.service.CourseHourService;
import xyz.playedu.api.service.UserCourseHourRecordService;
import xyz.playedu.api.service.UserCourseRecordService;
/**
* @Author 杭州白书科技有限公司
* @create 2023/3/20 17:41
*/
@Component
public class UserCourseHourFinishedListener {
@Autowired
private UserCourseRecordService userCourseRecordService;
@Autowired
private UserCourseHourRecordService userCourseHourRecordService;
@Autowired
private CourseHourService hourService;
@EventListener
@Async
public void userCourseProgressUpdate(UserCourseHourFinishedEvent evt) {
Integer hourCount = hourService.getCountByCourseId(evt.getCourseId());
Integer finishedCount = userCourseHourRecordService.getFinishedHourCount(evt.getUserId(), evt.getCourseId());
userCourseRecordService.storeOrUpdate(evt.getUserId(), evt.getCourseId(), hourCount, finishedCount);
}
}

View File

@ -0,0 +1,14 @@
package xyz.playedu.api.request.frontend;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* @Author 杭州白书科技有限公司
* @create 2023/3/20 17:12
*/
@Data
public class CourseHourRecordRequest {
@NotNull(message = "duration参数不存在")
private Integer duration;
}

View File

@ -4,10 +4,14 @@ import xyz.playedu.api.domain.UserCourseHourRecord;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author tengteng
* @description 针对表user_course_hour_records的数据库操作Service
* @createDate 2023-03-20 16:41:08
*/
* @author tengteng
* @description 针对表user_course_hour_records的数据库操作Service
* @createDate 2023-03-20 16:41:08
*/
public interface UserCourseHourRecordService extends IService<UserCourseHourRecord> {
UserCourseHourRecord find(Integer userId, Integer courseId, Integer hourId);
UserCourseHourRecord storeOrUpdate(Integer userId, Integer courseId, Integer hourId, Integer duration, Integer totalDuration);
Integer getFinishedHourCount(Integer userId, Integer courseId);
}

View File

@ -4,10 +4,13 @@ import xyz.playedu.api.domain.UserCourseRecord;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author tengteng
* @description 针对表user_course_records的数据库操作Service
* @createDate 2023-03-20 16:41:04
*/
* @author tengteng
* @description 针对表user_course_records的数据库操作Service
* @createDate 2023-03-20 16:41:04
*/
public interface UserCourseRecordService extends IService<UserCourseRecord> {
UserCourseRecord find(Integer userId, Integer courseId);
void storeOrUpdate(Integer userId, Integer courseId, Integer hourCount, Integer finishedCount);
}

View File

@ -1,20 +1,86 @@
package xyz.playedu.api.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import xyz.playedu.api.domain.UserCourseHourRecord;
import xyz.playedu.api.event.UserCourseHourFinishedEvent;
import xyz.playedu.api.service.UserCourseHourRecordService;
import xyz.playedu.api.mapper.UserCourseHourRecordMapper;
import org.springframework.stereotype.Service;
/**
* @author tengteng
* @description 针对表user_course_hour_records的数据库操作Service实现
* @createDate 2023-03-20 16:41:08
*/
@Service
public class UserCourseHourRecordServiceImpl extends ServiceImpl<UserCourseHourRecordMapper, UserCourseHourRecord>
implements UserCourseHourRecordService{
import java.util.Date;
/**
* @author tengteng
* @description 针对表user_course_hour_records的数据库操作Service实现
* @createDate 2023-03-20 16:41:08
*/
@Service
public class UserCourseHourRecordServiceImpl extends ServiceImpl<UserCourseHourRecordMapper, UserCourseHourRecord> implements UserCourseHourRecordService {
@Autowired
private ApplicationContext ctx;
@Override
public UserCourseHourRecord find(Integer userId, Integer courseId, Integer hourId) {
return getOne(query().getWrapper().eq("user_id", userId).eq("course_id", courseId).eq("hour_id", hourId));
}
@Override
public UserCourseHourRecord storeOrUpdate(Integer userId, Integer courseId, Integer hourId, Integer duration, Integer totalDuration) {
UserCourseHourRecord record = find(userId, courseId, hourId);
// 已看完
if (record != null && record.getIsFinished() == 1) {
return record;
}
// 是否看完
boolean isFinished = duration >= totalDuration;
Date finishedAt = isFinished ? new Date() : null;
if (record == null) {
UserCourseHourRecord insertRecord = new UserCourseHourRecord();
insertRecord.setUserId(userId);
insertRecord.setCourseId(courseId);
insertRecord.setHourId(hourId);
insertRecord.setTotalDuration(totalDuration);
insertRecord.setFinishedDuration(duration);
insertRecord.setIsFinished(isFinished ? 1 : 0);
insertRecord.setFinishedAt(finishedAt);
insertRecord.setCreatedAt(new Date());
insertRecord.setUpdatedAt(new Date());
save(insertRecord);
record = insertRecord;
} else {
// 未看完 && 大于存在的观看记录时长
if (record.getFinishedDuration() < duration) {
UserCourseHourRecord updateRecord = new UserCourseHourRecord();
updateRecord.setId(record.getId());
updateRecord.setTotalDuration(totalDuration);
updateRecord.setFinishedDuration(duration);
updateRecord.setIsFinished(isFinished ? 1 : 0);
updateRecord.setFinishedAt(finishedAt);
updateById(updateRecord);
record = updateRecord;
}
}
if (isFinished) {
ctx.publishEvent(new UserCourseHourFinishedEvent(this, record.getUserId(), record.getCourseId(), record.getHourId()));
}
return record;
}
@Override
public Integer getFinishedHourCount(Integer userId, Integer courseId) {
return Math.toIntExact(count(query().getWrapper().eq("user_id", userId).eq("course_id", courseId).eq("is_finished", 1)));
}
}

View File

@ -6,15 +6,64 @@ import xyz.playedu.api.service.UserCourseRecordService;
import xyz.playedu.api.mapper.UserCourseRecordMapper;
import org.springframework.stereotype.Service;
import java.util.Date;
/**
* @author tengteng
* @description 针对表user_course_records的数据库操作Service实现
* @createDate 2023-03-20 16:41:04
*/
* @author tengteng
* @description 针对表user_course_records的数据库操作Service实现
* @createDate 2023-03-20 16:41:04
*/
@Service
public class UserCourseRecordServiceImpl extends ServiceImpl<UserCourseRecordMapper, UserCourseRecord>
implements UserCourseRecordService{
implements UserCourseRecordService {
@Override
public UserCourseRecord find(Integer userId, Integer courseId) {
return getOne(query().getWrapper().eq("user_id", userId).eq("course_id", courseId));
}
@Override
public void storeOrUpdate(Integer userId, Integer courseId, Integer hourCount, Integer finishedCount) {
if (hourCount == 0) {
return;
}
UserCourseRecord record = find(userId, courseId);
// 已看完
if (record != null && record.getIsFinished() == 1) {
return;
}
boolean isFinished = finishedCount >= hourCount;
Date finishedAt = isFinished ? new Date() : null;
Integer progress = finishedCount * 100 / hourCount * 100;
if (record == null) {
UserCourseRecord insertRecord = new UserCourseRecord();
insertRecord.setUserId(userId);
insertRecord.setCourseId(courseId);
insertRecord.setHourCount(hourCount);
insertRecord.setFinishedCount(finishedCount);
insertRecord.setFinishedAt(finishedAt);
insertRecord.setIsFinished(isFinished ? 1 : 0);
insertRecord.setProgress(progress);
insertRecord.setCreatedAt(new Date());
insertRecord.setUpdatedAt(new Date());
save(insertRecord);
} else {
UserCourseRecord updateRecord = new UserCourseRecord();
updateRecord.setId(record.getId());
updateRecord.setHourCount(hourCount);
updateRecord.setFinishedCount(finishedCount);
updateRecord.setFinishedAt(finishedAt);
updateRecord.setIsFinished(isFinished ? 1 : 0);
updateRecord.setProgress(progress);
updateById(updateRecord);
}
}
}