diff --git a/src/main/java/xyz/playedu/api/controller/backend/CourseAttachmentController.java b/src/main/java/xyz/playedu/api/controller/backend/CourseAttachmentController.java new file mode 100644 index 0000000..59d4d3a --- /dev/null +++ b/src/main/java/xyz/playedu/api/controller/backend/CourseAttachmentController.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 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.controller.backend; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import xyz.playedu.api.constant.BPermissionConstant; +import xyz.playedu.api.constant.BackendConstant; +import xyz.playedu.api.domain.CourseAttachment; +import xyz.playedu.api.exception.NotFoundException; +import xyz.playedu.api.middleware.BackendPermissionMiddleware; +import xyz.playedu.api.request.backend.CourseAttachmentMultiRequest; +import xyz.playedu.api.request.backend.CourseAttachmentRequest; +import xyz.playedu.api.request.backend.CourseAttachmentSortRequest; +import xyz.playedu.api.service.CourseAttachmentService; +import xyz.playedu.api.types.JsonResponse; + +import java.util.*; + +@RestController +@Slf4j +@RequestMapping("/backend/v1/course/{courseId}/attachment") +public class CourseAttachmentController { + + @Autowired private CourseAttachmentService attachmentService; + + @BackendPermissionMiddleware(slug = BPermissionConstant.COURSE) + @PostMapping("/create") + public JsonResponse store( + @PathVariable(name = "courseId") Integer courseId, + @RequestBody @Validated CourseAttachmentRequest req) + throws NotFoundException { + // 附件类型校验 + String type = req.getType(); + if (!BackendConstant.RESOURCE_TYPE_ATTACHMENT.contains(type)) { + return JsonResponse.error("附件类型不支持"); + } + + // 课时重复添加校验 + List existsRids = attachmentService.getRidsByCourseId(courseId); + if (existsRids != null) { + if (existsRids.contains(req.getRid())) { + return JsonResponse.error("附件已存在"); + } + } + + CourseAttachment courseAttachment = + attachmentService.create( + courseId, + req.getSort(), + req.getTitle(), + type, + req.getRid()); + return JsonResponse.success(); + } + + @BackendPermissionMiddleware(slug = BPermissionConstant.COURSE) + @PostMapping("/create-batch") + @Transactional + public JsonResponse storeMulti( + @PathVariable(name = "courseId") Integer courseId, + @RequestBody @Validated CourseAttachmentMultiRequest req) { + if (req.getAttachments().size() == 0) { + return JsonResponse.error("参数为空"); + } + + List existsRids = attachmentService.getRidsByCourseId(courseId); + + List attachments = new ArrayList<>(); + Date now = new Date(); + + for (CourseAttachmentMultiRequest.AttachmentItem item : req.getAttachments()) { + if (existsRids.contains(item.getRid())) { + return JsonResponse.error("附件《" + item.getTitle() + "》已存在"); + } + + attachments.add( + new CourseAttachment() { + { + setCourseId(courseId); + setSort(item.getSort()); + setType(item.getType()); + setRid(item.getRid()); + setTitle(item.getTitle()); + setCreatedAt(now); + } + }); + } + + attachmentService.saveBatch(attachments); + return JsonResponse.success(); + } + + @BackendPermissionMiddleware(slug = BPermissionConstant.COURSE) + @GetMapping("/{id}") + public JsonResponse edit( + @PathVariable(name = "courseId") Integer courseId, + @PathVariable(name = "id") Integer id) + throws NotFoundException { + CourseAttachment courseAttachment = attachmentService.findOrFail(id, courseId); + return JsonResponse.data(courseAttachment); + } + + @BackendPermissionMiddleware(slug = BPermissionConstant.COURSE) + @PutMapping("/{id}") + public JsonResponse update( + @PathVariable(name = "courseId") Integer courseId, + @PathVariable(name = "id") Integer id, + @RequestBody @Validated CourseAttachmentRequest req) + throws NotFoundException { + CourseAttachment courseAttachment = attachmentService.findOrFail(id, courseId); + attachmentService.update(courseAttachment, req.getSort(), req.getTitle()); + return JsonResponse.success(); + } + + @BackendPermissionMiddleware(slug = BPermissionConstant.COURSE) + @DeleteMapping("/{id}") + public JsonResponse destroy( + @PathVariable(name = "courseId") Integer courseId, + @PathVariable(name = "id") Integer id) + throws NotFoundException { + CourseAttachment courseAttachment = attachmentService.findOrFail(id, courseId); + attachmentService.removeById(courseAttachment.getId()); + return JsonResponse.success(); + } + + @PutMapping("/update/sort") + public JsonResponse updateSort( + @PathVariable(name = "courseId") Integer courseId, + @RequestBody @Validated CourseAttachmentSortRequest req) { + attachmentService.updateSort(req.getIds(), courseId); + return JsonResponse.success(); + } +} diff --git a/src/main/java/xyz/playedu/api/controller/backend/CourseController.java b/src/main/java/xyz/playedu/api/controller/backend/CourseController.java index 1d81cd6..e08a5b3 100644 --- a/src/main/java/xyz/playedu/api/controller/backend/CourseController.java +++ b/src/main/java/xyz/playedu/api/controller/backend/CourseController.java @@ -26,7 +26,6 @@ import org.springframework.web.bind.annotation.*; import xyz.playedu.api.BCtx; import xyz.playedu.api.constant.BPermissionConstant; -import xyz.playedu.api.constant.BackendConstant; import xyz.playedu.api.domain.*; import xyz.playedu.api.event.CourseDestroyEvent; import xyz.playedu.api.exception.NotFoundException; @@ -54,6 +53,10 @@ public class CourseController { @Autowired private CourseHourService hourService; + @Autowired private CourseAttachmentService attachmentService; + + @Autowired private ResourceService resourceService; + @Autowired private DepartmentService departmentService; @Autowired private ApplicationContext ctx; @@ -192,6 +195,28 @@ public class CourseController { courseService.updateClassHour(course.getId(), classHourCount); } + // 课程附件 + if (req.getAttachments().size() > 0) { + List insertAttachments = new ArrayList<>(); + final Integer[] sort = {0}; + for (CourseRequest.AttachmentItem attachmentItem : req.getAttachments()) { + insertAttachments.add( + new CourseAttachment() { + { + setCourseId(course.getId()); + setSort(sort[0]++); + setTitle(attachmentItem.getName()); + setType(attachmentItem.getType()); + setRid(attachmentItem.getRid()); + setCreatedAt(now); + } + }); + } + if (insertAttachments.size() > 0) { + attachmentService.saveBatch(insertAttachments); + } + } + return JsonResponse.success(); } @@ -203,28 +228,20 @@ public class CourseController { List categoryIds = courseService.getCategoryIdsByCourseId(course.getId()); List chapters = chapterService.getChaptersByCourseId(course.getId()); List hours = hourService.getHoursByCourseId(course.getId()); + List attachments = attachmentService.getAttachmentsByCourseId(course.getId()); + Map resourceMap = resourceService.chunks(attachments.stream().map(CourseAttachment::getRid).toList()) + .stream().collect(Collectors.toMap(Resource::getId, Resource::getUrl)); + attachments.forEach(courseAttachment -> { + courseAttachment.setUrl(resourceMap.get(courseAttachment.getRid())); + }); HashMap data = new HashMap<>(); data.put("course", course); data.put("dep_ids", depIds); // 已关联的部门 data.put("category_ids", categoryIds); // 已关联的分类 data.put("chapters", chapters); - data.put( - "hours", - hours.stream() - .filter( - courseHour -> - BackendConstant.RESOURCE_TYPE_VIDEO.equals( - courseHour.getType())) - .collect(Collectors.groupingBy(CourseHour::getChapterId))); - data.put( - "attachments", - hours.stream() - .filter( - courseHour -> - BackendConstant.RESOURCE_TYPE_ATTACHMENT.contains( - courseHour.getType())) - .collect(Collectors.groupingBy(CourseHour::getChapterId))); + data.put("hours", hours.stream().collect(Collectors.groupingBy(CourseHour::getChapterId))); + data.put("attachments", attachments); return JsonResponse.data(data); } diff --git a/src/main/java/xyz/playedu/api/domain/CourseAttachment.java b/src/main/java/xyz/playedu/api/domain/CourseAttachment.java index 9edb2ae..6eabb61 100644 --- a/src/main/java/xyz/playedu/api/domain/CourseAttachment.java +++ b/src/main/java/xyz/playedu/api/domain/CourseAttachment.java @@ -52,6 +52,9 @@ public class CourseAttachment implements Serializable { /** 资源id */ private Integer rid; + /** 资源url */ + private String url; + /** */ @JsonIgnore private Date createdAt; @@ -86,6 +89,9 @@ public class CourseAttachment implements Serializable { && (this.getRid() == null ? other.getRid() == null : this.getRid().equals(other.getRid())) + && (this.getUrl() == null + ? other.getUrl() == null + : this.getUrl().equals(other.getUrl())) && (this.getCreatedAt() == null ? other.getCreatedAt() == null : this.getCreatedAt().equals(other.getCreatedAt())); @@ -101,6 +107,7 @@ public class CourseAttachment implements Serializable { result = prime * result + ((getTitle() == null) ? 0 : getTitle().hashCode()); result = prime * result + ((getType() == null) ? 0 : getType().hashCode()); result = prime * result + ((getRid() == null) ? 0 : getRid().hashCode()); + result = prime * result + ((getUrl() == null) ? 0 : getUrl().hashCode()); result = prime * result + ((getCreatedAt() == null) ? 0 : getCreatedAt().hashCode()); return result; } @@ -117,6 +124,7 @@ public class CourseAttachment implements Serializable { sb.append(", title=").append(title); sb.append(", type=").append(type); sb.append(", rid=").append(rid); + sb.append(", url=").append(url); sb.append(", createdAt=").append(createdAt); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); diff --git a/src/main/java/xyz/playedu/api/listener/CourseDestroyListener.java b/src/main/java/xyz/playedu/api/listener/CourseDestroyListener.java index 7d45007..c9ece1e 100644 --- a/src/main/java/xyz/playedu/api/listener/CourseDestroyListener.java +++ b/src/main/java/xyz/playedu/api/listener/CourseDestroyListener.java @@ -20,6 +20,7 @@ import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; import xyz.playedu.api.event.CourseDestroyEvent; +import xyz.playedu.api.service.CourseAttachmentService; import xyz.playedu.api.service.CourseDepartmentService; import xyz.playedu.api.service.UserCourseHourRecordService; import xyz.playedu.api.service.UserCourseRecordService; @@ -41,6 +42,8 @@ public class CourseDestroyListener { @Autowired private UserCourseHourRecordService userCourseHourRecordService; + @Autowired private CourseAttachmentService courseAttachmentService; + @EventListener public void departmentRelateRemove(CourseDestroyEvent event) { courseDepartmentService.removeByCourseId(event.getCourseId()); @@ -51,6 +54,11 @@ public class CourseDestroyListener { courseCategoryService.removeByCourseId(event.getCourseId()); } + @EventListener + public void attachmentRelateRemove(CourseDestroyEvent event) { + courseAttachmentService.remove(event.getCourseId()); + } + @EventListener public void removeUserRecords(CourseDestroyEvent event) { userCourseRecordService.removeByCourseId(event.getCourseId()); diff --git a/src/main/java/xyz/playedu/api/request/backend/CourseAttachmentMultiRequest.java b/src/main/java/xyz/playedu/api/request/backend/CourseAttachmentMultiRequest.java new file mode 100644 index 0000000..2f4660a --- /dev/null +++ b/src/main/java/xyz/playedu/api/request/backend/CourseAttachmentMultiRequest.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 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.request.backend; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +@Data +public class CourseAttachmentMultiRequest { + @Data + public static class AttachmentItem { + private String title; + private Integer sort; + private String type; + private Integer rid; + } + + @NotNull(message = "attachments参数不存在") + private List attachments; +} diff --git a/src/main/java/xyz/playedu/api/request/backend/CourseAttachmentRequest.java b/src/main/java/xyz/playedu/api/request/backend/CourseAttachmentRequest.java new file mode 100644 index 0000000..c6cd80c --- /dev/null +++ b/src/main/java/xyz/playedu/api/request/backend/CourseAttachmentRequest.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 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.request.backend; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Data +public class CourseAttachmentRequest { + + @NotBlank(message = "请输入附件名称") + private String title; + + @NotNull(message = "sort参数不存在") + private Integer sort; + + @NotBlank(message = "请选择附件类型") + private String type; + + @NotNull(message = "rid参数不存在") + private Integer rid; +} diff --git a/src/main/java/xyz/playedu/api/request/backend/CourseAttachmentSortRequest.java b/src/main/java/xyz/playedu/api/request/backend/CourseAttachmentSortRequest.java new file mode 100644 index 0000000..83e36ac --- /dev/null +++ b/src/main/java/xyz/playedu/api/request/backend/CourseAttachmentSortRequest.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 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.request.backend; + +import lombok.Data; + +import java.util.List; + +@Data +public class CourseAttachmentSortRequest { + private List ids; +} diff --git a/src/main/java/xyz/playedu/api/request/backend/CourseRequest.java b/src/main/java/xyz/playedu/api/request/backend/CourseRequest.java index 16d35c1..7833f3b 100644 --- a/src/main/java/xyz/playedu/api/request/backend/CourseRequest.java +++ b/src/main/java/xyz/playedu/api/request/backend/CourseRequest.java @@ -72,6 +72,13 @@ public class CourseRequest { private List hours; } + @Data + public static class AttachmentItem { + private String name; + private String type; + private Integer rid; + } + // 格式 // [ // { @@ -100,4 +107,14 @@ public class CourseRequest { // ] @NotNull(message = "hours参数不存在") private List hours; + + // 格式 + // [ + // { + // 'name' => '附件名', + // 'type' => '附件类型', + // 'rid' => '资源id', + // }... + // ] + private List attachments; } diff --git a/src/main/java/xyz/playedu/api/service/CourseAttachmentService.java b/src/main/java/xyz/playedu/api/service/CourseAttachmentService.java index f054fd0..a6426fc 100644 --- a/src/main/java/xyz/playedu/api/service/CourseAttachmentService.java +++ b/src/main/java/xyz/playedu/api/service/CourseAttachmentService.java @@ -27,7 +27,7 @@ public interface CourseAttachmentService extends IService { void update(CourseAttachment courseAttachment, Integer sort, String title); - List getAttachmentByCourseId(Integer courseId); + List getAttachmentsByCourseId(Integer courseId); CourseAttachment create( Integer courseId, @@ -42,7 +42,7 @@ public interface CourseAttachmentService extends IService { void updateSort(List ids, Integer cid); - List getRidsByCourseId(Integer courseId, String type); + List getRidsByCourseId(Integer courseId); List chunk(List attachmentIds); } diff --git a/src/main/java/xyz/playedu/api/service/impl/internal/CourseAttachmentServiceImpl.java b/src/main/java/xyz/playedu/api/service/impl/internal/CourseAttachmentServiceImpl.java index eca5c54..744bc11 100644 --- a/src/main/java/xyz/playedu/api/service/impl/internal/CourseAttachmentServiceImpl.java +++ b/src/main/java/xyz/playedu/api/service/impl/internal/CourseAttachmentServiceImpl.java @@ -53,7 +53,7 @@ public class CourseAttachmentServiceImpl extends ServiceImpl getAttachmentByCourseId(Integer courseId) { + public List getAttachmentsByCourseId(Integer courseId) { return list(query().getWrapper().eq("course_id", courseId).orderByAsc("sort")); } @@ -108,8 +108,8 @@ public class CourseAttachmentServiceImpl extends ServiceImpl getRidsByCourseId(Integer courseId, String type) { - return list(query().getWrapper().eq("course_id", courseId).eq("type", type)).stream() + public List getRidsByCourseId(Integer courseId) { + return list(query().getWrapper().eq("course_id", courseId)).stream() .map(CourseAttachment::getRid) .toList(); }