diff --git a/app/api/playedu-api/src/main/java/xyz/playedu/api/controller/backend/jc/BookChapterController.java b/app/api/playedu-api/src/main/java/xyz/playedu/api/controller/backend/jc/BookChapterController.java index 0073fb3..a7fdf79 100644 --- a/app/api/playedu-api/src/main/java/xyz/playedu/api/controller/backend/jc/BookChapterController.java +++ b/app/api/playedu-api/src/main/java/xyz/playedu/api/controller/backend/jc/BookChapterController.java @@ -4,6 +4,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import xyz.playedu.common.types.JsonResponse; import xyz.playedu.jc.domain.BookChapter; +import xyz.playedu.jc.domain.dto.ChapterSortDTO; +import xyz.playedu.jc.domain.vo.ChapterTreeVO; import xyz.playedu.jc.service.IBookChapterService; import java.util.List; @@ -11,6 +13,8 @@ import java.util.Optional; /** * 教材章节 后台管理接口 + * 对应需求文档: + * 1.1 章节管理:新增/编辑/删除/拖拽排序/三级目录树 */ @RestController @RequestMapping("/textbook/chapter") @@ -19,42 +23,50 @@ public class BookChapterController { @Autowired private IBookChapterService bookChapterService; - /** - * 根据教材ID获取章节列表(平铺 + 已排序) - * 你可以在前端把它组装成树,也可以后面在后端加树形 VO。 - */ + /** 获取某教材的章节平铺列表 */ @GetMapping("/list") public JsonResponse list(@RequestParam("bookId") Integer bookId) { - List chapters = bookChapterService.getByBookId(bookId); - List safeList = Optional.ofNullable(chapters).orElseGet(java.util.Collections::emptyList); - return JsonResponse.data(safeList); + List chapters = bookChapterService.listByBookId(bookId); + return JsonResponse.data(chapters); } - /** - * 新增章节 - */ + /** 获取章节树(最多三级目录) */ + @GetMapping("/tree") + public JsonResponse tree(@RequestParam("bookId") Integer bookId) { + List tree = bookChapterService.treeByBookId(bookId); + return JsonResponse.data(tree); + } + + /** 新增章节 */ @PostMapping public JsonResponse create(@RequestBody BookChapter chapter) { - // 可在这里补充 creator / tenantId 等信息 + // 默认 parentId 为 0 + if (chapter.getParentId() == null) { + chapter.setParentId(0); + } bookChapterService.save(chapter); return JsonResponse.success(); } - /** - * 修改章节 - */ + /** 修改章节 */ @PutMapping public JsonResponse update(@RequestBody BookChapter chapter) { bookChapterService.updateById(chapter); return JsonResponse.success(); } - /** - * 删除章节 - */ + /** 删除章节(可在 Service 里做子节点/内容校验) */ @DeleteMapping("/{id}") public JsonResponse delete(@PathVariable("id") Integer id) { - bookChapterService.removeById(id); + bookChapterService.removeChapter(id); + return JsonResponse.success(); + } + + /** 拖拽排序 / 批量调整层级 */ + @PostMapping("/sort") + public JsonResponse sort(@RequestParam("bookId") Integer bookId, + @RequestBody List sorts) { + bookChapterService.sort(bookId, sorts); return JsonResponse.success(); } } \ No newline at end of file diff --git a/app/api/playedu-api/src/main/java/xyz/playedu/api/controller/backend/jc/ChapterContentController.java b/app/api/playedu-api/src/main/java/xyz/playedu/api/controller/backend/jc/ChapterContentController.java index 1bbaec3..8316f3e 100644 --- a/app/api/playedu-api/src/main/java/xyz/playedu/api/controller/backend/jc/ChapterContentController.java +++ b/app/api/playedu-api/src/main/java/xyz/playedu/api/controller/backend/jc/ChapterContentController.java @@ -7,6 +7,8 @@ import xyz.playedu.jc.domain.ChapterContent; import xyz.playedu.jc.service.IChapterContentService; /** + * + * todo * 章节内容 后台接口 */ @RestController diff --git a/app/api/playedu-api/src/main/java/xyz/playedu/api/controller/backend/jc/JCResourceController.java b/app/api/playedu-api/src/main/java/xyz/playedu/api/controller/backend/jc/JCResourceController.java index 3aebafa..d45e41f 100644 --- a/app/api/playedu-api/src/main/java/xyz/playedu/api/controller/backend/jc/JCResourceController.java +++ b/app/api/playedu-api/src/main/java/xyz/playedu/api/controller/backend/jc/JCResourceController.java @@ -1,9 +1,11 @@ package xyz.playedu.api.controller.backend.jc; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import xyz.playedu.common.types.JsonResponse; import xyz.playedu.jc.domain.JCResource; +import xyz.playedu.jc.domain.dto.ResourcePageRequestDTO; import xyz.playedu.jc.service.JCIResourceService; import java.util.List; @@ -31,21 +33,33 @@ public class JCResourceController { return JsonResponse.data(list); } + + + /** 新增资源(,) */ @PostMapping - public JsonResponse create(@RequestBody JCResource JCResource) { - resourceService.save(JCResource); + public JsonResponse create(@RequestBody JCResource resource) { + resourceService.save(resource); return JsonResponse.success(); } + /** 编辑/替换资源(可以修改URL、封面、名称、章节绑定等) */ @PutMapping - public JsonResponse update(@RequestBody JCResource JCResource) { - resourceService.updateById(JCResource); + public JsonResponse update(@RequestBody JCResource resource) { + resourceService.updateById(resource); return JsonResponse.success(); } + /** 删除资源 */ @DeleteMapping("/{id}") public JsonResponse delete(@PathVariable("id") Integer id) { resourceService.removeById(id); return JsonResponse.success(); } + + /** 资源详情 */ + @GetMapping("/{id}") + public JsonResponse detail(@PathVariable("id") Integer id) { + JCResource resource = resourceService.getById(id); + return JsonResponse.data(resource); + } } \ No newline at end of file diff --git a/app/api/playedu-course/src/main/java/xyz/playedu/jc/domain/dto/ChapterContentSaveDTO.java b/app/api/playedu-course/src/main/java/xyz/playedu/jc/domain/dto/ChapterContentSaveDTO.java new file mode 100644 index 0000000..587a5ca --- /dev/null +++ b/app/api/playedu-course/src/main/java/xyz/playedu/jc/domain/dto/ChapterContentSaveDTO.java @@ -0,0 +1,24 @@ +package xyz.playedu.jc.domain.dto; + +import lombok.Data; + +import java.util.List; + +/** + * 章节内容保存 DTO + */ +@Data +public class ChapterContentSaveDTO { + + /** 富文本内容 */ + private String content; + + /** 关联资源ID列表 */ + private List resourceIds; + + /** 关联知识点ID列表(预留,) */ + private List knowledgeIds; + + /** 重点标记(如是否为重点章节) */ + private Boolean highlight; +} \ No newline at end of file diff --git a/app/api/playedu-course/src/main/java/xyz/playedu/jc/domain/dto/ChapterSortDTO.java b/app/api/playedu-course/src/main/java/xyz/playedu/jc/domain/dto/ChapterSortDTO.java new file mode 100644 index 0000000..119e053 --- /dev/null +++ b/app/api/playedu-course/src/main/java/xyz/playedu/jc/domain/dto/ChapterSortDTO.java @@ -0,0 +1,18 @@ +package xyz.playedu.jc.domain.dto; + +import lombok.Data; + +/** + * 章节排序 / 移动 DTO + */ +@Data +public class ChapterSortDTO { + + private Integer id; + + /** 新的父节点ID */ + private Integer parentId; + + /** 新的排序值 */ + private Integer sort; +} \ No newline at end of file diff --git a/app/api/playedu-course/src/main/java/xyz/playedu/jc/domain/dto/ResourcePageRequestDTO.java b/app/api/playedu-course/src/main/java/xyz/playedu/jc/domain/dto/ResourcePageRequestDTO.java new file mode 100644 index 0000000..6ad751a --- /dev/null +++ b/app/api/playedu-course/src/main/java/xyz/playedu/jc/domain/dto/ResourcePageRequestDTO.java @@ -0,0 +1,19 @@ +package xyz.playedu.jc.domain.dto; + + +import lombok.Data; + +/** + * 资源分页查询条件 + */ +@Data +public class ResourcePageRequestDTO { + + private Integer bookId; + private Integer chapterId; + private String type; // video/ppt/img... + private String name; // 模糊搜索资源名 + + private Long page = 1L; + private Long size = 20L; +} \ No newline at end of file diff --git a/app/api/playedu-course/src/main/java/xyz/playedu/jc/domain/vo/ChapterTreeVO.java b/app/api/playedu-course/src/main/java/xyz/playedu/jc/domain/vo/ChapterTreeVO.java new file mode 100644 index 0000000..bc3205b --- /dev/null +++ b/app/api/playedu-course/src/main/java/xyz/playedu/jc/domain/vo/ChapterTreeVO.java @@ -0,0 +1,22 @@ +package xyz.playedu.jc.domain.vo; + +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * 章节树节点 VO + */ +@Data +public class ChapterTreeVO { + + private Integer id; + private Integer bookId; + private Integer parentId; + private String name; + private Integer sort; + private String chapterCode; + + private List children = new ArrayList<>(); +} \ No newline at end of file diff --git a/app/api/playedu-course/src/main/java/xyz/playedu/jc/service/IBookChapterService.java b/app/api/playedu-course/src/main/java/xyz/playedu/jc/service/IBookChapterService.java index 7b636ec..036539e 100644 --- a/app/api/playedu-course/src/main/java/xyz/playedu/jc/service/IBookChapterService.java +++ b/app/api/playedu-course/src/main/java/xyz/playedu/jc/service/IBookChapterService.java @@ -2,6 +2,8 @@ package xyz.playedu.jc.service; import com.baomidou.mybatisplus.extension.service.IService; import xyz.playedu.jc.domain.BookChapter; +import xyz.playedu.jc.domain.dto.ChapterSortDTO; +import xyz.playedu.jc.domain.vo.ChapterTreeVO; import java.util.List; @@ -14,4 +16,16 @@ public interface IBookChapterService extends IService { * 根据教材ID获取章节列表(已排序) */ List getByBookId(Integer bookId); + + /** 根据教材ID获取平铺的章节列表(已按 parentId + sort 排好序) */ + List listByBookId(Integer bookId); + + /** 构建章节树 */ + List treeByBookId(Integer bookId); + + /** 拖拽移动章节 / 批量调整 parentId + sort */ + void sort(Integer bookId, List sorts); + + /** 删除章节时做子节点/内容校验(需要的话) */ + void removeChapter(Integer id); } \ No newline at end of file diff --git a/app/api/playedu-course/src/main/java/xyz/playedu/jc/service/impl/BookChapterServiceImpl.java b/app/api/playedu-course/src/main/java/xyz/playedu/jc/service/impl/BookChapterServiceImpl.java index d1755db..4578119 100644 --- a/app/api/playedu-course/src/main/java/xyz/playedu/jc/service/impl/BookChapterServiceImpl.java +++ b/app/api/playedu-course/src/main/java/xyz/playedu/jc/service/impl/BookChapterServiceImpl.java @@ -4,10 +4,15 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.stereotype.Service; import xyz.playedu.jc.domain.BookChapter; +import xyz.playedu.jc.domain.dto.ChapterSortDTO; +import xyz.playedu.jc.domain.vo.ChapterTreeVO; import xyz.playedu.jc.mapper.BookChapterMapper; import xyz.playedu.jc.service.IBookChapterService; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * 教材章节 Service 实现 @@ -27,5 +32,67 @@ public class BookChapterServiceImpl return list(wrapper); } - // 如果后面你要做拖拽排序、批量更新等,可以在这里继续加方法 + @Override + public List listByBookId(Integer bookId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(BookChapter::getBookId, bookId) + .orderByAsc(BookChapter::getParentId) + .orderByAsc(BookChapter::getSort) + .orderByAsc(BookChapter::getId); + return list(wrapper); + } + + @Override + public List treeByBookId(Integer bookId) { + List chapters = listByBookId(bookId); + Map map = new HashMap<>(); + List roots = new ArrayList<>(); + + for (BookChapter c : chapters) { + ChapterTreeVO vo = new ChapterTreeVO(); + vo.setId(c.getId()); + vo.setBookId(c.getBookId()); + vo.setParentId(c.getParentId()); + vo.setName(c.getName()); + vo.setSort(c.getSort()); + vo.setChapterCode(c.getChapterCode()); + map.put(vo.getId(), vo); + } + + for (ChapterTreeVO vo : map.values()) { + if (vo.getParentId() == null || vo.getParentId() == 0) { + roots.add(vo); + } else { + ChapterTreeVO parent = map.get(vo.getParentId()); + if (parent != null) { + parent.getChildren().add(vo); + } else { + roots.add(vo); // 避免脏数据直接丢失 + } + } + } + return roots; + } + + @Override + public void sort(Integer bookId, List sorts) { + if (sorts == null || sorts.isEmpty()) { + return; + } + List updates = new ArrayList<>(); + for (ChapterSortDTO dto : sorts) { + BookChapter entity = new BookChapter(); + entity.setId(dto.getId()); + entity.setParentId(dto.getParentId()); + entity.setSort(dto.getSort()); + updates.add(entity); + } + updateBatchById(updates); + } + + @Override + public void removeChapter(Integer id) { + //todo 校验是否有子节点 / 内容 + removeById(id); + } } \ No newline at end of file