From 11fc4e4c3f2f053b8b4e4019954ba81ad26b22fa Mon Sep 17 00:00:00 2001 From: none Date: Sun, 23 Apr 2023 00:26:11 +0800 Subject: [PATCH 01/11] =?UTF-8?q?=E6=96=B0=E5=A2=9E:=E5=90=8E=E5=8F=B0?= =?UTF-8?q?=E5=AD=A6=E5=91=98=E7=BA=BF=E4=B8=8A=E8=AF=BE=E8=AF=BE=E6=97=B6?= =?UTF-8?q?=E7=9A=84=E5=AD=A6=E4=B9=A0=E8=AF=A6=E6=83=85api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- .../controller/backend/UserController.java | 25 +++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) 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/controller/backend/UserController.java b/src/main/java/xyz/playedu/api/controller/backend/UserController.java index a67ae9f..ca293de 100644 --- a/src/main/java/xyz/playedu/api/controller/backend/UserController.java +++ b/src/main/java/xyz/playedu/api/controller/backend/UserController.java @@ -370,7 +370,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); @@ -405,7 +405,7 @@ public class UserController { @BackendPermissionMiddleware(slug = BPermissionConstant.USER_LEARN) @GetMapping("/{id}/learn-courses") - public JsonResponse latestLearnCourses( + public JsonResponse learnCourses( @PathVariable(name = "id") Integer id, @RequestParam HashMap params) { Integer page = MapUtils.getInteger(params, "page", 1); Integer size = MapUtils.getInteger(params, "size", 10); @@ -438,6 +438,27 @@ public class UserController { 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 From 3ff3973d6c592714763aa5dce75e88448dfde634 Mon Sep 17 00:00:00 2001 From: none Date: Sun, 23 Apr 2023 00:39:25 +0800 Subject: [PATCH 02/11] =?UTF-8?q?=E9=83=A8=E9=97=A8=E5=AD=A6=E5=91=98?= =?UTF-8?q?=E5=AD=A6=E4=B9=A0=E8=BF=9B=E5=BA=A6api=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E6=9B=B4=E5=A4=9A=E8=BF=87=E6=BB=A4=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/DepartmentController.java | 52 +++++++++++++++---- 1 file changed, 41 insertions(+), 11 deletions(-) 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<>(); From 78386a289ef4cf3898685402b85ce7a6ecc0dc84 Mon Sep 17 00:00:00 2001 From: none Date: Sun, 23 Apr 2023 09:31:04 +0800 Subject: [PATCH 03/11] =?UTF-8?q?fixed:=20=E5=9B=BE=E5=BD=A2=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E7=A0=81=E6=A0=A1=E9=AA=8C=E7=9A=84=E5=A4=A7=E5=86=99?= =?UTF-8?q?=E6=97=A0=E6=95=88bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xyz/playedu/api/service/impl/ImageCaptchaServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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); From fdb4c9ad736f2ff628acce84cd990e021e06f3cc Mon Sep 17 00:00:00 2001 From: none Date: Sun, 23 Apr 2023 11:57:57 +0800 Subject: [PATCH 04/11] =?UTF-8?q?=E6=96=B0=E5=A2=9E:=E8=AF=BE=E6=97=B6?= =?UTF-8?q?=E8=AF=A6=E6=83=85apic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/frontend/HourController.java | 25 +++++++++++++++++++ .../UserCourseHourFinishedListener.java | 10 -------- 2 files changed, 25 insertions(+), 10 deletions(-) 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..211864e 100644 --- a/src/main/java/xyz/playedu/api/controller/frontend/HourController.java +++ b/src/main/java/xyz/playedu/api/controller/frontend/HourController.java @@ -28,6 +28,7 @@ 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; @@ -43,6 +44,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 +58,28 @@ public class HourController { @Autowired private UserCanSeeCourseCache userCanSeeCourseCache; @Autowired private CourseCache courseCache; + @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( diff --git a/src/main/java/xyz/playedu/api/listener/UserCourseHourFinishedListener.java b/src/main/java/xyz/playedu/api/listener/UserCourseHourFinishedListener.java index a1e1daf..7043663 100644 --- a/src/main/java/xyz/playedu/api/listener/UserCourseHourFinishedListener.java +++ b/src/main/java/xyz/playedu/api/listener/UserCourseHourFinishedListener.java @@ -15,8 +15,6 @@ */ 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; @@ -33,7 +31,6 @@ import xyz.playedu.api.service.UserCourseRecordService; * @create 2023/3/20 17:41 */ @Component -@Slf4j public class UserCourseHourFinishedListener { @Autowired private UserCourseRecordService userCourseRecordService; @@ -49,13 +46,6 @@ public class UserCourseHourFinishedListener { 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); } From 488c7c5c02ba950f5f75d77ca345869fa4578ac8 Mon Sep 17 00:00:00 2001 From: none Date: Sun, 23 Apr 2023 14:27:59 +0800 Subject: [PATCH 05/11] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=AD=A6=E4=B9=A0?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E5=88=A0=E9=99=A4api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/checks/AdminPermissionCheck.java | 9 ++++++++ .../api/constant/BPermissionConstant.java | 1 + .../controller/backend/UserController.java | 21 +++++++++++++++++++ .../service/UserCourseHourRecordService.java | 2 ++ .../api/service/UserCourseRecordService.java | 2 ++ .../impl/UserCourseHourRecordServiceImpl.java | 9 ++++++++ .../impl/UserCourseRecordServiceImpl.java | 5 +++++ 7 files changed, 49 insertions(+) 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/UserController.java b/src/main/java/xyz/playedu/api/controller/backend/UserController.java index ca293de..71b7566 100644 --- a/src/main/java/xyz/playedu/api/controller/backend/UserController.java +++ b/src/main/java/xyz/playedu/api/controller/backend/UserController.java @@ -505,4 +505,25 @@ 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); + 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); + return JsonResponse.success(); + } } 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..a3f1adb 100644 --- a/src/main/java/xyz/playedu/api/service/UserCourseRecordService.java +++ b/src/main/java/xyz/playedu/api/service/UserCourseRecordService.java @@ -43,6 +43,8 @@ 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); 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..fdb8cce 100644 --- a/src/main/java/xyz/playedu/api/service/impl/UserCourseRecordServiceImpl.java +++ b/src/main/java/xyz/playedu/api/service/impl/UserCourseRecordServiceImpl.java @@ -132,4 +132,9 @@ 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)); + } } From 015d8df1ac3c00dfc5e8c04e095ec2988cce63f7 Mon Sep 17 00:00:00 2001 From: none Date: Sun, 23 Apr 2023 15:04:49 +0800 Subject: [PATCH 06/11] =?UTF-8?q?fixed:=20=E5=AD=A6=E5=91=98=E8=AF=BE?= =?UTF-8?q?=E6=97=B6=E5=AD=A6=E4=B9=A0=E8=AE=B0=E5=BD=95=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E7=BA=BF=E4=B8=8A=E8=AF=BE=E5=AD=A6=E4=B9=A0=E8=AE=B0=E5=BD=95?= =?UTF-8?q?=E7=9A=84=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/backend/UserController.java | 4 ++ .../UserCourseHourRecordDestroyEvent.java | 47 +++++++++++++++++ .../UserCourseHourRecordDestroyListener.java | 51 +++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 src/main/java/xyz/playedu/api/event/UserCourseHourRecordDestroyEvent.java create mode 100644 src/main/java/xyz/playedu/api/listener/UserCourseHourRecordDestroyListener.java 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 71b7566..111d18d 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,7 @@ 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.UserDestroyEvent; import xyz.playedu.api.exception.NotFoundException; import xyz.playedu.api.middleware.BackendPermissionMiddleware; @@ -78,6 +79,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) { @@ -524,6 +527,7 @@ public class UserController { @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/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/UserCourseHourRecordDestroyListener.java b/src/main/java/xyz/playedu/api/listener/UserCourseHourRecordDestroyListener.java new file mode 100644 index 0000000..4eba267 --- /dev/null +++ b/src/main/java/xyz/playedu/api/listener/UserCourseHourRecordDestroyListener.java @@ -0,0 +1,51 @@ +/* + * 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.domain.UserCourseRecord; +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) { + UserCourseRecord record = userCourseRecordService.find(e.getUserId(), e.getCourseId()); + if (record == null) { + return; + } + userCourseRecordService.storeOrUpdate( + record.getUserId(), + record.getCourseId(), + record.getHourCount(), + record.getFinishedCount() - 1); + } +} From 8b1203d9d20df1a4b4d16cdf0281cca4843ca6d9 Mon Sep 17 00:00:00 2001 From: none Date: Sun, 23 Apr 2023 15:35:26 +0800 Subject: [PATCH 07/11] =?UTF-8?q?fixed:=20=E5=AD=A6=E5=91=98=E8=AF=BE?= =?UTF-8?q?=E6=97=B6=E8=AE=B0=E5=BD=95=E5=88=A0=E9=99=A4=E7=9A=84=E7=BA=BF?= =?UTF-8?q?=E4=B8=8A=E8=AF=BE=E8=BF=9B=E5=BA=A6=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UserCourseHourRecordDestroyListener.java | 11 +---------- .../api/service/UserCourseRecordService.java | 2 ++ .../impl/UserCourseRecordServiceImpl.java | 19 +++++++++++++++++++ 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/main/java/xyz/playedu/api/listener/UserCourseHourRecordDestroyListener.java b/src/main/java/xyz/playedu/api/listener/UserCourseHourRecordDestroyListener.java index 4eba267..f3ce656 100644 --- a/src/main/java/xyz/playedu/api/listener/UserCourseHourRecordDestroyListener.java +++ b/src/main/java/xyz/playedu/api/listener/UserCourseHourRecordDestroyListener.java @@ -21,7 +21,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; -import xyz.playedu.api.domain.UserCourseRecord; import xyz.playedu.api.event.UserCourseHourRecordDestroyEvent; import xyz.playedu.api.service.UserCourseRecordService; @@ -38,14 +37,6 @@ public class UserCourseHourRecordDestroyListener { @EventListener public void updateUserCourseRecord(UserCourseHourRecordDestroyEvent e) { - UserCourseRecord record = userCourseRecordService.find(e.getUserId(), e.getCourseId()); - if (record == null) { - return; - } - userCourseRecordService.storeOrUpdate( - record.getUserId(), - record.getCourseId(), - record.getHourCount(), - record.getFinishedCount() - 1); + userCourseRecordService.decrease(e.getUserId(), e.getCourseId(), 1); } } diff --git a/src/main/java/xyz/playedu/api/service/UserCourseRecordService.java b/src/main/java/xyz/playedu/api/service/UserCourseRecordService.java index a3f1adb..ed2e702 100644 --- a/src/main/java/xyz/playedu/api/service/UserCourseRecordService.java +++ b/src/main/java/xyz/playedu/api/service/UserCourseRecordService.java @@ -48,4 +48,6 @@ public interface UserCourseRecordService extends IService { 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/UserCourseRecordServiceImpl.java b/src/main/java/xyz/playedu/api/service/impl/UserCourseRecordServiceImpl.java index fdb8cce..b1b9c0f 100644 --- a/src/main/java/xyz/playedu/api/service/impl/UserCourseRecordServiceImpl.java +++ b/src/main/java/xyz/playedu/api/service/impl/UserCourseRecordServiceImpl.java @@ -137,4 +137,23 @@ public class UserCourseRecordServiceImpl 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 * 100 / record.getHourCount()); + newRecord.setIsFinished(0); + + updateById(newRecord); + } } From 51e077afb9ba8835a6aa8f20ecbf099ca8b1602d Mon Sep 17 00:00:00 2001 From: none Date: Sun, 23 Apr 2023 15:40:09 +0800 Subject: [PATCH 08/11] =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=AD=A6=E5=91=98?= =?UTF-8?q?=E7=BA=BF=E4=B8=8A=E8=AF=BE=E7=9A=84=E5=AD=A6=E4=B9=A0=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E5=90=8C=E6=97=B6=E6=B8=85=E7=A9=BA=E8=AF=BE=E6=97=B6?= =?UTF-8?q?=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/xyz/playedu/api/controller/backend/UserController.java | 2 ++ 1 file changed, 2 insertions(+) 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 111d18d..e6ad7b3 100644 --- a/src/main/java/xyz/playedu/api/controller/backend/UserController.java +++ b/src/main/java/xyz/playedu/api/controller/backend/UserController.java @@ -34,6 +34,7 @@ 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; @@ -516,6 +517,7 @@ public class UserController { @PathVariable(name = "id") Integer id, @PathVariable(name = "courseId") Integer courseId) { userCourseRecordService.destroy(id, courseId); + ctx.publishEvent(new UserCourseRecordDestroyEvent(this, id, courseId)); return JsonResponse.success(); } From abf6a36c4f3596a37d4d8611cb3f122cd9aadc57 Mon Sep 17 00:00:00 2001 From: none Date: Mon, 24 Apr 2023 10:49:52 +0800 Subject: [PATCH 09/11] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=90=8E=E5=8F=B0?= =?UTF-8?q?=E5=AD=A6=E5=91=98=E7=9A=84=E5=85=A8=E9=83=A8=E7=BA=BF=E4=B8=8A?= =?UTF-8?q?=E8=AF=BE=E8=BF=9B=E5=BA=A6api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/backend/UserController.java | 54 ++++++++++++++++++- .../api/service/DepartmentService.java | 2 + .../api/service/impl/CourseServiceImpl.java | 3 ++ .../service/impl/DepartmentServiceImpl.java | 8 +++ 4 files changed, 66 insertions(+), 1 deletion(-) 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 e6ad7b3..6ad3556 100644 --- a/src/main/java/xyz/playedu/api/controller/backend/UserController.java +++ b/src/main/java/xyz/playedu/api/controller/backend/UserController.java @@ -409,7 +409,7 @@ public class UserController { @BackendPermissionMiddleware(slug = BPermissionConstant.USER_LEARN) @GetMapping("/{id}/learn-courses") - public JsonResponse learnCourses( + public JsonResponse latestLearnCourses( @PathVariable(name = "id") Integer id, @RequestParam HashMap params) { Integer page = MapUtils.getInteger(params, "page", 1); Integer size = MapUtils.getInteger(params, "size", 10); @@ -442,6 +442,58 @@ 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 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/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)); + } } From 246cfe6501e4ce743536e985d96918eec9a0dd30 Mon Sep 17 00:00:00 2001 From: none Date: Tue, 25 Apr 2023 10:23:57 +0800 Subject: [PATCH 10/11] =?UTF-8?q?=E5=AD=A6=E5=91=98=E7=9A=84=E5=AD=A6?= =?UTF-8?q?=E4=B9=A0=E8=AE=B0=E5=BD=95=E5=A2=9E=E5=8A=A0=E7=88=B1=E9=94=81?= =?UTF-8?q?=E6=8E=A7=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/xyz/playedu/api/bus/UserBus.java | 1 + .../controller/frontend/HourController.java | 32 +++++++++++++++++-- .../UserCourseHourFinishedListener.java | 2 -- .../UserLearnCourseUpdateListener.java | 2 -- .../UserLearnDurationStatsServiceImpl.java | 9 +++--- 5 files changed, 34 insertions(+), 12 deletions(-) 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/controller/frontend/HourController.java b/src/main/java/xyz/playedu/api/controller/frontend/HourController.java index 211864e..d46b770 100644 --- a/src/main/java/xyz/playedu/api/controller/frontend/HourController.java +++ b/src/main/java/xyz/playedu/api/controller/frontend/HourController.java @@ -32,8 +32,10 @@ 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 杭州白书科技有限公司 @@ -58,6 +60,8 @@ public class HourController { @Autowired private UserCanSeeCourseCache userCanSeeCourseCache; @Autowired private CourseCache courseCache; + @Autowired private RedisDistributedLock redisDistributedLock; + @GetMapping("/{id}") @SneakyThrows public JsonResponse detail( @@ -108,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(); } @@ -127,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/listener/UserCourseHourFinishedListener.java b/src/main/java/xyz/playedu/api/listener/UserCourseHourFinishedListener.java index 7043663..7805098 100644 --- a/src/main/java/xyz/playedu/api/listener/UserCourseHourFinishedListener.java +++ b/src/main/java/xyz/playedu/api/listener/UserCourseHourFinishedListener.java @@ -17,7 +17,6 @@ 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; @@ -39,7 +38,6 @@ public class UserCourseHourFinishedListener { @Autowired private CourseHourService hourService; - @Async @EventListener public void userCourseProgressUpdate(UserCourseHourFinishedEvent evt) { Integer hourCount = hourService.getCountByCourseId(evt.getCourseId()); 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/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; } From 417b6d57d75f2a2d4720ebe0dca230822a726765 Mon Sep 17 00:00:00 2001 From: none Date: Thu, 4 May 2023 09:08:41 +0800 Subject: [PATCH 11/11] =?UTF-8?q?fixed:=20=E5=90=8E=E5=8F=B0=E9=87=8D?= =?UTF-8?q?=E5=88=B6=E5=AD=A6=E5=91=98=E7=9A=84=E5=AD=A6=E4=B9=A0=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E5=AF=BC=E8=87=B4=E8=BF=9B=E5=BA=A6=E8=AE=A1=E7=AE=97?= =?UTF-8?q?=E5=A4=B1=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../playedu/api/service/impl/UserCourseRecordServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 b1b9c0f..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(); @@ -151,7 +151,7 @@ public class UserCourseRecordServiceImpl newRecord.setId(record.getId()); newRecord.setFinishedCount(finishedCount); newRecord.setFinishedAt(null); - newRecord.setProgress(finishedCount * 100 / record.getHourCount()); + newRecord.setProgress(finishedCount * 10000 / record.getHourCount()); newRecord.setIsFinished(0); updateById(newRecord);