mirror of
https://github.com/PlayEdu/PlayEdu
synced 2025-07-26 03:49:31 +08:00
commit
b8f06a3bdb
2
pom.xml
2
pom.xml
@ -10,7 +10,7 @@
|
|||||||
</parent>
|
</parent>
|
||||||
<groupId>xyz.playedu</groupId>
|
<groupId>xyz.playedu</groupId>
|
||||||
<artifactId>playedu-api</artifactId>
|
<artifactId>playedu-api</artifactId>
|
||||||
<version>0.1-beta.1</version>
|
<version>1.0-beta.4</version>
|
||||||
<name>playedu-api</name>
|
<name>playedu-api</name>
|
||||||
<description>playedu-api</description>
|
<description>playedu-api</description>
|
||||||
<properties>
|
<properties>
|
||||||
|
@ -60,6 +60,7 @@ public class UserBus {
|
|||||||
return CollectionUtils.intersection(courseDepIds, userDepIds).size() > 0;
|
return CollectionUtils.intersection(courseDepIds, userDepIds).size() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 注意,调用该方法需要考虑到并发写入问题
|
||||||
public void userLearnDurationRecord(User user, Course course, CourseHour hour) {
|
public void userLearnDurationRecord(User user, Course course, CourseHour hour) {
|
||||||
Long curTime = System.currentTimeMillis();
|
Long curTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
@ -162,6 +162,15 @@ public class AdminPermissionCheck implements ApplicationRunner {
|
|||||||
setSlug(BPermissionConstant.USER_LEARN);
|
setSlug(BPermissionConstant.USER_LEARN);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
new AdminPermission() {
|
||||||
|
{
|
||||||
|
setSort(50);
|
||||||
|
setName("学习-删除");
|
||||||
|
setSlug(
|
||||||
|
BPermissionConstant
|
||||||
|
.USER_LEARN_DESTROY);
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
// 线上课
|
// 线上课
|
||||||
put(
|
put(
|
||||||
|
@ -42,6 +42,7 @@ public class BPermissionConstant {
|
|||||||
public static final String USER_UPDATE = "user-update";
|
public static final String USER_UPDATE = "user-update";
|
||||||
public static final String USER_DESTROY = "user-destroy";
|
public static final String USER_DESTROY = "user-destroy";
|
||||||
public static final String USER_LEARN = "user-learn";
|
public static final String USER_LEARN = "user-learn";
|
||||||
|
public static final String USER_LEARN_DESTROY = "user-learn-destroy";
|
||||||
|
|
||||||
public static final String COURSE = "course";
|
public static final String COURSE = "course";
|
||||||
public static final String COURSE_USER = "course-user";
|
public static final String COURSE_USER = "course-user";
|
||||||
|
@ -190,6 +190,9 @@ public class DepartmentController {
|
|||||||
String idCard = MapUtils.getString(params, "id_card");
|
String idCard = MapUtils.getString(params, "id_card");
|
||||||
String depIds = String.valueOf(id);
|
String depIds = String.valueOf(id);
|
||||||
|
|
||||||
|
String courseIdsStr = MapUtils.getString(params, "course_ids");
|
||||||
|
String showMode = MapUtils.getString(params, "show_mode");
|
||||||
|
|
||||||
UserPaginateFilter filter =
|
UserPaginateFilter filter =
|
||||||
new UserPaginateFilter() {
|
new UserPaginateFilter() {
|
||||||
{
|
{
|
||||||
@ -204,21 +207,48 @@ public class DepartmentController {
|
|||||||
|
|
||||||
PaginationResult<User> users = userService.paginate(page, size, filter);
|
PaginationResult<User> users = userService.paginate(page, size, filter);
|
||||||
|
|
||||||
// 部门关联线上课
|
List<Course> courses;
|
||||||
List<Course> courses =
|
if (courseIdsStr != null && courseIdsStr.trim().length() > 0) {
|
||||||
courseService.getDepCoursesAndShow(
|
// 指定了需要显示的线上课
|
||||||
new ArrayList<>() {
|
courses =
|
||||||
{
|
courseService.chunks(
|
||||||
add(id);
|
Arrays.stream(courseIdsStr.split(",")).map(Integer::valueOf).toList());
|
||||||
}
|
} else {
|
||||||
});
|
if ("only_open".equals(showMode)) {
|
||||||
|
// 公开(无关联部门)线上课
|
||||||
|
courses = courseService.getOpenCoursesAndShow(10000);
|
||||||
|
} else if ("only_dep".equals(showMode)) {
|
||||||
|
// 部门关联线上课
|
||||||
|
courses =
|
||||||
|
courseService.getDepCoursesAndShow(
|
||||||
|
new ArrayList<>() {
|
||||||
|
{
|
||||||
|
add(id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 部门关联线上课
|
||||||
|
courses =
|
||||||
|
courseService.getDepCoursesAndShow(
|
||||||
|
new ArrayList<>() {
|
||||||
|
{
|
||||||
|
add(id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
List<Course> openCourses = courseService.getOpenCoursesAndShow(10000);
|
||||||
|
;
|
||||||
|
if (openCourses != null) {
|
||||||
|
courses.addAll(openCourses);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Integer> courseIds = courses.stream().map(Course::getId).toList();
|
||||||
|
|
||||||
// 学员的课程学习进度
|
// 学员的课程学习进度
|
||||||
Map<Integer, List<UserCourseRecord>> userCourseRecords =
|
Map<Integer, List<UserCourseRecord>> userCourseRecords =
|
||||||
userCourseRecordService
|
userCourseRecordService
|
||||||
.chunk(
|
.chunk(users.getData().stream().map(User::getId).toList(), courseIds)
|
||||||
users.getData().stream().map(User::getId).toList(),
|
|
||||||
courses.stream().map(Course::getId).toList())
|
|
||||||
.stream()
|
.stream()
|
||||||
.collect(Collectors.groupingBy(UserCourseRecord::getUserId));
|
.collect(Collectors.groupingBy(UserCourseRecord::getUserId));
|
||||||
Map<Integer, Map<Integer, UserCourseRecord>> userCourseRecordsMap = new HashMap<>();
|
Map<Integer, Map<Integer, UserCourseRecord>> userCourseRecordsMap = new HashMap<>();
|
||||||
|
@ -33,6 +33,8 @@ import xyz.playedu.api.constant.BPermissionConstant;
|
|||||||
import xyz.playedu.api.constant.CConfig;
|
import xyz.playedu.api.constant.CConfig;
|
||||||
import xyz.playedu.api.constant.SystemConstant;
|
import xyz.playedu.api.constant.SystemConstant;
|
||||||
import xyz.playedu.api.domain.*;
|
import xyz.playedu.api.domain.*;
|
||||||
|
import xyz.playedu.api.event.UserCourseHourRecordDestroyEvent;
|
||||||
|
import xyz.playedu.api.event.UserCourseRecordDestroyEvent;
|
||||||
import xyz.playedu.api.event.UserDestroyEvent;
|
import xyz.playedu.api.event.UserDestroyEvent;
|
||||||
import xyz.playedu.api.exception.NotFoundException;
|
import xyz.playedu.api.exception.NotFoundException;
|
||||||
import xyz.playedu.api.middleware.BackendPermissionMiddleware;
|
import xyz.playedu.api.middleware.BackendPermissionMiddleware;
|
||||||
@ -78,6 +80,8 @@ public class UserController {
|
|||||||
|
|
||||||
@Autowired private UserLearnDurationStatsService userLearnDurationStatsService;
|
@Autowired private UserLearnDurationStatsService userLearnDurationStatsService;
|
||||||
|
|
||||||
|
@Autowired private ApplicationContext ctx;
|
||||||
|
|
||||||
@BackendPermissionMiddleware(slug = BPermissionConstant.USER_INDEX)
|
@BackendPermissionMiddleware(slug = BPermissionConstant.USER_INDEX)
|
||||||
@GetMapping("/index")
|
@GetMapping("/index")
|
||||||
public JsonResponse index(@RequestParam HashMap<String, Object> params) {
|
public JsonResponse index(@RequestParam HashMap<String, Object> params) {
|
||||||
@ -370,7 +374,7 @@ public class UserController {
|
|||||||
@BackendPermissionMiddleware(slug = BPermissionConstant.USER_LEARN)
|
@BackendPermissionMiddleware(slug = BPermissionConstant.USER_LEARN)
|
||||||
@GetMapping("/{id}/learn-hours")
|
@GetMapping("/{id}/learn-hours")
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public JsonResponse latestLearnHours(
|
public JsonResponse learnHours(
|
||||||
@PathVariable(name = "id") Integer id, @RequestParam HashMap<String, Object> params) {
|
@PathVariable(name = "id") Integer id, @RequestParam HashMap<String, Object> params) {
|
||||||
Integer page = MapUtils.getInteger(params, "page", 1);
|
Integer page = MapUtils.getInteger(params, "page", 1);
|
||||||
Integer size = MapUtils.getInteger(params, "size", 10);
|
Integer size = MapUtils.getInteger(params, "size", 10);
|
||||||
@ -438,6 +442,79 @@ public class UserController {
|
|||||||
return JsonResponse.data(data);
|
return JsonResponse.data(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@BackendPermissionMiddleware(slug = BPermissionConstant.USER_LEARN)
|
||||||
|
@GetMapping("/{id}/all-courses")
|
||||||
|
public JsonResponse allCourses(@PathVariable(name = "id") Integer id) {
|
||||||
|
// 读取学员关联的部门
|
||||||
|
List<Integer> depIds = userService.getDepIdsByUserId(id);
|
||||||
|
List<Department> departments = new ArrayList<>();
|
||||||
|
HashMap<Integer, List<Course>> depCourses = new HashMap<>();
|
||||||
|
List<Integer> courseIds = new ArrayList<>();
|
||||||
|
|
||||||
|
if (depIds != null && depIds.size() > 0) {
|
||||||
|
departments = departmentService.chunk(depIds);
|
||||||
|
depIds.forEach(
|
||||||
|
(depId) -> {
|
||||||
|
List<Course> tmpCourses =
|
||||||
|
courseService.getDepCoursesAndShow(
|
||||||
|
new ArrayList<>() {
|
||||||
|
{
|
||||||
|
add(depId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
depCourses.put(depId, tmpCourses);
|
||||||
|
|
||||||
|
if (tmpCourses != null && tmpCourses.size() > 0) {
|
||||||
|
courseIds.addAll(tmpCourses.stream().map(Course::getId).toList());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 未关联部门课程
|
||||||
|
List<Course> openCourses = courseService.getOpenCoursesAndShow(1000);
|
||||||
|
if (openCourses != null && openCourses.size() > 0) {
|
||||||
|
courseIds.addAll(openCourses.stream().map(Course::getId).toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取学员的线上课学习记录
|
||||||
|
List<UserCourseRecord> userCourseRecords = new ArrayList<>();
|
||||||
|
if (courseIds.size() > 0) {
|
||||||
|
userCourseRecords = userCourseRecordService.chunk(id, courseIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap<String, Object> data = new HashMap<>();
|
||||||
|
data.put("open_courses", openCourses);
|
||||||
|
data.put("departments", departments);
|
||||||
|
data.put("dep_courses", depCourses);
|
||||||
|
data.put(
|
||||||
|
"user_course_records",
|
||||||
|
userCourseRecords.stream()
|
||||||
|
.collect(Collectors.toMap(UserCourseRecord::getCourseId, e -> e)));
|
||||||
|
|
||||||
|
return JsonResponse.data(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@BackendPermissionMiddleware(slug = BPermissionConstant.USER_LEARN)
|
||||||
|
@GetMapping("/{id}/learn-course/{courseId}")
|
||||||
|
@SneakyThrows
|
||||||
|
public JsonResponse learnCourseDetail(
|
||||||
|
@PathVariable(name = "id") Integer id,
|
||||||
|
@PathVariable(name = "courseId") Integer courseId) {
|
||||||
|
// 读取线上课下的所有课时
|
||||||
|
List<CourseHour> hours = courseHourService.getHoursByCourseId(courseId);
|
||||||
|
// 读取学员的课时学习记录
|
||||||
|
List<UserCourseHourRecord> records = userCourseHourRecordService.getRecords(id, courseId);
|
||||||
|
|
||||||
|
HashMap<String, Object> data = new HashMap<>();
|
||||||
|
data.put("hours", hours);
|
||||||
|
data.put(
|
||||||
|
"learn_records",
|
||||||
|
records.stream()
|
||||||
|
.collect(Collectors.toMap(UserCourseHourRecord::getHourId, e -> e)));
|
||||||
|
|
||||||
|
return JsonResponse.data(data);
|
||||||
|
}
|
||||||
|
|
||||||
@BackendPermissionMiddleware(slug = BPermissionConstant.USER_LEARN)
|
@BackendPermissionMiddleware(slug = BPermissionConstant.USER_LEARN)
|
||||||
@GetMapping("/{id}/learn-stats")
|
@GetMapping("/{id}/learn-stats")
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@ -484,4 +561,27 @@ public class UserController {
|
|||||||
|
|
||||||
return JsonResponse.data(data);
|
return JsonResponse.data(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@BackendPermissionMiddleware(slug = BPermissionConstant.USER_LEARN_DESTROY)
|
||||||
|
@DeleteMapping("/{id}/learn-course/{courseId}")
|
||||||
|
@SneakyThrows
|
||||||
|
public JsonResponse destroyUserCourse(
|
||||||
|
@PathVariable(name = "id") Integer id,
|
||||||
|
@PathVariable(name = "courseId") Integer courseId) {
|
||||||
|
userCourseRecordService.destroy(id, courseId);
|
||||||
|
ctx.publishEvent(new UserCourseRecordDestroyEvent(this, id, courseId));
|
||||||
|
return JsonResponse.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@BackendPermissionMiddleware(slug = BPermissionConstant.USER_LEARN_DESTROY)
|
||||||
|
@DeleteMapping("/{id}/learn-course/{courseId}/hour/{hourId}")
|
||||||
|
@SneakyThrows
|
||||||
|
public JsonResponse destroyUserHour(
|
||||||
|
@PathVariable(name = "id") Integer id,
|
||||||
|
@PathVariable(name = "courseId") Integer courseId,
|
||||||
|
@PathVariable(name = "hourId") Integer hourId) {
|
||||||
|
userCourseHourRecordService.remove(id, courseId, hourId);
|
||||||
|
ctx.publishEvent(new UserCourseHourRecordDestroyEvent(this, id, courseId, hourId));
|
||||||
|
return JsonResponse.success();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,11 +28,14 @@ import xyz.playedu.api.caches.UserCanSeeCourseCache;
|
|||||||
import xyz.playedu.api.domain.*;
|
import xyz.playedu.api.domain.*;
|
||||||
import xyz.playedu.api.request.frontend.CourseHourRecordRequest;
|
import xyz.playedu.api.request.frontend.CourseHourRecordRequest;
|
||||||
import xyz.playedu.api.service.CourseHourService;
|
import xyz.playedu.api.service.CourseHourService;
|
||||||
|
import xyz.playedu.api.service.CourseService;
|
||||||
import xyz.playedu.api.service.ResourceService;
|
import xyz.playedu.api.service.ResourceService;
|
||||||
import xyz.playedu.api.service.UserCourseHourRecordService;
|
import xyz.playedu.api.service.UserCourseHourRecordService;
|
||||||
import xyz.playedu.api.types.JsonResponse;
|
import xyz.playedu.api.types.JsonResponse;
|
||||||
|
import xyz.playedu.api.util.RedisDistributedLock;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author 杭州白书科技有限公司
|
* @Author 杭州白书科技有限公司
|
||||||
@ -43,6 +46,8 @@ import java.util.HashMap;
|
|||||||
@RequestMapping("/api/v1/course/{courseId}/hour")
|
@RequestMapping("/api/v1/course/{courseId}/hour")
|
||||||
public class HourController {
|
public class HourController {
|
||||||
|
|
||||||
|
@Autowired private CourseService courseService;
|
||||||
|
|
||||||
@Autowired private CourseHourService hourService;
|
@Autowired private CourseHourService hourService;
|
||||||
|
|
||||||
@Autowired private ResourceService resourceService;
|
@Autowired private ResourceService resourceService;
|
||||||
@ -55,6 +60,30 @@ public class HourController {
|
|||||||
@Autowired private UserCanSeeCourseCache userCanSeeCourseCache;
|
@Autowired private UserCanSeeCourseCache userCanSeeCourseCache;
|
||||||
@Autowired private CourseCache courseCache;
|
@Autowired private CourseCache courseCache;
|
||||||
|
|
||||||
|
@Autowired private RedisDistributedLock redisDistributedLock;
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
@SneakyThrows
|
||||||
|
public JsonResponse detail(
|
||||||
|
@PathVariable(name = "courseId") Integer courseId,
|
||||||
|
@PathVariable(name = "id") Integer id) {
|
||||||
|
Course course = courseService.findOrFail(courseId);
|
||||||
|
CourseHour courseHour = hourService.findOrFail(id, courseId);
|
||||||
|
|
||||||
|
UserCourseHourRecord userCourseHourRecord = null;
|
||||||
|
if (FCtx.getId() != null && FCtx.getId() > 0) {
|
||||||
|
// 学员已登录
|
||||||
|
userCourseHourRecord = userCourseHourRecordService.find(FCtx.getId(), courseId, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap<String, Object> data = new HashMap<>();
|
||||||
|
data.put("course", course);
|
||||||
|
data.put("hour", courseHour);
|
||||||
|
data.put("user_hour_record", userCourseHourRecord);
|
||||||
|
|
||||||
|
return JsonResponse.data(data);
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping("/{id}/play")
|
@GetMapping("/{id}/play")
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public JsonResponse play(
|
public JsonResponse play(
|
||||||
@ -83,13 +112,23 @@ public class HourController {
|
|||||||
if (duration <= 0) {
|
if (duration <= 0) {
|
||||||
return JsonResponse.error("duration参数错误");
|
return JsonResponse.error("duration参数错误");
|
||||||
}
|
}
|
||||||
User user = FCtx.getUser();
|
|
||||||
Course course = courseCache.findOrFail(courseId);
|
Course course = courseCache.findOrFail(courseId);
|
||||||
userCanSeeCourseCache.check(user, course, true);
|
|
||||||
CourseHour hour = hourService.findOrFail(id, courseId);
|
CourseHour hour = hourService.findOrFail(id, courseId);
|
||||||
|
userCanSeeCourseCache.check(FCtx.getUser(), course, true);
|
||||||
|
|
||||||
|
// 获取锁
|
||||||
|
String lockKey = String.format("record:%d", FCtx.getId());
|
||||||
|
boolean tryLock = redisDistributedLock.tryLock(lockKey, 5, TimeUnit.SECONDS);
|
||||||
|
if (!tryLock) {
|
||||||
|
return JsonResponse.error("请稍后再试");
|
||||||
|
}
|
||||||
|
|
||||||
userCourseHourRecordService.storeOrUpdate(
|
userCourseHourRecordService.storeOrUpdate(
|
||||||
user.getId(), course.getId(), hour.getId(), duration, hour.getDuration());
|
FCtx.getId(), course.getId(), hour.getId(), duration, hour.getDuration());
|
||||||
|
|
||||||
|
// 此处未考虑上面代码执行失败释放锁
|
||||||
|
redisDistributedLock.releaseLock(lockKey);
|
||||||
|
|
||||||
return JsonResponse.success();
|
return JsonResponse.success();
|
||||||
}
|
}
|
||||||
@ -102,7 +141,19 @@ public class HourController {
|
|||||||
Course course = courseCache.findOrFail(courseId);
|
Course course = courseCache.findOrFail(courseId);
|
||||||
CourseHour hour = hourService.findOrFail(id, courseId);
|
CourseHour hour = hourService.findOrFail(id, courseId);
|
||||||
userCanSeeCourseCache.check(FCtx.getUser(), course, true);
|
userCanSeeCourseCache.check(FCtx.getUser(), course, true);
|
||||||
|
|
||||||
|
// 获取锁
|
||||||
|
String lockKey = String.format("ping:%d", FCtx.getId());
|
||||||
|
boolean tryLock = redisDistributedLock.tryLock(lockKey, 5, TimeUnit.SECONDS);
|
||||||
|
if (!tryLock) {
|
||||||
|
return JsonResponse.error("请稍后再试");
|
||||||
|
}
|
||||||
|
|
||||||
userBus.userLearnDurationRecord(FCtx.getUser(), course, hour);
|
userBus.userLearnDurationRecord(FCtx.getUser(), course, hour);
|
||||||
|
|
||||||
|
// 此处未考虑上面代码执行失败释放锁
|
||||||
|
redisDistributedLock.releaseLock(lockKey);
|
||||||
|
|
||||||
return JsonResponse.success();
|
return JsonResponse.success();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 杭州白书科技有限公司
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package xyz.playedu.api.event;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import org.springframework.context.ApplicationEvent;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author 杭州白书科技有限公司
|
||||||
|
*
|
||||||
|
* @create 2023/4/23 14:48
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class UserCourseHourRecordDestroyEvent extends ApplicationEvent {
|
||||||
|
|
||||||
|
private Integer userId;
|
||||||
|
private Integer courseId;
|
||||||
|
private Integer hourId;
|
||||||
|
private Date at;
|
||||||
|
|
||||||
|
public UserCourseHourRecordDestroyEvent(
|
||||||
|
Object source, Integer userId, Integer courseId, Integer hourId) {
|
||||||
|
super(source);
|
||||||
|
this.userId = userId;
|
||||||
|
this.courseId = courseId;
|
||||||
|
this.hourId = hourId;
|
||||||
|
this.at = new Date();
|
||||||
|
}
|
||||||
|
}
|
@ -15,11 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package xyz.playedu.api.listener;
|
package xyz.playedu.api.listener;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.event.EventListener;
|
import org.springframework.context.event.EventListener;
|
||||||
import org.springframework.scheduling.annotation.Async;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import xyz.playedu.api.event.UserCourseHourFinishedEvent;
|
import xyz.playedu.api.event.UserCourseHourFinishedEvent;
|
||||||
@ -33,7 +30,6 @@ import xyz.playedu.api.service.UserCourseRecordService;
|
|||||||
* @create 2023/3/20 17:41
|
* @create 2023/3/20 17:41
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
@Slf4j
|
|
||||||
public class UserCourseHourFinishedListener {
|
public class UserCourseHourFinishedListener {
|
||||||
|
|
||||||
@Autowired private UserCourseRecordService userCourseRecordService;
|
@Autowired private UserCourseRecordService userCourseRecordService;
|
||||||
@ -42,20 +38,12 @@ public class UserCourseHourFinishedListener {
|
|||||||
|
|
||||||
@Autowired private CourseHourService hourService;
|
@Autowired private CourseHourService hourService;
|
||||||
|
|
||||||
@Async
|
|
||||||
@EventListener
|
@EventListener
|
||||||
public void userCourseProgressUpdate(UserCourseHourFinishedEvent evt) {
|
public void userCourseProgressUpdate(UserCourseHourFinishedEvent evt) {
|
||||||
Integer hourCount = hourService.getCountByCourseId(evt.getCourseId());
|
Integer hourCount = hourService.getCountByCourseId(evt.getCourseId());
|
||||||
Integer finishedCount =
|
Integer finishedCount =
|
||||||
userCourseHourRecordService.getFinishedHourCount(
|
userCourseHourRecordService.getFinishedHourCount(
|
||||||
evt.getUserId(), evt.getCourseId());
|
evt.getUserId(), evt.getCourseId());
|
||||||
log.info(
|
|
||||||
"UserCourseHourFinishedListener courseId={} userId={} hourCount={}"
|
|
||||||
+ " finishedCount={}",
|
|
||||||
evt.getCourseId(),
|
|
||||||
evt.getUserId(),
|
|
||||||
hourCount,
|
|
||||||
finishedCount);
|
|
||||||
userCourseRecordService.storeOrUpdate(
|
userCourseRecordService.storeOrUpdate(
|
||||||
evt.getUserId(), evt.getCourseId(), hourCount, finishedCount);
|
evt.getUserId(), evt.getCourseId(), hourCount, finishedCount);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 杭州白书科技有限公司
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package xyz.playedu.api.listener;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.event.EventListener;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import xyz.playedu.api.event.UserCourseHourRecordDestroyEvent;
|
||||||
|
import xyz.playedu.api.service.UserCourseRecordService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author 杭州白书科技有限公司
|
||||||
|
*
|
||||||
|
* @create 2023/4/23 14:51
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class UserCourseHourRecordDestroyListener {
|
||||||
|
|
||||||
|
@Autowired private UserCourseRecordService userCourseRecordService;
|
||||||
|
|
||||||
|
@EventListener
|
||||||
|
public void updateUserCourseRecord(UserCourseHourRecordDestroyEvent e) {
|
||||||
|
userCourseRecordService.decrease(e.getUserId(), e.getCourseId(), 1);
|
||||||
|
}
|
||||||
|
}
|
@ -19,7 +19,6 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.event.EventListener;
|
import org.springframework.context.event.EventListener;
|
||||||
import org.springframework.scheduling.annotation.Async;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import xyz.playedu.api.event.UserLearnCourseUpdateEvent;
|
import xyz.playedu.api.event.UserLearnCourseUpdateEvent;
|
||||||
@ -39,7 +38,6 @@ public class UserLearnCourseUpdateListener {
|
|||||||
|
|
||||||
@Autowired private UserLearnDurationStatsService userLearnDurationStatsService;
|
@Autowired private UserLearnDurationStatsService userLearnDurationStatsService;
|
||||||
|
|
||||||
@Async
|
|
||||||
@EventListener
|
@EventListener
|
||||||
public void storeLearnDuration(UserLearnCourseUpdateEvent event) {
|
public void storeLearnDuration(UserLearnCourseUpdateEvent event) {
|
||||||
// 观看时长统计
|
// 观看时长统计
|
||||||
|
@ -64,4 +64,6 @@ public interface DepartmentService extends IService<Department> {
|
|||||||
Long total();
|
Long total();
|
||||||
|
|
||||||
Map<Integer, Integer> getDepartmentsUserCount();
|
Map<Integer, Integer> getDepartmentsUserCount();
|
||||||
|
|
||||||
|
List<Department> chunk(List<Integer> ids);
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,8 @@ public interface UserCourseHourRecordService extends IService<UserCourseHourReco
|
|||||||
|
|
||||||
void remove(Integer userId, Integer courseId);
|
void remove(Integer userId, Integer courseId);
|
||||||
|
|
||||||
|
void remove(Integer userId, Integer courseId, Integer hourId);
|
||||||
|
|
||||||
List<UserCourseHourRecordCountMapper> getUserCourseHourCount(
|
List<UserCourseHourRecordCountMapper> getUserCourseHourCount(
|
||||||
Integer userId, List<Integer> courseIds, Integer isFinished);
|
Integer userId, List<Integer> courseIds, Integer isFinished);
|
||||||
|
|
||||||
|
@ -43,7 +43,11 @@ public interface UserCourseRecordService extends IService<UserCourseRecord> {
|
|||||||
|
|
||||||
void destroy(Integer courseId, List<Integer> ids);
|
void destroy(Integer courseId, List<Integer> ids);
|
||||||
|
|
||||||
|
void destroy(Integer userId, Integer courseId);
|
||||||
|
|
||||||
void removeByCourseId(Integer courseId);
|
void removeByCourseId(Integer courseId);
|
||||||
|
|
||||||
List<UserCourseRecord> chunks(List<Integer> ids, List<String> fields);
|
List<UserCourseRecord> chunks(List<Integer> ids, List<String> fields);
|
||||||
|
|
||||||
|
void decrease(Integer userId, Integer courseId, int count);
|
||||||
}
|
}
|
||||||
|
@ -214,6 +214,9 @@ public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course> impleme
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Course> getDepCoursesAndShow(List<Integer> depIds) {
|
public List<Course> getDepCoursesAndShow(List<Integer> depIds) {
|
||||||
|
if (depIds == null || depIds.size() == 0) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
List<Integer> courseIds = courseDepartmentService.getCourseIdsByDepIds(depIds);
|
List<Integer> courseIds = courseDepartmentService.getCourseIdsByDepIds(depIds);
|
||||||
if (courseIds == null || courseIds.size() == 0) {
|
if (courseIds == null || courseIds.size() == 0) {
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
|
@ -268,4 +268,12 @@ public class DepartmentServiceImpl extends ServiceImpl<DepartmentMapper, Departm
|
|||||||
DepartmentsUserCountMapRes::getDepId,
|
DepartmentsUserCountMapRes::getDepId,
|
||||||
DepartmentsUserCountMapRes::getTotal));
|
DepartmentsUserCountMapRes::getTotal));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Department> chunk(List<Integer> ids) {
|
||||||
|
if (ids == null || ids.size() == 0) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
return list(query().getWrapper().in("id", ids));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@ public class ImageCaptchaServiceImpl implements ImageCaptchaService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
String cacheValue = (String) queryResult;
|
String cacheValue = (String) queryResult;
|
||||||
boolean verifyResult = cacheValue.equals(code);
|
boolean verifyResult = cacheValue.equalsIgnoreCase(code);
|
||||||
|
|
||||||
if (verifyResult) { // 验证成功删除缓存->防止多次使用
|
if (verifyResult) { // 验证成功删除缓存->防止多次使用
|
||||||
RedisUtil.del(cacheKey);
|
RedisUtil.del(cacheKey);
|
||||||
|
@ -153,4 +153,13 @@ public class UserCourseHourRecordServiceImpl
|
|||||||
|
|
||||||
return pageResult;
|
return pageResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(Integer userId, Integer courseId, Integer hourId) {
|
||||||
|
remove(
|
||||||
|
query().getWrapper()
|
||||||
|
.eq("user_id", userId)
|
||||||
|
.eq("course_id", courseId)
|
||||||
|
.eq("hour_id", hourId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ public class UserCourseRecordServiceImpl
|
|||||||
|
|
||||||
boolean isFinished = finishedCount >= hourCount;
|
boolean isFinished = finishedCount >= hourCount;
|
||||||
Date finishedAt = isFinished ? new Date() : null;
|
Date finishedAt = isFinished ? new Date() : null;
|
||||||
Integer progress = finishedCount * 100 / hourCount * 100;
|
Integer progress = finishedCount * 10000 / hourCount;
|
||||||
|
|
||||||
if (record == null) {
|
if (record == null) {
|
||||||
UserCourseRecord insertRecord = new UserCourseRecord();
|
UserCourseRecord insertRecord = new UserCourseRecord();
|
||||||
@ -132,4 +132,28 @@ public class UserCourseRecordServiceImpl
|
|||||||
public List<UserCourseRecord> chunks(List<Integer> ids, List<String> fields) {
|
public List<UserCourseRecord> chunks(List<Integer> ids, List<String> fields) {
|
||||||
return list(query().getWrapper().in("id", ids).select(fields));
|
return list(query().getWrapper().in("id", ids).select(fields));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy(Integer userId, Integer courseId) {
|
||||||
|
remove(query().getWrapper().in("user_id", userId).eq("course_id", courseId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void decrease(Integer userId, Integer courseId, int count) {
|
||||||
|
UserCourseRecord record = find(userId, courseId);
|
||||||
|
if (record == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int finishedCount = record.getFinishedCount() - count;
|
||||||
|
|
||||||
|
UserCourseRecord newRecord = new UserCourseRecord();
|
||||||
|
newRecord.setId(record.getId());
|
||||||
|
newRecord.setFinishedCount(finishedCount);
|
||||||
|
newRecord.setFinishedAt(null);
|
||||||
|
newRecord.setProgress(finishedCount * 10000 / record.getHourCount());
|
||||||
|
newRecord.setIsFinished(0);
|
||||||
|
|
||||||
|
updateById(newRecord);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package xyz.playedu.api.service.impl;
|
package xyz.playedu.api.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.DateTime;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
@ -42,10 +44,7 @@ public class UserLearnDurationStatsServiceImpl
|
|||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public void storeOrUpdate(Integer userId, Long startTime, Long endTime) {
|
public void storeOrUpdate(Integer userId, Long startTime, Long endTime) {
|
||||||
// 处理日期
|
String date = new DateTime().toDateStr();
|
||||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
|
||||||
String date = simpleDateFormat.format(new Date(endTime));
|
|
||||||
// duration
|
|
||||||
Long duration = endTime - startTime;
|
Long duration = endTime - startTime;
|
||||||
|
|
||||||
UserLearnDurationStats stats =
|
UserLearnDurationStats stats =
|
||||||
@ -54,7 +53,7 @@ public class UserLearnDurationStatsServiceImpl
|
|||||||
UserLearnDurationStats newStats = new UserLearnDurationStats();
|
UserLearnDurationStats newStats = new UserLearnDurationStats();
|
||||||
newStats.setUserId(userId);
|
newStats.setUserId(userId);
|
||||||
newStats.setDuration(duration);
|
newStats.setDuration(duration);
|
||||||
newStats.setCreatedDate(simpleDateFormat.parse(date));
|
newStats.setCreatedDate(new DateTime(date));
|
||||||
save(newStats);
|
save(newStats);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user