diff --git a/pom.xml b/pom.xml index 2f1ddc2..a4bd74b 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ xyz.playedu playedu-api - 0.1-beta.1 + 1.0-beta.4 playedu-api playedu-api diff --git a/src/main/java/xyz/playedu/api/bus/UserBus.java b/src/main/java/xyz/playedu/api/bus/UserBus.java index 8620c93..5df2956 100644 --- a/src/main/java/xyz/playedu/api/bus/UserBus.java +++ b/src/main/java/xyz/playedu/api/bus/UserBus.java @@ -60,6 +60,7 @@ public class UserBus { return CollectionUtils.intersection(courseDepIds, userDepIds).size() > 0; } + // 注意,调用该方法需要考虑到并发写入问题 public void userLearnDurationRecord(User user, Course course, CourseHour hour) { Long curTime = System.currentTimeMillis(); diff --git a/src/main/java/xyz/playedu/api/checks/AdminPermissionCheck.java b/src/main/java/xyz/playedu/api/checks/AdminPermissionCheck.java index 95d8951..ae5fcda 100644 --- a/src/main/java/xyz/playedu/api/checks/AdminPermissionCheck.java +++ b/src/main/java/xyz/playedu/api/checks/AdminPermissionCheck.java @@ -162,6 +162,15 @@ public class AdminPermissionCheck implements ApplicationRunner { setSlug(BPermissionConstant.USER_LEARN); } }, + new AdminPermission() { + { + setSort(50); + setName("学习-删除"); + setSlug( + BPermissionConstant + .USER_LEARN_DESTROY); + } + }, }); // 线上课 put( diff --git a/src/main/java/xyz/playedu/api/constant/BPermissionConstant.java b/src/main/java/xyz/playedu/api/constant/BPermissionConstant.java index 332aed1..f49bc6d 100644 --- a/src/main/java/xyz/playedu/api/constant/BPermissionConstant.java +++ b/src/main/java/xyz/playedu/api/constant/BPermissionConstant.java @@ -42,6 +42,7 @@ public class BPermissionConstant { public static final String USER_UPDATE = "user-update"; public static final String USER_DESTROY = "user-destroy"; 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_USER = "course-user"; diff --git a/src/main/java/xyz/playedu/api/controller/backend/DepartmentController.java b/src/main/java/xyz/playedu/api/controller/backend/DepartmentController.java index c66a3ab..16c7b2b 100644 --- a/src/main/java/xyz/playedu/api/controller/backend/DepartmentController.java +++ b/src/main/java/xyz/playedu/api/controller/backend/DepartmentController.java @@ -190,6 +190,9 @@ public class DepartmentController { String idCard = MapUtils.getString(params, "id_card"); String depIds = String.valueOf(id); + String courseIdsStr = MapUtils.getString(params, "course_ids"); + String showMode = MapUtils.getString(params, "show_mode"); + UserPaginateFilter filter = new UserPaginateFilter() { { @@ -204,21 +207,48 @@ public class DepartmentController { PaginationResult users = userService.paginate(page, size, filter); - // 部门关联线上课 - List courses = - courseService.getDepCoursesAndShow( - new ArrayList<>() { - { - add(id); - } - }); + List courses; + if (courseIdsStr != null && courseIdsStr.trim().length() > 0) { + // 指定了需要显示的线上课 + courses = + courseService.chunks( + 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 openCourses = courseService.getOpenCoursesAndShow(10000); + ; + if (openCourses != null) { + courses.addAll(openCourses); + } + } + } + + List courseIds = courses.stream().map(Course::getId).toList(); // 学员的课程学习进度 Map> userCourseRecords = userCourseRecordService - .chunk( - users.getData().stream().map(User::getId).toList(), - courses.stream().map(Course::getId).toList()) + .chunk(users.getData().stream().map(User::getId).toList(), courseIds) .stream() .collect(Collectors.groupingBy(UserCourseRecord::getUserId)); Map> userCourseRecordsMap = new HashMap<>(); diff --git a/src/main/java/xyz/playedu/api/controller/backend/UserController.java b/src/main/java/xyz/playedu/api/controller/backend/UserController.java index a67ae9f..6ad3556 100644 --- a/src/main/java/xyz/playedu/api/controller/backend/UserController.java +++ b/src/main/java/xyz/playedu/api/controller/backend/UserController.java @@ -33,6 +33,8 @@ import xyz.playedu.api.constant.BPermissionConstant; import xyz.playedu.api.constant.CConfig; import xyz.playedu.api.constant.SystemConstant; 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.exception.NotFoundException; import xyz.playedu.api.middleware.BackendPermissionMiddleware; @@ -78,6 +80,8 @@ public class UserController { @Autowired private UserLearnDurationStatsService userLearnDurationStatsService; + @Autowired private ApplicationContext ctx; + @BackendPermissionMiddleware(slug = BPermissionConstant.USER_INDEX) @GetMapping("/index") public JsonResponse index(@RequestParam HashMap params) { @@ -370,7 +374,7 @@ public class UserController { @BackendPermissionMiddleware(slug = BPermissionConstant.USER_LEARN) @GetMapping("/{id}/learn-hours") @SneakyThrows - public JsonResponse latestLearnHours( + public JsonResponse learnHours( @PathVariable(name = "id") Integer id, @RequestParam HashMap params) { Integer page = MapUtils.getInteger(params, "page", 1); Integer size = MapUtils.getInteger(params, "size", 10); @@ -438,6 +442,79 @@ public class UserController { return JsonResponse.data(data); } + @BackendPermissionMiddleware(slug = BPermissionConstant.USER_LEARN) + @GetMapping("/{id}/all-courses") + public JsonResponse allCourses(@PathVariable(name = "id") Integer id) { + // 读取学员关联的部门 + List depIds = userService.getDepIdsByUserId(id); + List departments = new ArrayList<>(); + HashMap> depCourses = new HashMap<>(); + List courseIds = new ArrayList<>(); + + if (depIds != null && depIds.size() > 0) { + departments = departmentService.chunk(depIds); + depIds.forEach( + (depId) -> { + List 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 openCourses = courseService.getOpenCoursesAndShow(1000); + if (openCourses != null && openCourses.size() > 0) { + courseIds.addAll(openCourses.stream().map(Course::getId).toList()); + } + + // 读取学员的线上课学习记录 + List userCourseRecords = new ArrayList<>(); + if (courseIds.size() > 0) { + userCourseRecords = userCourseRecordService.chunk(id, courseIds); + } + + HashMap 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 hours = courseHourService.getHoursByCourseId(courseId); + // 读取学员的课时学习记录 + List records = userCourseHourRecordService.getRecords(id, courseId); + + HashMap 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) @GetMapping("/{id}/learn-stats") @SneakyThrows @@ -484,4 +561,27 @@ public class UserController { 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(); + } } diff --git a/src/main/java/xyz/playedu/api/controller/frontend/HourController.java b/src/main/java/xyz/playedu/api/controller/frontend/HourController.java index f7b9b52..d46b770 100644 --- a/src/main/java/xyz/playedu/api/controller/frontend/HourController.java +++ b/src/main/java/xyz/playedu/api/controller/frontend/HourController.java @@ -28,11 +28,14 @@ import xyz.playedu.api.caches.UserCanSeeCourseCache; import xyz.playedu.api.domain.*; 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 xyz.playedu.api.util.RedisDistributedLock; import java.util.HashMap; +import java.util.concurrent.TimeUnit; /** * @Author 杭州白书科技有限公司 @@ -43,6 +46,8 @@ import java.util.HashMap; @RequestMapping("/api/v1/course/{courseId}/hour") public class HourController { + @Autowired private CourseService courseService; + @Autowired private CourseHourService hourService; @Autowired private ResourceService resourceService; @@ -55,6 +60,30 @@ public class HourController { @Autowired private UserCanSeeCourseCache userCanSeeCourseCache; @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 data = new HashMap<>(); + data.put("course", course); + data.put("hour", courseHour); + data.put("user_hour_record", userCourseHourRecord); + + return JsonResponse.data(data); + } + @GetMapping("/{id}/play") @SneakyThrows public JsonResponse play( @@ -83,13 +112,23 @@ public class HourController { if (duration <= 0) { return JsonResponse.error("duration参数错误"); } - User user = FCtx.getUser(); + Course course = courseCache.findOrFail(courseId); - userCanSeeCourseCache.check(user, course, true); 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( - user.getId(), course.getId(), hour.getId(), duration, hour.getDuration()); + FCtx.getId(), course.getId(), hour.getId(), duration, hour.getDuration()); + + // 此处未考虑上面代码执行失败释放锁 + redisDistributedLock.releaseLock(lockKey); return JsonResponse.success(); } @@ -102,7 +141,19 @@ public class HourController { Course course = courseCache.findOrFail(courseId); CourseHour hour = hourService.findOrFail(id, courseId); 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); + + // 此处未考虑上面代码执行失败释放锁 + redisDistributedLock.releaseLock(lockKey); + return JsonResponse.success(); } } diff --git a/src/main/java/xyz/playedu/api/event/UserCourseHourRecordDestroyEvent.java b/src/main/java/xyz/playedu/api/event/UserCourseHourRecordDestroyEvent.java new file mode 100644 index 0000000..29df33b --- /dev/null +++ b/src/main/java/xyz/playedu/api/event/UserCourseHourRecordDestroyEvent.java @@ -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(); + } +} diff --git a/src/main/java/xyz/playedu/api/listener/UserCourseHourFinishedListener.java b/src/main/java/xyz/playedu/api/listener/UserCourseHourFinishedListener.java index a1e1daf..7805098 100644 --- a/src/main/java/xyz/playedu/api/listener/UserCourseHourFinishedListener.java +++ b/src/main/java/xyz/playedu/api/listener/UserCourseHourFinishedListener.java @@ -15,11 +15,8 @@ */ 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.scheduling.annotation.Async; import org.springframework.stereotype.Component; import xyz.playedu.api.event.UserCourseHourFinishedEvent; @@ -33,7 +30,6 @@ import xyz.playedu.api.service.UserCourseRecordService; * @create 2023/3/20 17:41 */ @Component -@Slf4j public class UserCourseHourFinishedListener { @Autowired private UserCourseRecordService userCourseRecordService; @@ -42,20 +38,12 @@ public class UserCourseHourFinishedListener { @Autowired private CourseHourService hourService; - @Async @EventListener public void userCourseProgressUpdate(UserCourseHourFinishedEvent evt) { Integer hourCount = hourService.getCountByCourseId(evt.getCourseId()); Integer finishedCount = userCourseHourRecordService.getFinishedHourCount( evt.getUserId(), evt.getCourseId()); - log.info( - "UserCourseHourFinishedListener courseId={} userId={} hourCount={}" - + " finishedCount={}", - evt.getCourseId(), - evt.getUserId(), - hourCount, - finishedCount); userCourseRecordService.storeOrUpdate( evt.getUserId(), evt.getCourseId(), hourCount, finishedCount); } diff --git a/src/main/java/xyz/playedu/api/listener/UserCourseHourRecordDestroyListener.java b/src/main/java/xyz/playedu/api/listener/UserCourseHourRecordDestroyListener.java new file mode 100644 index 0000000..f3ce656 --- /dev/null +++ b/src/main/java/xyz/playedu/api/listener/UserCourseHourRecordDestroyListener.java @@ -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); + } +} diff --git a/src/main/java/xyz/playedu/api/listener/UserLearnCourseUpdateListener.java b/src/main/java/xyz/playedu/api/listener/UserLearnCourseUpdateListener.java index e94ed0f..6e379df 100644 --- a/src/main/java/xyz/playedu/api/listener/UserLearnCourseUpdateListener.java +++ b/src/main/java/xyz/playedu/api/listener/UserLearnCourseUpdateListener.java @@ -19,7 +19,6 @@ import lombok.extern.slf4j.Slf4j; 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.UserLearnCourseUpdateEvent; @@ -39,7 +38,6 @@ public class UserLearnCourseUpdateListener { @Autowired private UserLearnDurationStatsService userLearnDurationStatsService; - @Async @EventListener public void storeLearnDuration(UserLearnCourseUpdateEvent event) { // 观看时长统计 diff --git a/src/main/java/xyz/playedu/api/service/DepartmentService.java b/src/main/java/xyz/playedu/api/service/DepartmentService.java index be0b71d..d6ce589 100644 --- a/src/main/java/xyz/playedu/api/service/DepartmentService.java +++ b/src/main/java/xyz/playedu/api/service/DepartmentService.java @@ -64,4 +64,6 @@ public interface DepartmentService extends IService { Long total(); Map getDepartmentsUserCount(); + + List chunk(List ids); } diff --git a/src/main/java/xyz/playedu/api/service/UserCourseHourRecordService.java b/src/main/java/xyz/playedu/api/service/UserCourseHourRecordService.java index 91e59c9..1a61e59 100644 --- a/src/main/java/xyz/playedu/api/service/UserCourseHourRecordService.java +++ b/src/main/java/xyz/playedu/api/service/UserCourseHourRecordService.java @@ -49,6 +49,8 @@ public interface UserCourseHourRecordService extends IService getUserCourseHourCount( Integer userId, List courseIds, Integer isFinished); diff --git a/src/main/java/xyz/playedu/api/service/UserCourseRecordService.java b/src/main/java/xyz/playedu/api/service/UserCourseRecordService.java index 83603fd..ed2e702 100644 --- a/src/main/java/xyz/playedu/api/service/UserCourseRecordService.java +++ b/src/main/java/xyz/playedu/api/service/UserCourseRecordService.java @@ -43,7 +43,11 @@ public interface UserCourseRecordService extends IService { void destroy(Integer courseId, List ids); + void destroy(Integer userId, Integer courseId); + void removeByCourseId(Integer courseId); List chunks(List ids, List fields); + + void decrease(Integer userId, Integer courseId, int count); } diff --git a/src/main/java/xyz/playedu/api/service/impl/CourseServiceImpl.java b/src/main/java/xyz/playedu/api/service/impl/CourseServiceImpl.java index 38faa5c..23225fc 100644 --- a/src/main/java/xyz/playedu/api/service/impl/CourseServiceImpl.java +++ b/src/main/java/xyz/playedu/api/service/impl/CourseServiceImpl.java @@ -214,6 +214,9 @@ public class CourseServiceImpl extends ServiceImpl impleme @Override public List getDepCoursesAndShow(List depIds) { + if (depIds == null || depIds.size() == 0) { + return new ArrayList<>(); + } List courseIds = courseDepartmentService.getCourseIdsByDepIds(depIds); if (courseIds == null || courseIds.size() == 0) { return new ArrayList<>(); diff --git a/src/main/java/xyz/playedu/api/service/impl/DepartmentServiceImpl.java b/src/main/java/xyz/playedu/api/service/impl/DepartmentServiceImpl.java index ff74f3e..8b62a39 100644 --- a/src/main/java/xyz/playedu/api/service/impl/DepartmentServiceImpl.java +++ b/src/main/java/xyz/playedu/api/service/impl/DepartmentServiceImpl.java @@ -268,4 +268,12 @@ public class DepartmentServiceImpl extends ServiceImpl chunk(List ids) { + if (ids == null || ids.size() == 0) { + return new ArrayList<>(); + } + return list(query().getWrapper().in("id", ids)); + } } diff --git a/src/main/java/xyz/playedu/api/service/impl/ImageCaptchaServiceImpl.java b/src/main/java/xyz/playedu/api/service/impl/ImageCaptchaServiceImpl.java index 7dfe8d6..662b6de 100644 --- a/src/main/java/xyz/playedu/api/service/impl/ImageCaptchaServiceImpl.java +++ b/src/main/java/xyz/playedu/api/service/impl/ImageCaptchaServiceImpl.java @@ -81,7 +81,7 @@ public class ImageCaptchaServiceImpl implements ImageCaptchaService { return false; } String cacheValue = (String) queryResult; - boolean verifyResult = cacheValue.equals(code); + boolean verifyResult = cacheValue.equalsIgnoreCase(code); if (verifyResult) { // 验证成功删除缓存->防止多次使用 RedisUtil.del(cacheKey); diff --git a/src/main/java/xyz/playedu/api/service/impl/UserCourseHourRecordServiceImpl.java b/src/main/java/xyz/playedu/api/service/impl/UserCourseHourRecordServiceImpl.java index 1d8c46a..1743325 100644 --- a/src/main/java/xyz/playedu/api/service/impl/UserCourseHourRecordServiceImpl.java +++ b/src/main/java/xyz/playedu/api/service/impl/UserCourseHourRecordServiceImpl.java @@ -153,4 +153,13 @@ public class UserCourseHourRecordServiceImpl 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)); + } } diff --git a/src/main/java/xyz/playedu/api/service/impl/UserCourseRecordServiceImpl.java b/src/main/java/xyz/playedu/api/service/impl/UserCourseRecordServiceImpl.java index 8047885..5c892b1 100644 --- a/src/main/java/xyz/playedu/api/service/impl/UserCourseRecordServiceImpl.java +++ b/src/main/java/xyz/playedu/api/service/impl/UserCourseRecordServiceImpl.java @@ -60,7 +60,7 @@ public class UserCourseRecordServiceImpl boolean isFinished = finishedCount >= hourCount; Date finishedAt = isFinished ? new Date() : null; - Integer progress = finishedCount * 100 / hourCount * 100; + Integer progress = finishedCount * 10000 / hourCount; if (record == null) { UserCourseRecord insertRecord = new UserCourseRecord(); @@ -132,4 +132,28 @@ public class UserCourseRecordServiceImpl public List chunks(List ids, List 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); + } } diff --git a/src/main/java/xyz/playedu/api/service/impl/UserLearnDurationStatsServiceImpl.java b/src/main/java/xyz/playedu/api/service/impl/UserLearnDurationStatsServiceImpl.java index c737d74..8b0b716 100644 --- a/src/main/java/xyz/playedu/api/service/impl/UserLearnDurationStatsServiceImpl.java +++ b/src/main/java/xyz/playedu/api/service/impl/UserLearnDurationStatsServiceImpl.java @@ -15,6 +15,8 @@ */ package xyz.playedu.api.service.impl; +import cn.hutool.core.date.DateTime; + import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import lombok.SneakyThrows; @@ -42,10 +44,7 @@ public class UserLearnDurationStatsServiceImpl @Override @SneakyThrows public void storeOrUpdate(Integer userId, Long startTime, Long endTime) { - // 处理日期 - SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); - String date = simpleDateFormat.format(new Date(endTime)); - // duration + String date = new DateTime().toDateStr(); Long duration = endTime - startTime; UserLearnDurationStats stats = @@ -54,7 +53,7 @@ public class UserLearnDurationStatsServiceImpl UserLearnDurationStats newStats = new UserLearnDurationStats(); newStats.setUserId(userId); newStats.setDuration(duration); - newStats.setCreatedDate(simpleDateFormat.parse(date)); + newStats.setCreatedDate(new DateTime(date)); save(newStats); return; }