From c5c134fbb3652992c9e1d7fab156e42337a0de75 Mon Sep 17 00:00:00 2001 From: wangys <3401275564@qq.com> Date: Tue, 28 Oct 2025 19:04:35 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=86=E5=9B=BE=E4=BF=AE=E6=94=B9=E3=80=81?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../.bin/mysql/sql/createTableResource.sql | 4 +- .../xyzh/api/news/banner/BannerService.java | 13 +- .../xyzh/api/study/course/CourseService.java | 31 +- .../xyzh/common/dto/resource/TbBanner.java | 11 + .../java/org/xyzh/common/vo/ChapterVO.java | 35 - .../java/org/xyzh/common/vo/CourseItemVO.java | 589 +++++++ .../java/org/xyzh/common/vo/CourseVO.java | 39 - .../org/xyzh/crontab/enums/TaskEnums.java | 37 + .../xyzh/crontab/task/newsTask/NewsTask.java | 12 + .../news/controller/BannerController.java | 47 +- .../org/xyzh/news/mapper/BannerMapper.java | 23 +- .../service/impl/NCBannerServiceImpl.java | 81 +- .../news/service/impl/NCTagServiceImpl.java | 32 +- .../main/resources/mapper/BannerMapper.xml | 68 +- .../study/controller/CourseController.java | 22 +- .../controller/LearningTaskController.java | 4 +- .../org/xyzh/study/mapper/CourseMapper.java | 11 + .../xyzh/study/mapper/CourseNodeMapper.java | 11 + .../service/impl/SCCourseServiceImpl.java | 293 ++-- .../impl/SCLearningTaskServiceImpl.java | 25 +- .../main/resources/mapper/CourseMapper.xml | 21 +- .../resources/mapper/CourseNodeMapper.xml | 58 + .../resources/mapper/LearningRecordMapper.xml | 13 +- .../main/resources/mapper/TaskUserMapper.xml | 8 +- schoolNewsWeb/public/img/admin/agent.svg | 8 + schoolNewsWeb/public/img/admin/course.svg | 4 + schoolNewsWeb/public/img/admin/logs.svg | 5 + schoolNewsWeb/public/img/admin/overview.svg | 6 + schoolNewsWeb/public/img/admin/settings.svg | 4 + schoolNewsWeb/public/img/admin/study.svg | 5 + schoolNewsWeb/public/img/admin/usermange.svg | 6 + schoolNewsWeb/src/apis/homepage/banner.ts | 41 - schoolNewsWeb/src/apis/homepage/index.ts | 1 - schoolNewsWeb/src/apis/resource/banner.ts | 124 ++ schoolNewsWeb/src/apis/resource/index.ts | 4 +- .../src/apis/resource/resourceCategory.ts | 117 -- schoolNewsWeb/src/apis/study/course.ts | 34 +- schoolNewsWeb/src/apis/study/learning-task.ts | 2 +- schoolNewsWeb/src/assets/imgs/article.svg | 4 + schoolNewsWeb/src/assets/imgs/book-read.svg | 4 + schoolNewsWeb/src/assets/imgs/clock.svg | 4 + schoolNewsWeb/src/assets/imgs/edit.svg | 5 + schoolNewsWeb/src/assets/imgs/eye.svg | 9 + schoolNewsWeb/src/assets/imgs/plus.svg | 5 + schoolNewsWeb/src/assets/imgs/time-line.svg | 5 + schoolNewsWeb/src/assets/imgs/trashbin.svg | 8 + schoolNewsWeb/src/assets/imgs/video.svg | 4 + .../src/components/base/MenuItem.vue | 35 +- schoolNewsWeb/src/layouts/SidebarLayout.vue | 18 +- schoolNewsWeb/src/types/resource/index.ts | 7 +- schoolNewsWeb/src/types/study/index.ts | 73 +- schoolNewsWeb/src/utils/routeUtils.ts | 2 - schoolNewsWeb/src/views/admin/AdminLayout.vue | 172 ++ schoolNewsWeb/src/views/admin/index.ts | 1 + .../achievement/AchievementManagementView.vue | 32 +- .../views/admin/manage/ai/AIConfigView.vue | 113 +- .../admin/manage/ai/AIManagementView.vue | 29 +- .../manage/ai/KnowledgeManagementView.vue | 99 +- .../manage/content/BannerManagementView.vue | 1517 ++++++++++++++++- .../manage/content/ColumnManagementView.vue | 59 +- .../manage/content/ContentManagementView.vue | 51 +- .../manage/content/TagManagementView.vue | 356 ++-- .../manage/crontab/LogManagementView.vue | 413 ++--- .../admin/manage/crontab/NewsCrawlerView.vue | 517 +++--- .../manage/crontab/TaskManagementView.vue | 455 ++--- .../views/admin/manage/logs/LoginLogsView.vue | 103 +- .../admin/manage/logs/OperationLogsView.vue | 126 +- .../admin/manage/logs/SystemConfigView.vue | 145 +- .../admin/manage/logs/SystemLogsView.vue | 35 +- .../manage/resource/ArticleManagementView.vue | 38 +- .../admin/manage/resource/DataRecordsView.vue | 36 +- .../resource/ResourceManagementView.vue | 45 +- .../manage/study/CourseManagementView.vue | 41 +- .../manage/study/StudyManagementView.vue | 25 +- .../admin/manage/study/StudyRecordsView.vue | 121 +- .../admin/manage/study/TaskManageView.vue | 30 +- .../admin/manage/system/DeptManageView.vue | 30 +- .../admin/manage/system/MenuManageView.vue | 30 +- .../system/ModulePermissionManageView.vue | 34 +- .../admin/manage/system/RoleManageView.vue | 32 +- .../admin/manage/system/UserManageView.vue | 704 ++++---- .../views/public/article/ArticleAddView.vue | 332 +--- .../views/public/article/ArticleShowView.vue | 880 +--------- .../public/article/components/ArticleAdd.vue | 332 ++++ .../public/article/components/ArticleShow.vue | 887 ++++++++++ .../views/public/article/components/index.ts | 3 + .../src/views/public/article/index.ts | 3 +- .../src/views/public/banner/BannerCard.vue | 15 +- .../public/course/components/CourseAdd.vue | 180 +- .../public/course/components/CourseDetail.vue | 770 ++++++--- .../course/components/CourseLearning.vue | 393 ++--- .../public/course/components/CourseList.vue | 2 +- .../src/views/user/home/HomeView.vue | 82 +- .../resource-center/ResourceCenterView.vue | 2 +- .../components/ResourceArticle.vue | 5 +- .../user/study-plan/CourseDetailView.vue | 9 +- 96 files changed, 7122 insertions(+), 4194 deletions(-) delete mode 100644 schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/ChapterVO.java create mode 100644 schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/CourseItemVO.java delete mode 100644 schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/CourseVO.java create mode 100644 schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/enums/TaskEnums.java create mode 100644 schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/task/newsTask/NewsTask.java create mode 100644 schoolNewsWeb/public/img/admin/agent.svg create mode 100644 schoolNewsWeb/public/img/admin/course.svg create mode 100644 schoolNewsWeb/public/img/admin/logs.svg create mode 100644 schoolNewsWeb/public/img/admin/overview.svg create mode 100644 schoolNewsWeb/public/img/admin/settings.svg create mode 100644 schoolNewsWeb/public/img/admin/study.svg create mode 100644 schoolNewsWeb/public/img/admin/usermange.svg delete mode 100644 schoolNewsWeb/src/apis/homepage/banner.ts create mode 100644 schoolNewsWeb/src/apis/resource/banner.ts delete mode 100644 schoolNewsWeb/src/apis/resource/resourceCategory.ts create mode 100644 schoolNewsWeb/src/assets/imgs/article.svg create mode 100644 schoolNewsWeb/src/assets/imgs/book-read.svg create mode 100644 schoolNewsWeb/src/assets/imgs/clock.svg create mode 100644 schoolNewsWeb/src/assets/imgs/edit.svg create mode 100644 schoolNewsWeb/src/assets/imgs/eye.svg create mode 100644 schoolNewsWeb/src/assets/imgs/plus.svg create mode 100644 schoolNewsWeb/src/assets/imgs/time-line.svg create mode 100644 schoolNewsWeb/src/assets/imgs/trashbin.svg create mode 100644 schoolNewsWeb/src/assets/imgs/video.svg create mode 100644 schoolNewsWeb/src/views/admin/AdminLayout.vue create mode 100644 schoolNewsWeb/src/views/admin/index.ts create mode 100644 schoolNewsWeb/src/views/public/article/components/ArticleAdd.vue create mode 100644 schoolNewsWeb/src/views/public/article/components/ArticleShow.vue create mode 100644 schoolNewsWeb/src/views/public/article/components/index.ts diff --git a/schoolNewsServ/.bin/mysql/sql/createTableResource.sql b/schoolNewsServ/.bin/mysql/sql/createTableResource.sql index d32d08a..2ee047a 100644 --- a/schoolNewsServ/.bin/mysql/sql/createTableResource.sql +++ b/schoolNewsServ/.bin/mysql/sql/createTableResource.sql @@ -37,7 +37,8 @@ CREATE TABLE `tb_resource` ( -- Banner管理表 DROP TABLE IF EXISTS `tb_banner`; CREATE TABLE `tb_banner` ( - `id` VARCHAR(50) NOT NULL COMMENT 'Banner ID', + `id` VARCHAR(50) NOT NULL COMMENT 'ID', + `banner_id` VARCHAR(50) NOT NULL COMMENT 'Banner ID', `title` VARCHAR(255) NOT NULL COMMENT 'Banner标题', `image_url` VARCHAR(500) NOT NULL COMMENT 'Banner图片URL', `link_type` INT(4) DEFAULT 1 COMMENT '链接类型(1资源 2课程 3外部链接)', @@ -52,6 +53,7 @@ CREATE TABLE `tb_banner` ( `delete_time` TIMESTAMP NULL DEFAULT NULL COMMENT '删除时间', `deleted` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否删除', PRIMARY KEY (`id`), + UNIQUE KEY `uk_banner_id` (`banner_id`), KEY `idx_order` (`order_num`), KEY `idx_status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='Banner表'; diff --git a/schoolNewsServ/api/api-news/src/main/java/org/xyzh/api/news/banner/BannerService.java b/schoolNewsServ/api/api-news/src/main/java/org/xyzh/api/news/banner/BannerService.java index 846450c..72a3047 100644 --- a/schoolNewsServ/api/api-news/src/main/java/org/xyzh/api/news/banner/BannerService.java +++ b/schoolNewsServ/api/api-news/src/main/java/org/xyzh/api/news/banner/BannerService.java @@ -1,6 +1,7 @@ package org.xyzh.api.news.banner; import org.xyzh.common.core.domain.ResultDomain; +import org.xyzh.common.core.page.PageParam; import org.xyzh.common.dto.resource.TbBanner; import java.util.List; @@ -21,7 +22,7 @@ public interface BannerService { * @author yslg * @since 2025-10-15 */ - ResultDomain getBannerList(Integer status); + ResultDomain getBannerList(TbBanner filter); /** * @description 根据ID获取Banner详情 @@ -30,7 +31,7 @@ public interface BannerService { * @author yslg * @since 2025-10-15 */ - ResultDomain getBannerById(String bannerID); + ResultDomain getBannerPage(PageParam pageParam,TbBanner filter); /** * @description 创建Banner @@ -96,4 +97,12 @@ public interface BannerService { * @since 2025-10-15 */ ResultDomain batchUpdateBannerOrder(java.util.Map bannerOrders); + + /** + * @description 获取首页横幅列表 + * @return ResultDomain 首页横幅列表 + * @author yslg + * @since 2025-10-15 + */ + ResultDomain selectHomeBanners(); } diff --git a/schoolNewsServ/api/api-study/src/main/java/org/xyzh/api/study/course/CourseService.java b/schoolNewsServ/api/api-study/src/main/java/org/xyzh/api/study/course/CourseService.java index 6d0ccfd..0ee31fa 100644 --- a/schoolNewsServ/api/api-study/src/main/java/org/xyzh/api/study/course/CourseService.java +++ b/schoolNewsServ/api/api-study/src/main/java/org/xyzh/api/study/course/CourseService.java @@ -1,15 +1,11 @@ package org.xyzh.api.study.course; import org.xyzh.common.core.domain.ResultDomain; -import org.xyzh.common.core.page.PageParam; import org.xyzh.common.core.page.PageRequest; import org.xyzh.common.dto.study.TbCourse; import org.xyzh.common.dto.study.TbCourseChapter; import org.xyzh.common.dto.study.TbCourseNode; -import org.xyzh.common.dto.study.TbLearningRecord; -import org.xyzh.common.vo.CourseVO; - -import java.util.List; +import org.xyzh.common.vo.CourseItemVO; /** * @description 课程服务接口 @@ -41,29 +37,29 @@ public interface CourseService { /** * @description 根据ID获取课程详情 * @param courseID 课程ID - * @return ResultDomain 课程详情 + * @return ResultDomain 课程详情 * @author yslg * @since 2025-10-15 */ - ResultDomain getCourseById(String courseID); + ResultDomain getCourseById(String courseID); /** * @description 创建课程 - * @param course 课程信息 - * @return ResultDomain 创建结果 + * @param courseItemVO 课程信息 + * @return ResultDomain 创建结果 * @author yslg * @since 2025-10-15 */ - ResultDomain createCourse(CourseVO courseVO); + ResultDomain createCourse(CourseItemVO courseItemVO); /** * @description 更新课程 - * @param courseVO 课程信息 - * @return ResultDomain 更新结果 + * @param courseItemVO 课程信息 + * @return ResultDomain 更新结果 * @author yslg * @since 2025-10-15 */ - ResultDomain updateCourse(CourseVO courseVO); + ResultDomain updateCourse(CourseItemVO courseItemVO); /** * @description 删除课程 @@ -185,4 +181,13 @@ public interface CourseService { * @since 2025-10-22 */ ResultDomain deleteChapterNode(String nodeID); + + /** + * @description 获取课程进度 + * @param courseID 课程ID + * @return ResultDomain 课程进度 + * @author yslg + * @since 2025-10-28 + */ + ResultDomain getCourseProgress(String courseID); } diff --git a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/resource/TbBanner.java b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/resource/TbBanner.java index 5dde3f3..a921ba1 100644 --- a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/resource/TbBanner.java +++ b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/resource/TbBanner.java @@ -12,6 +12,8 @@ import org.xyzh.common.dto.BaseDTO; public class TbBanner extends BaseDTO { private static final long serialVersionUID = 1L; + + private String bannerID; /** * @description Banner标题 @@ -58,6 +60,14 @@ public class TbBanner extends BaseDTO { */ private String updater; + public String getBannerID() { + return bannerID; + } + + public void setBannerID(String bannerID) { + this.bannerID = bannerID; + } + public String getTitle() { return title; } @@ -134,6 +144,7 @@ public class TbBanner extends BaseDTO { public String toString() { return "TbBanner{" + "id=" + getID() + + ", bannerID='" + bannerID + '\'' + ", title='" + title + '\'' + ", imageUrl='" + imageUrl + '\'' + ", linkType=" + linkType + diff --git a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/ChapterVO.java b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/ChapterVO.java deleted file mode 100644 index 1510cd4..0000000 --- a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/ChapterVO.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.xyzh.common.vo; - -import org.xyzh.common.dto.BaseDTO; -import org.xyzh.common.dto.study.TbCourseNode; -import org.xyzh.common.dto.study.TbCourseChapter; - -import java.util.ArrayList; -import java.util.List; - -public class ChapterVO extends BaseDTO{ - private static final long serialVersionUID = 1L; - - private TbCourseChapter chapter; - private List nodes; - - public TbCourseChapter getChapter() { - return chapter; - } - public void setChapter(TbCourseChapter chapter) { - this.chapter = chapter; - } - public List getNodes() { - return nodes; - } - public void setNodes(List nodes) { - this.nodes = nodes; - } - - public void addNode(TbCourseNode node) { - if (nodes == null) { - nodes = new ArrayList<>(); - } - nodes.add(node); - } -} diff --git a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/CourseItemVO.java b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/CourseItemVO.java new file mode 100644 index 0000000..ea5e22d --- /dev/null +++ b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/CourseItemVO.java @@ -0,0 +1,589 @@ +package org.xyzh.common.vo; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import org.xyzh.common.dto.BaseDTO; +import org.xyzh.common.dto.study.TbCourse; +import org.xyzh.common.dto.study.TbCourseChapter; +import org.xyzh.common.dto.study.TbCourseNode; +import org.xyzh.common.dto.study.TbLearningRecord; + +/** + * @description 课程项目VO - 包含课程和学习记录的关键信息 + * @filename CourseItemVO.java + * @author yslg + * @copyright xyzh + * @since 2025-10-28 + */ +public class CourseItemVO extends BaseDTO { + + private static final long serialVersionUID = 1L; + + // ========== 课程基本信息 ========== + /** + * @description 课程ID + */ + private String courseID; + + /** + * @description 课程名称 + */ + private String name; + + /** + * @description 课程封面图片 + */ + private String coverImage; + + /** + * @description 课程描述 + */ + private String description; + + /** + * @description 课程时长(分钟) + */ + private Integer duration; + + /** + * @description 授课老师 + */ + private String teacher; + + /** + * @description 课程状态(0未上线 1已上线 2已下架) + */ + private Integer status; + + /** + * @description 浏览次数 + */ + private Integer viewCount; + + /** + * @description 学习人数 + */ + private Integer learnCount; + + /** + * @description 课程创建时间 + */ + private Date createTime; + + // ========== 学习记录信息 ========== + /** + * @description 学习记录ID + */ + private String recordID; + + /** + * @description 学习进度(0-100) + */ + private BigDecimal progress; + + /** + * @description 是否完成 + */ + private Boolean isComplete; + + /** + * @description 学习时长(秒) + */ + private Integer learningDuration; + + /** + * @description 最后学习时间 + */ + private Date lastLearnTime; + + /** + * @description 完成时间 + */ + private Date completeTime; + + // ========== 章节节点信息 ========== + /** + * @description 章节ID(当此对象代表章节或节点时使用) + */ + private String chapterID; + + /** + * @description 节点ID(当此对象代表节点时使用) + */ + private String nodeID; + + /** + * @description 父级ID(章节的父章节ID) + */ + private String parentID; + + /** + * @description 节点类型(1视频 2文档 3音频 4图片 5链接) + */ + private Integer nodeType; + + /** + * @description 节点内容(富文本内容) + */ + private String content; + + /** + * @description 视频URL + */ + private String videoUrl; + + /** + * @description 资源ID + */ + private String resourceID; + + /** + * @description 排序号 + */ + private Integer orderNum; + + /** + * @description 是否必修(1必修 0选修) + */ + private Integer isRequired; + + // ========== 层级结构 ========== + /** + * @description 章节列表(课程的章节列表) + */ + private List chapters; + + /** + * @description 章节节点映射(key: chapterID, value: 该章节下的节点列表) + */ + private Map> chapterNodes; + + // ========== Getters and Setters ========== + + public String getCourseID() { + return courseID; + } + + public void setCourseID(String courseID) { + this.courseID = courseID; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCoverImage() { + return coverImage; + } + + public void setCoverImage(String coverImage) { + this.coverImage = coverImage; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Integer getDuration() { + return duration; + } + + public void setDuration(Integer duration) { + this.duration = duration; + } + + public String getTeacher() { + return teacher; + } + + public void setTeacher(String teacher) { + this.teacher = teacher; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Integer getViewCount() { + return viewCount; + } + + public void setViewCount(Integer viewCount) { + this.viewCount = viewCount; + } + + public Integer getLearnCount() { + return learnCount; + } + + public void setLearnCount(Integer learnCount) { + this.learnCount = learnCount; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public String getRecordID() { + return recordID; + } + + public void setRecordID(String recordID) { + this.recordID = recordID; + } + + public BigDecimal getProgress() { + return progress; + } + + public void setProgress(BigDecimal progress) { + this.progress = progress; + } + + public Boolean getIsComplete() { + return isComplete; + } + + public void setIsComplete(Boolean isComplete) { + this.isComplete = isComplete; + } + + public Integer getLearningDuration() { + return learningDuration; + } + + public void setLearningDuration(Integer learningDuration) { + this.learningDuration = learningDuration; + } + + public Date getLastLearnTime() { + return lastLearnTime; + } + + public void setLastLearnTime(Date lastLearnTime) { + this.lastLearnTime = lastLearnTime; + } + + public Date getCompleteTime() { + return completeTime; + } + + public void setCompleteTime(Date completeTime) { + this.completeTime = completeTime; + } + + public String getChapterID() { + return chapterID; + } + + public void setChapterID(String chapterID) { + this.chapterID = chapterID; + } + + public String getNodeID() { + return nodeID; + } + + public void setNodeID(String nodeID) { + this.nodeID = nodeID; + } + + public String getParentID() { + return parentID; + } + + public void setParentID(String parentID) { + this.parentID = parentID; + } + + public Integer getNodeType() { + return nodeType; + } + + public void setNodeType(Integer nodeType) { + this.nodeType = nodeType; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getVideoUrl() { + return videoUrl; + } + + public void setVideoUrl(String videoUrl) { + this.videoUrl = videoUrl; + } + + public String getResourceID() { + return resourceID; + } + + public void setResourceID(String resourceID) { + this.resourceID = resourceID; + } + + public Integer getOrderNum() { + return orderNum; + } + + public void setOrderNum(Integer orderNum) { + this.orderNum = orderNum; + } + + public Integer getIsRequired() { + return isRequired; + } + + public void setIsRequired(Integer isRequired) { + this.isRequired = isRequired; + } + + public List getChapters() { + return chapters; + } + + public void setChapters(List chapters) { + this.chapters = chapters; + } + + public Map> getChapterNodes() { + return chapterNodes; + } + + public void setChapterNodes(Map> chapterNodes) { + this.chapterNodes = chapterNodes; + } + + // ========== 类型转换方法 ========== + + /** + * @description 转换为课程实体对象 + * @return TbCourse 课程实体 + */ + public TbCourse toCourse() { + TbCourse course = new TbCourse(); + course.setID(this.getID()); + course.setCourseID(this.courseID); + course.setName(this.name); + course.setCoverImage(this.coverImage); + course.setDescription(this.description); + course.setDuration(this.duration); + course.setTeacher(this.teacher); + course.setStatus(this.status); + course.setViewCount(this.viewCount); + course.setLearnCount(this.learnCount); + course.setCreateTime(this.createTime); + return course; + } + + /** + * @description 从课程实体创建CourseItemVO + * @param course 课程实体 + * @return CourseItemVO + */ + public static CourseItemVO fromCourse(TbCourse course) { + if (course == null) { + return null; + } + + CourseItemVO vo = new CourseItemVO(); + vo.setID(course.getID()); + vo.setCourseID(course.getCourseID()); + vo.setName(course.getName()); + vo.setCoverImage(course.getCoverImage()); + vo.setDescription(course.getDescription()); + vo.setDuration(course.getDuration()); + vo.setTeacher(course.getTeacher()); + vo.setStatus(course.getStatus()); + vo.setViewCount(course.getViewCount()); + vo.setLearnCount(course.getLearnCount()); + vo.setCreateTime(course.getCreateTime()); + return vo; + } + + /** + * @description 转换为学习记录实体对象 + * @return TbLearningRecord 学习记录实体 + */ + public TbLearningRecord toLearningRecord() { + TbLearningRecord record = new TbLearningRecord(); + if (this.recordID != null) { + // 如果有recordID,说明是从已有记录转换的,需要设置ID + record.setID(this.recordID); + } + record.setCourseID(this.courseID); + record.setDuration(this.learningDuration); + record.setProgress(this.progress); + record.setIsComplete(this.isComplete); + record.setCompleteTime(this.completeTime); + record.setLastLearnTime(this.lastLearnTime); + return record; + } + + /** + * @description 设置学习记录信息 + * @param record 学习记录实体 + */ + public void setLearningRecordInfo(TbLearningRecord record) { + if (record == null) { + return; + } + + this.recordID = record.getID(); + this.progress = record.getProgress(); + this.isComplete = record.getIsComplete(); + this.learningDuration = record.getDuration(); + this.lastLearnTime = record.getLastLearnTime(); + this.completeTime = record.getCompleteTime(); + } + + /** + * @description 从课程和学习记录创建CourseItemVO + * @param course 课程实体 + * @param record 学习记录实体 + * @return CourseItemVO + */ + public static CourseItemVO fromCourseAndRecord(TbCourse course, TbLearningRecord record) { + CourseItemVO vo = fromCourse(course); + if (vo != null && record != null) { + vo.setLearningRecordInfo(record); + } + return vo; + } + + /** + * @description 从章节创建CourseItemVO + * @param chapter 章节实体 + * @return CourseItemVO + */ + public static CourseItemVO fromChapter(TbCourseChapter chapter) { + if (chapter == null) { + return null; + } + + CourseItemVO vo = new CourseItemVO(); + vo.setID(chapter.getID()); + vo.setChapterID(chapter.getChapterID()); + vo.setCourseID(chapter.getCourseID()); + vo.setParentID(chapter.getParentID()); + vo.setName(chapter.getName()); + vo.setDescription(chapter.getContent()); + vo.setDuration(chapter.getDuration()); + vo.setVideoUrl(chapter.getVideoUrl()); + vo.setResourceID(chapter.getResourceID()); + vo.setNodeType(chapter.getChapterType()); + vo.setOrderNum(chapter.getOrderNum()); + vo.setCreateTime(chapter.getCreateTime()); + return vo; + } + + /** + * @description 从节点创建CourseItemVO + * @param node 节点实体 + * @return CourseItemVO + */ + public static CourseItemVO fromNode(TbCourseNode node) { + if (node == null) { + return null; + } + + CourseItemVO vo = new CourseItemVO(); + vo.setID(node.getID()); + vo.setNodeID(node.getNodeID()); + vo.setChapterID(node.getChapterID()); + vo.setName(node.getName()); + + vo.setContent(node.getContent()); + vo.setDuration(node.getDuration()); + vo.setVideoUrl(node.getVideoUrl()); + vo.setResourceID(node.getResourceID()); + vo.setNodeType(node.getNodeType()); + vo.setOrderNum(node.getOrderNum()); + vo.setIsRequired(node.getIsRequired()); + vo.setCreateTime(node.getCreateTime()); + return vo; + } + + /** + * @description 转换为章节实体对象 + * @return TbCourseChapter 章节实体 + */ + public TbCourseChapter toChapter() { + TbCourseChapter chapter = new TbCourseChapter(); + chapter.setID(this.getID()); + chapter.setChapterID(this.chapterID); + chapter.setCourseID(this.courseID); + chapter.setParentID(this.parentID); + chapter.setName(this.name); + chapter.setContent(this.description); + chapter.setDuration(this.duration); + chapter.setVideoUrl(this.videoUrl); + chapter.setResourceID(this.resourceID); + chapter.setChapterType(this.nodeType); + chapter.setOrderNum(this.orderNum); + return chapter; + } + + /** + * @description 转换为节点实体对象 + * @return TbCourseNode 节点实体 + */ + public TbCourseNode toNode() { + TbCourseNode node = new TbCourseNode(); + node.setID(this.getID()); + node.setNodeID(this.nodeID); + node.setChapterID(this.chapterID); + node.setName(this.name); + node.setContent(this.content); + node.setDuration(this.duration); + node.setVideoUrl(this.videoUrl); + node.setResourceID(this.resourceID); + node.setNodeType(this.nodeType); + node.setOrderNum(this.orderNum); + node.setIsRequired(this.isRequired); + return node; + } + + @Override + public String toString() { + return "CourseItemVO{" + + "courseID='" + courseID + '\'' + + ", name='" + name + '\'' + + ", coverImage='" + coverImage + '\'' + + ", teacher='" + teacher + '\'' + + ", duration=" + duration + + ", status=" + status + + ", progress=" + progress + + ", isComplete=" + isComplete + + ", learningDuration=" + learningDuration + + ", lastLearnTime=" + lastLearnTime + + ", viewCount=" + viewCount + + ", learnCount=" + learnCount + + '}'; + } +} diff --git a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/CourseVO.java b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/CourseVO.java deleted file mode 100644 index 767f52b..0000000 --- a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/CourseVO.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.xyzh.common.vo; - -import org.xyzh.common.dto.BaseDTO; -import org.xyzh.common.dto.study.TbCourse; -import org.xyzh.common.dto.study.TbCourseTag; -import org.xyzh.common.vo.ChapterVO; - -import java.util.List; - -public class CourseVO extends BaseDTO{ - private static final long serialVersionUID = 1L; - - private TbCourse course; - private List courseChapters; - private List courseTags; - - - public TbCourse getCourse() { - return course; - } - public void setCourse(TbCourse course) { - this.course = course; - } - public List getCourseChapters() { - return courseChapters; - } - public void setCourseChapters(List courseChapters) { - this.courseChapters = courseChapters; - } - public List getCourseTags() { - return courseTags; - } - public void setCourseTags(List courseTags) { - this.courseTags = courseTags; - } - - - -} diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/enums/TaskEnums.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/enums/TaskEnums.java new file mode 100644 index 0000000..4c9ea3b --- /dev/null +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/enums/TaskEnums.java @@ -0,0 +1,37 @@ +package org.xyzh.crontab.enums; + +import java.util.Arrays; + +import org.xyzh.crontab.task.DataBackupTask; +import org.xyzh.crontab.task.LogCleanTask; +import org.xyzh.crontab.task.SystemStatisticsTask; + +public enum TaskEnums { + DATA_BACKUP("dataBackup", DataBackupTask.class), + LOG_CLEAN("logClean", LogCleanTask.class), + SystemStatistics("systemStatistics", SystemStatisticsTask.class); + + + private String name; + private Class clazz; + + TaskEnums(String name, Class clazz) { + this.name = name; + this.clazz = clazz; + } + + public String getName() { + return name; + } + + public Class getClazz() { + return clazz; + } + + public static TaskEnums getByName(String name) { + return Arrays.stream(TaskEnums.values()) + .filter(task -> task.getName().equals(name)) + .findFirst() + .orElse(null); + } +} diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/task/newsTask/NewsTask.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/task/newsTask/NewsTask.java new file mode 100644 index 0000000..2b66d94 --- /dev/null +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/task/newsTask/NewsTask.java @@ -0,0 +1,12 @@ +package org.xyzh.crontab.task.newsTask; + + +abstract public class NewsTask { + + // 爬取网站目标 + private String target; + // 爬取标题 + private String title; + + +} diff --git a/schoolNewsServ/news/src/main/java/org/xyzh/news/controller/BannerController.java b/schoolNewsServ/news/src/main/java/org/xyzh/news/controller/BannerController.java index 21084f5..75169fa 100644 --- a/schoolNewsServ/news/src/main/java/org/xyzh/news/controller/BannerController.java +++ b/schoolNewsServ/news/src/main/java/org/xyzh/news/controller/BannerController.java @@ -6,7 +6,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.xyzh.api.news.banner.BannerService; import org.xyzh.common.core.domain.ResultDomain; +import org.xyzh.common.core.page.PageRequest; import org.xyzh.common.dto.resource.TbBanner; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + /** * @description 横幅控制器 @@ -27,16 +31,16 @@ public class BannerController { * 获取横幅列表 */ @GetMapping("/list") - public ResultDomain getBannerList() { - return bannerService.getBannerList(null); + public ResultDomain getBannerList(TbBanner filter) { + return bannerService.getBannerList(filter); } /** * 根据ID获取横幅详情 */ - @GetMapping("/banner/{bannerID}") - public ResultDomain getBannerById(@PathVariable String bannerID) { - return bannerService.getBannerById(bannerID); + @PostMapping("/banner/page") + public ResultDomain getBannerPage(@RequestBody PageRequest pageRequest) { + return bannerService.getBannerPage(pageRequest.getPageParam(), pageRequest.getFilter()); } /** @@ -58,36 +62,27 @@ public class BannerController { /** * 删除横幅 */ - @DeleteMapping("/banner/{bannerID}") - public ResultDomain deleteBanner(@PathVariable String bannerID) { - return bannerService.deleteBanner(bannerID); + @DeleteMapping("/banner") + public ResultDomain deleteBanner(@RequestBody TbBanner banner) { + return bannerService.deleteBanner(banner.getBannerID()); } /** * 更新横幅状态 */ - @PutMapping("/banner/{bannerID}/status") - public ResultDomain updateBannerStatus( - @PathVariable String bannerID, - @RequestParam Integer status) { - return bannerService.updateBannerStatus(bannerID, status); + @PutMapping("/banner/status") + public ResultDomain updateBannerStatus(@RequestBody TbBanner banner) { + return bannerService.updateBannerStatus(banner.getBannerID(), banner.getStatus()); } /** - * 更新横幅排序 + * 获取首页横幅列表 */ - @PutMapping("/banner/{bannerID}/order") - public ResultDomain updateBannerOrder( - @PathVariable String bannerID, - @RequestParam Integer orderNum) { - return bannerService.updateBannerOrder(bannerID, orderNum); + @GetMapping("/home") + public ResultDomain getHomeBannerList() { + return bannerService.selectHomeBanners(); } + + - /** - * 获取活跃横幅 - */ - @GetMapping("/active") - public ResultDomain getActiveBanners(@RequestParam(required = false) Integer limit) { - return bannerService.getActiveBanners(limit); - } } diff --git a/schoolNewsServ/news/src/main/java/org/xyzh/news/mapper/BannerMapper.java b/schoolNewsServ/news/src/main/java/org/xyzh/news/mapper/BannerMapper.java index 4baeced..b8a9f94 100644 --- a/schoolNewsServ/news/src/main/java/org/xyzh/news/mapper/BannerMapper.java +++ b/schoolNewsServ/news/src/main/java/org/xyzh/news/mapper/BannerMapper.java @@ -27,6 +27,19 @@ public interface BannerMapper extends BaseMapper { */ List selectBanners(TbBanner filter); + List selectBannersLimit(@Param("filter") TbBanner filter, @Param("limit") Integer limit); + + + /** + * @description 分页查询Banner + * @param filter 过滤条件 + * @param pageParam 分页参数 + * @return List Banner列表 + * @author yslg + * @since 2025-10-15 + */ + List selectBannersPage(@Param("filter") TbBanner filter, @Param("pageParam") PageParam pageParam); + /** * @description 根据Banner ID查询Banner信息 * @param bannerId Banner ID @@ -117,16 +130,6 @@ public interface BannerMapper extends BaseMapper { */ int batchDeleteBanners(@Param("ids") List ids); - /** - * @description 分页查询Banner - * @param filter 过滤条件 - * @param pageParam 分页参数 - * @return List Banner列表 - * @author yslg - * @since 2025-10-15 - */ - List selectBannersPage(@Param("filter") TbBanner filter, @Param("pageParam") PageParam pageParam); - /** * @description 统计Banner总数 * @param filter 过滤条件 diff --git a/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCBannerServiceImpl.java b/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCBannerServiceImpl.java index b9c1c2f..42e99bb 100644 --- a/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCBannerServiceImpl.java +++ b/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCBannerServiceImpl.java @@ -11,6 +11,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import org.xyzh.common.core.domain.ResultDomain; +import org.xyzh.common.core.page.PageDomain; +import org.xyzh.common.core.page.PageParam; import org.xyzh.common.dto.resource.TbBanner; import org.xyzh.common.utils.IDUtils; import org.xyzh.news.mapper.BannerMapper; @@ -32,49 +34,27 @@ public class NCBannerServiceImpl implements BannerService { private BannerMapper bannerMapper; @Override - public ResultDomain getBannerList(Integer status) { + public ResultDomain getBannerList(TbBanner filter) { ResultDomain resultDomain = new ResultDomain<>(); - try { - List list; - if (status != null) { - list = bannerMapper.selectByStatus(status); - } else { - TbBanner filter = new TbBanner(); - list = bannerMapper.selectBanners(filter); - } - resultDomain.success("获取横幅列表成功", list); - return resultDomain; - } catch (Exception e) { - logger.error("获取横幅列表异常: {}", e.getMessage(), e); - resultDomain.fail("获取横幅列表失败: " + e.getMessage()); - return resultDomain; - } + + List list = bannerMapper.selectBanners(filter); + resultDomain.success("获取横幅列表成功", list); + return resultDomain; } @Override - public ResultDomain getBannerById(String bannerID) { + public ResultDomain getBannerPage(PageParam pageParam,TbBanner filter) { ResultDomain resultDomain = new ResultDomain<>(); - try { - // 参数验证 - if (!StringUtils.hasText(bannerID)) { - resultDomain.fail("横幅ID不能为空"); - return resultDomain; - } + List list = bannerMapper.selectBannersPage(filter, pageParam); + PageDomain pageDomain = new PageDomain<>(); + int total = (int)bannerMapper.countBanners(filter); + pageParam.setTotalElements(total); + pageParam.setTotalPages( (int)Math.ceil((double)total / pageParam.getPageSize())); + pageDomain.setDataList(list); + pageDomain.setPageParam(pageParam); - // 查询横幅 - TbBanner banner = bannerMapper.selectByBannerId(bannerID); - if (banner == null || banner.getDeleted()) { - resultDomain.fail("横幅不存在"); - return resultDomain; - } - - resultDomain.success("获取横幅详情成功", banner); - return resultDomain; - } catch (Exception e) { - logger.error("获取横幅详情异常: {}", e.getMessage(), e); - resultDomain.fail("获取横幅详情失败: " + e.getMessage()); - return resultDomain; - } + resultDomain.success("获取横幅列表成功", pageDomain); + return resultDomain; } @Override @@ -103,7 +83,7 @@ public class NCBannerServiceImpl implements BannerService { if (banner.getID() == null) { banner.setID(IDUtils.generateID()); } - + banner.setBannerID(IDUtils.generateID()); banner.setCreateTime(new Date()); banner.setUpdateTime(new Date()); banner.setDeleted(false); @@ -139,13 +119,13 @@ public class NCBannerServiceImpl implements BannerService { ResultDomain resultDomain = new ResultDomain<>(); try { // 参数验证 - if (banner == null || !StringUtils.hasText(banner.getID())) { + if (banner == null || !StringUtils.hasText(banner.getBannerID())) { resultDomain.fail("横幅ID不能为空"); return resultDomain; } // 检查横幅是否存在 - TbBanner existing = bannerMapper.selectById(banner.getID()); + TbBanner existing = bannerMapper.selectByBannerId(banner.getBannerID()); if (existing == null || existing.getDeleted()) { resultDomain.fail("横幅不存在"); return resultDomain; @@ -153,7 +133,7 @@ public class NCBannerServiceImpl implements BannerService { // 如果修改了标题,检查标题是否已被使用 if (StringUtils.hasText(banner.getTitle()) && !banner.getTitle().equals(existing.getTitle())) { - int count = bannerMapper.countByTitle(banner.getTitle(), banner.getID()); + int count = bannerMapper.countByTitle(banner.getTitle(), banner.getBannerID()); if (count > 0) { resultDomain.fail("横幅标题已存在"); return resultDomain; @@ -166,9 +146,9 @@ public class NCBannerServiceImpl implements BannerService { // 更新数据库 int result = bannerMapper.updateBanner(banner); if (result > 0) { - logger.info("更新横幅成功: {}", banner.getID()); + logger.info("更新横幅成功: {}", banner.getBannerID()); // 重新查询返回完整数据 - TbBanner updated = bannerMapper.selectById(banner.getID()); + TbBanner updated = bannerMapper.selectByBannerId(banner.getBannerID()); resultDomain.success("更新横幅成功", updated); return resultDomain; } else { @@ -247,7 +227,7 @@ public class NCBannerServiceImpl implements BannerService { if (result > 0) { logger.info("更新横幅状态成功: {}", bannerID); // 重新查询返回完整数据 - TbBanner updated = bannerMapper.selectById(banner.getID()); + TbBanner updated = bannerMapper.selectByBannerId(banner.getBannerID()); resultDomain.success("更新横幅状态成功", updated); return resultDomain; } else { @@ -291,7 +271,7 @@ public class NCBannerServiceImpl implements BannerService { if (result > 0) { logger.info("更新横幅排序成功: {}", bannerID); // 重新查询返回完整数据 - TbBanner updated = bannerMapper.selectById(banner.getID()); + TbBanner updated = bannerMapper.selectByBannerId(banner.getBannerID()); resultDomain.success("更新横幅排序成功", updated); return resultDomain; } else { @@ -377,4 +357,15 @@ public class NCBannerServiceImpl implements BannerService { return resultDomain; } } + + @Override + public ResultDomain selectHomeBanners() { + ResultDomain resultDomain = new ResultDomain<>(); + TbBanner filter = new TbBanner(); + filter.setStatus(1); + + List list = bannerMapper.selectBannersLimit(filter, 5); + resultDomain.success("获取首页横幅列表成功", list); + return resultDomain; + } } diff --git a/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCTagServiceImpl.java b/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCTagServiceImpl.java index 0fb91bf..3285eec 100644 --- a/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCTagServiceImpl.java +++ b/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCTagServiceImpl.java @@ -13,9 +13,11 @@ import org.springframework.util.StringUtils; import org.xyzh.common.core.domain.ResultDomain; import org.xyzh.common.dto.resource.TbResourceTag; import org.xyzh.common.dto.resource.TbTag; +import org.xyzh.common.dto.user.TbSysUser; import org.xyzh.common.utils.IDUtils; import org.xyzh.news.mapper.ResourceTagMapper; import org.xyzh.news.mapper.TagMapper; +import org.xyzh.system.utils.LoginUtil; import org.xyzh.api.news.tag.TagService; /** @@ -89,39 +91,27 @@ public class NCTagServiceImpl implements TagService { @Transactional(rollbackFor = Exception.class) public ResultDomain updateTag(TbTag tag) { ResultDomain resultDomain = new ResultDomain<>(); + TbSysUser user = LoginUtil.getCurrentUser(); + if (user == null) { + resultDomain.fail("用户未登录"); + return resultDomain; + } try { // 参数验证 - if (tag == null || !StringUtils.hasText(tag.getID())) { + if (tag == null || !StringUtils.hasText(tag.getTagID())) { resultDomain.fail("标签ID不能为空"); return resultDomain; } - - // 检查标签是否存在 - TbTag existingTag = tagMapper.selectById(tag.getID()); - if (existingTag == null || existingTag.getDeleted()) { - resultDomain.fail("标签不存在"); - return resultDomain; - } - - // 检查标签名称是否重复(排除自身,同类型下) - if (StringUtils.hasText(tag.getName())) { - Integer tagType = tag.getTagType() != null ? tag.getTagType() : existingTag.getTagType(); - int count = tagMapper.countByNameAndType(tag.getName(), tagType, tag.getID()); - if (count > 0) { - resultDomain.fail("该类型下标签名称已存在"); - return resultDomain; - } - } - // 更新时间 + tag.setUpdater(user.getID()); tag.setUpdateTime(new Date()); // 更新数据库 int result = tagMapper.updateTag(tag); if (result > 0) { - logger.info("更新标签成功: {}", tag.getID()); + logger.info("更新标签成功: {}", tag.getTagID()); // 重新查询返回完整数据 - TbTag updated = tagMapper.selectById(tag.getID()); + TbTag updated = tagMapper.selectByTagId(tag.getTagID()); resultDomain.success("更新标签成功", updated); return resultDomain; } else { diff --git a/schoolNewsServ/news/src/main/resources/mapper/BannerMapper.xml b/schoolNewsServ/news/src/main/resources/mapper/BannerMapper.xml index acce3ed..c424cfd 100644 --- a/schoolNewsServ/news/src/main/resources/mapper/BannerMapper.xml +++ b/schoolNewsServ/news/src/main/resources/mapper/BannerMapper.xml @@ -5,6 +5,7 @@ + @@ -22,7 +23,7 @@ - id, title, image_url, link_type, link_id, link_url, order_num, status, + id, banner_id, title, image_url, link_type, link_id, link_url, order_num, status, creator, updater, create_time, update_time, delete_time, deleted @@ -30,6 +31,9 @@ deleted = 0 + + AND banner_id = #{bannerID} + AND title LIKE CONCAT('%', #{title}, '%') @@ -44,6 +48,26 @@ + + + deleted = 0 + + AND banner_id = #{filter.bannerID} + + + AND title LIKE CONCAT('%', #{filter.title}, '%') + + + AND link_type = #{filter.linkType} + + + AND link_id = #{filter.linkID} + + + AND status = #{filter.status} + + + + + @@ -95,18 +128,18 @@ FROM tb_banner WHERE title = #{title} AND deleted = 0 - AND id != #{excludeId} + AND banner_id != #{excludeId} INSERT INTO tb_banner ( - id, title, image_url, link_type, link_id, link_url, order_num, status, - creator, updater, create_time, update_time, delete_time, deleted + id, banner_id, title, image_url, link_type, link_id, link_url, order_num, status, + creator, create_time ) VALUES ( - #{id}, #{title}, #{imageUrl}, #{linkType}, #{linkID}, #{linkUrl}, #{orderNum}, #{status}, - #{creator}, #{updater}, #{createTime}, #{updateTime}, #{deleteTime}, #{deleted} + #{id}, #{bannerID}, #{title}, #{imageUrl}, #{linkType}, #{linkID}, #{linkUrl}, #{orderNum}, #{status}, + #{creator}, #{createTime} ) @@ -114,6 +147,9 @@ UPDATE tb_banner + + banner_id = #{bannerID}, + title = #{title}, @@ -148,24 +184,24 @@ deleted = #{deleted}, - WHERE id = #{id} + WHERE banner_id = #{bannerID} DELETE FROM tb_banner - WHERE id = #{id} + WHERE banner_id = #{bannerID} INSERT INTO tb_banner ( - id, title, image_url, link_type, link_id, link_url, order_num, status, + id, banner_id, title, image_url, link_type, link_id, link_url, order_num, status, creator, updater, create_time, update_time, delete_time, deleted ) VALUES ( - #{item.id}, #{item.title}, #{item.imageUrl}, #{item.linkType}, #{item.linkID}, + #{item.id}, #{item.bannerID}, #{item.title}, #{item.imageUrl}, #{item.linkType}, #{item.linkID}, #{item.linkUrl}, #{item.orderNum}, #{item.status}, #{item.creator}, #{item.updater}, #{item.createTime}, #{item.updateTime}, #{item.deleteTime}, #{item.deleted} ) @@ -175,9 +211,9 @@ DELETE FROM tb_banner - WHERE id IN - - #{id} + WHERE banner_id IN + + #{bannerID} @@ -186,7 +222,7 @@ SELECT FROM tb_banner - + ORDER BY order_num ASC, create_time DESC LIMIT #{pageParam.pageSize} OFFSET #{pageParam.offset} @@ -195,7 +231,7 @@ diff --git a/schoolNewsServ/study/src/main/java/org/xyzh/study/controller/CourseController.java b/schoolNewsServ/study/src/main/java/org/xyzh/study/controller/CourseController.java index 52ce863..5c65d02 100644 --- a/schoolNewsServ/study/src/main/java/org/xyzh/study/controller/CourseController.java +++ b/schoolNewsServ/study/src/main/java/org/xyzh/study/controller/CourseController.java @@ -10,11 +10,11 @@ import org.xyzh.common.core.page.PageRequest; import org.xyzh.common.dto.study.TbCourse; import org.xyzh.common.dto.study.TbCourseChapter; import org.xyzh.common.dto.study.TbCourseNode; -import org.xyzh.common.dto.study.TbLearningRecord; -import org.xyzh.common.vo.ChapterVO; -import org.xyzh.common.vo.CourseVO; +import org.xyzh.common.vo.CourseItemVO; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.GetMapping; + /** @@ -52,24 +52,30 @@ public class CourseController { * 根据ID获取课程详情 */ @GetMapping("/{courseID}") - public ResultDomain getCourseById(@PathVariable("courseID") String courseID) { + public ResultDomain getCourseById(@PathVariable("courseID") String courseID) { return courseService.getCourseById(courseID); } + @GetMapping("/{courseID}/progress") + public ResultDomain getCourseProgress(@PathVariable("courseID") String courseID) { + return courseService.getCourseProgress(courseID); + } + + /** * 创建课程 */ @PostMapping("/course") - public ResultDomain createCourse(@RequestBody CourseVO courseVO) { - return courseService.createCourse(courseVO); + public ResultDomain createCourse(@RequestBody CourseItemVO courseItemVO) { + return courseService.createCourse(courseItemVO); } /** * 更新课程基本信息 */ @PutMapping("/course") - public ResultDomain updateCourse(@RequestBody CourseVO courseVO) { - return courseService.updateCourse(courseVO); + public ResultDomain updateCourse(@RequestBody CourseItemVO courseItemVO) { + return courseService.updateCourse(courseItemVO); } /** diff --git a/schoolNewsServ/study/src/main/java/org/xyzh/study/controller/LearningTaskController.java b/schoolNewsServ/study/src/main/java/org/xyzh/study/controller/LearningTaskController.java index 6adaad8..5e42088 100644 --- a/schoolNewsServ/study/src/main/java/org/xyzh/study/controller/LearningTaskController.java +++ b/schoolNewsServ/study/src/main/java/org/xyzh/study/controller/LearningTaskController.java @@ -88,8 +88,8 @@ public class LearningTaskController { * 删除任务 */ @DeleteMapping("/task") - public ResultDomain deleteTask(@PathVariable("taskID") String taskID) { - return learningTaskService.deleteTask(taskID); + public ResultDomain deleteTask(@RequestBody TbLearningTask task) { + return learningTaskService.deleteTask(task.getTaskID()); } /** diff --git a/schoolNewsServ/study/src/main/java/org/xyzh/study/mapper/CourseMapper.java b/schoolNewsServ/study/src/main/java/org/xyzh/study/mapper/CourseMapper.java index 92fc3b1..3189962 100644 --- a/schoolNewsServ/study/src/main/java/org/xyzh/study/mapper/CourseMapper.java +++ b/schoolNewsServ/study/src/main/java/org/xyzh/study/mapper/CourseMapper.java @@ -5,6 +5,7 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.xyzh.common.core.page.PageParam; import org.xyzh.common.dto.study.TbCourse; +import org.xyzh.common.vo.CourseItemVO; import java.util.List; @@ -181,4 +182,14 @@ public interface CourseMapper extends BaseMapper { * @since 2025-10-15 */ int incrementViewCount(@Param("courseID") String courseID); + + /** + * @description 增加课程学习人数 + * @param courseID 课程ID + * @param count 增加人数 + * @return int 影响行数 + * @author yslg + * @since 2025-10-15 + */ + int incrementLearnCount(@Param("courseID") String courseID, @Param("count") int count); } diff --git a/schoolNewsServ/study/src/main/java/org/xyzh/study/mapper/CourseNodeMapper.java b/schoolNewsServ/study/src/main/java/org/xyzh/study/mapper/CourseNodeMapper.java index 1c793f7..a33b5b1 100644 --- a/schoolNewsServ/study/src/main/java/org/xyzh/study/mapper/CourseNodeMapper.java +++ b/schoolNewsServ/study/src/main/java/org/xyzh/study/mapper/CourseNodeMapper.java @@ -5,6 +5,7 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.xyzh.common.core.page.PageParam; import org.xyzh.common.dto.study.TbCourseNode; +import org.xyzh.common.vo.CourseItemVO; import java.util.List; @@ -35,6 +36,16 @@ public interface CourseNodeMapper extends BaseMapper { * @since 2025-10-21 */ List selectCourseNodesByChapterIDs(@Param("chapterIDs") List chapterIDs); + + /** + * @description 查询节点进度 + * @param chapterIDs 章节ID列表 + * @param userID 用户ID(可选,传null则不关联学习记录) + * @return List 节点进度列表 + * @author yslg + * @since 2025-10-28 + */ + List selectNodesProgress(@Param("chapterIDs") List chapterIDs, @Param("userID") String userID); /** * @description 根据节点ID查询节点信息 * @param nodeId 节点ID diff --git a/schoolNewsServ/study/src/main/java/org/xyzh/study/service/impl/SCCourseServiceImpl.java b/schoolNewsServ/study/src/main/java/org/xyzh/study/service/impl/SCCourseServiceImpl.java index 74db2ea..93c5b21 100644 --- a/schoolNewsServ/study/src/main/java/org/xyzh/study/service/impl/SCCourseServiceImpl.java +++ b/schoolNewsServ/study/src/main/java/org/xyzh/study/service/impl/SCCourseServiceImpl.java @@ -20,14 +20,10 @@ import org.xyzh.common.core.page.PageRequest; import org.xyzh.common.dto.study.TbCourse; import org.xyzh.common.dto.study.TbCourseChapter; import org.xyzh.common.dto.study.TbCourseNode; -import org.xyzh.common.dto.study.TbCourseTag; -import org.xyzh.common.dto.study.TbLearningRecord; import org.xyzh.common.dto.user.TbSysUser; import org.xyzh.common.utils.IDUtils; -import org.xyzh.common.vo.ChapterVO; -import org.xyzh.common.vo.CourseVO; +import org.xyzh.common.vo.CourseItemVO; import org.xyzh.study.mapper.CourseMapper; -import org.xyzh.study.mapper.CourseTagMapper; import org.xyzh.study.mapper.CourseChapterMapper; import org.xyzh.study.mapper.CourseNodeMapper; import org.xyzh.study.service.SCCourseService; @@ -51,9 +47,6 @@ public class SCCourseServiceImpl implements SCCourseService { @Autowired private CourseChapterMapper courseChapterMapper; - @Autowired - private CourseTagMapper courseTagMapper; - @Autowired private CourseNodeMapper courseNodeMapper; @@ -81,108 +74,133 @@ public class SCCourseServiceImpl implements SCCourseService { } @Override - public ResultDomain getCourseById(String courseID) { - ResultDomain resultDomain = new ResultDomain<>(); + public ResultDomain getCourseById(String courseID) { + ResultDomain resultDomain = new ResultDomain<>(); // 查询课程 TbCourse course = courseMapper.selectByCourseId(courseID); - // 查询标签 - List tags = courseTagMapper.selectByCourseId(courseID); - CourseVO courseVO = new CourseVO(); - courseVO.setCourse(course); - courseVO.setCourseTags(tags); + if (course == null) { + resultDomain.fail("课程不存在"); + return resultDomain; + } + + // 从课程实体创建CourseItemVO + CourseItemVO courseItemVO = CourseItemVO.fromCourse(course); // 查询课程章节 TbCourseChapter filter = new TbCourseChapter(); filter.setCourseID(courseID); List chapters = courseChapterMapper.selectCourseChapters(filter); - // 查询子节点 - if (chapters.size() > 0) { - List chapterIDs = chapters.stream().map(TbCourseChapter::getChapterID).collect(Collectors.toList()); + + // 查询并构建章节及节点结构 + if (!chapters.isEmpty()) { + List chapterIDs = chapters.stream() + .map(TbCourseChapter::getChapterID) + .collect(Collectors.toList()); + + // 查询所有节点 List nodes = courseNodeMapper.selectCourseNodesByChapterIDs(chapterIDs); - Map> nodesMap = nodes.stream().collect(Collectors.groupingBy(TbCourseNode::getChapterID)); - List chapterVOs = chapters.stream().map(chapter -> { - ChapterVO chapterVO = new ChapterVO(); - chapterVO.setChapter(chapter); - chapterVO.setNodes(nodesMap.get(chapter.getChapterID())); - return chapterVO; - }).collect(Collectors.toList()); - courseVO.setCourseChapters(chapterVOs); + + // 转换章节为CourseItemVO列表 + List chapterVOs = chapters.stream() + .map(CourseItemVO::fromChapter) + .collect(Collectors.toList()); + + // 转换节点为CourseItemVO,并按章节ID分组 + Map> nodesMap = nodes.stream() + .map(CourseItemVO::fromNode) + .collect(Collectors.groupingBy(CourseItemVO::getChapterID)); + + // 设置章节列表和章节节点映射 + courseItemVO.setChapters(chapterVOs); + courseItemVO.setChapterNodes(nodesMap); } - resultDomain.success("获取课程详情成功", courseVO); + resultDomain.success("获取课程详情成功", courseItemVO); return resultDomain; } @Override - public ResultDomain createCourse(CourseVO courseVO) { - ResultDomain resultDomain = new ResultDomain<>(); + public ResultDomain createCourse(CourseItemVO courseItemVO) { + ResultDomain resultDomain = new ResultDomain<>(); TbSysUser user = LoginUtil.getCurrentUser(); if (user == null) { resultDomain.fail("请先登录"); return resultDomain; } - TbCourse course = courseVO.getCourse(); + + // 转换为课程实体并保存 + TbCourse course = courseItemVO.toCourse(); String courseID = IDUtils.generateID(); + course.setID(IDUtils.generateID()); course.setCreator(user.getID()); course.setCourseID(courseID); Date now = new Date(); course.setCreateTime(now); - course.setStatus(0); - - + if (course.getStatus() == null) { + course.setStatus(0); + } courseMapper.insertCourse(course); + courseItemVO.setCourseID(courseID); - List courseChapters = courseVO.getCourseChapters(); - List chapters = new ArrayList<>(); - List nodes = new ArrayList<>(); - int length = courseChapters.size(); - for (int i=0; i nodesList = chapterVO.getNodes(); + // 处理章节和节点 + List chapterVOs = courseItemVO.getChapters(); + if (chapterVOs != null && !chapterVOs.isEmpty()) { + List chapters = new ArrayList<>(); + List allNodes = new ArrayList<>(); + + for (int i = 0; i < chapterVOs.size(); i++) { + CourseItemVO chapterVO = chapterVOs.get(i); + TbCourseChapter chapter = chapterVO.toChapter(); + + String chapterID = IDUtils.generateID(); + chapter.setID(IDUtils.generateID()); + chapter.setChapterID(chapterID); + chapter.setCourseID(courseID); + chapter.setCreator(user.getID()); + chapter.setCreateTime(now); + chapter.setOrderNum(i); + chapters.add(chapter); + + // 更新chapterVO中的ID + chapterVO.setChapterID(chapterID); - chapter.setCourseID(courseID); - String chapterID = IDUtils.generateID(); - chapter.setID(IDUtils.generateID()); - chapter.setChapterID(chapterID); - chapter.setCreator(user.getID()); - chapter.setCreateTime(now); - chapter.setOrderNum(i); - chapters.add(chapter); - - for (int j=0; j nodeVOs = chapterVO.getChapters(); // 节点存储在chapters字段中 + if (nodeVOs != null && !nodeVOs.isEmpty()) { + for (int j = 0; j < nodeVOs.size(); j++) { + CourseItemVO nodeVO = nodeVOs.get(j); + TbCourseNode node = nodeVO.toNode(); + + String nodeID = IDUtils.generateID(); + node.setID(IDUtils.generateID()); + node.setNodeID(nodeID); + node.setChapterID(chapterID); + node.setCreator(user.getID()); + node.setCreateTime(now); + node.setOrderNum(j); + allNodes.add(node); + + // 更新nodeVO中的ID + nodeVO.setNodeID(nodeID); + } + } + } + + if (!chapters.isEmpty()) { + courseChapterMapper.batchInsertCourseChapters(chapters); + } + if (!allNodes.isEmpty()) { + courseNodeMapper.batchInsertCourseNodes(allNodes); } } - courseChapterMapper.batchInsertCourseChapters(chapters); - courseNodeMapper.batchInsertCourseNodes(nodes); - - List courseTags = courseVO.getCourseTags(); - length = courseTags.size(); - if (length > 0) { - for (int i=0; i updateCourse(CourseVO courseVO) { - ResultDomain resultDomain = new ResultDomain<>(); + public ResultDomain updateCourse(CourseItemVO courseItemVO) { + ResultDomain resultDomain = new ResultDomain<>(); TbSysUser user = LoginUtil.getCurrentUser(); if (user == null) { @@ -190,8 +208,7 @@ public class SCCourseServiceImpl implements SCCourseService { return resultDomain; } - TbCourse course = courseVO.getCourse(); - String courseID = course.getCourseID(); + String courseID = courseItemVO.getCourseID(); if (courseID == null || courseID.isEmpty()) { resultDomain.fail("课程ID不能为空"); @@ -199,12 +216,14 @@ public class SCCourseServiceImpl implements SCCourseService { } // 1. 更新课程基本信息 + TbCourse course = courseItemVO.toCourse(); course.setUpdater(user.getID()); - course.setUpdateTime(new Date()); + Date now = new Date(); + course.setUpdateTime(now); courseMapper.updateCourse(course); // 2. 处理章节和节点 - List newChapterVOs = courseVO.getCourseChapters(); + List newChapterVOs = courseItemVO.getChapters(); if (newChapterVOs == null) { newChapterVOs = new ArrayList<>(); } @@ -235,12 +254,11 @@ public class SCCourseServiceImpl implements SCCourseService { List nodesToUpdate = new ArrayList<>(); Set newChapterIDs = new HashSet<>(); - Date now = new Date(); // 遍历新的章节 for (int i = 0; i < newChapterVOs.size(); i++) { - ChapterVO chapterVO = newChapterVOs.get(i); - TbCourseChapter chapter = chapterVO.getChapter(); + CourseItemVO chapterVO = newChapterVOs.get(i); + TbCourseChapter chapter = chapterVO.toChapter(); String chapterID = chapter.getChapterID(); chapter.setCourseID(courseID); @@ -266,9 +284,9 @@ public class SCCourseServiceImpl implements SCCourseService { newChapterIDs.add(chapterID); // 处理该章节的节点 - List newNodes = chapterVO.getNodes(); - if (newNodes == null) { - newNodes = new ArrayList<>(); + List newNodeVOs = chapterVO.getChapters(); // 节点存储在chapters字段中 + if (newNodeVOs == null) { + newNodeVOs = new ArrayList<>(); } List existingNodesForChapter = existingNodesMap.getOrDefault(chapterID, new ArrayList<>()); @@ -278,8 +296,9 @@ public class SCCourseServiceImpl implements SCCourseService { Set newNodeIDs = new HashSet<>(); // 遍历新的节点 - for (int j = 0; j < newNodes.size(); j++) { - TbCourseNode node = newNodes.get(j); + for (int j = 0; j < newNodeVOs.size(); j++) { + CourseItemVO nodeVO = newNodeVOs.get(j); + TbCourseNode node = nodeVO.toNode(); String nodeID = node.getNodeID(); node.setChapterID(chapterID); @@ -349,50 +368,7 @@ public class SCCourseServiceImpl implements SCCourseService { } } - // 3. 处理标签 - List newTags = courseVO.getCourseTags(); - if (newTags == null) { - newTags = new ArrayList<>(); - } - - // 获取现有标签 - List existingTags = courseTagMapper.selectByCourseId(courseID); - Map existingTagMap = existingTags.stream() - .collect(Collectors.toMap(TbCourseTag::getTagID, tag -> tag)); - - Set newTagIDs = new HashSet<>(); - List tagsToInsert = new ArrayList<>(); - - // 处理新标签 - for (TbCourseTag tag : newTags) { - String tagID = tag.getTagID(); - - if (tagID == null || tagID.isEmpty() || !existingTagMap.containsKey(tagID)) { - // 新增标签 - tag.setID(IDUtils.generateID()); - tag.setCourseID(courseID); - tag.setCreator(user.getID()); - tag.setCreateTime(now); - tagsToInsert.add(tag); - } else { - newTagIDs.add(tagID); - } - } - - // 找出要删除的标签 - List tagIDsToDelete = existingTagMap.keySet().stream() - .filter(id -> !newTagIDs.contains(id)) - .collect(Collectors.toList()); - - if (!tagIDsToDelete.isEmpty()) { - courseTagMapper.batchDeleteCourseTags(tagIDsToDelete); - } - - if (!tagsToInsert.isEmpty()) { - courseTagMapper.batchInsertCourseTags(tagsToInsert); - } - - resultDomain.success("更新课程成功", courseVO); + resultDomain.success("更新课程成功", courseItemVO); return resultDomain; } @@ -544,4 +520,57 @@ public class SCCourseServiceImpl implements SCCourseService { return resultDomain; } } + + @Override + public ResultDomain getCourseProgress(String courseID) { + ResultDomain resultDomain = new ResultDomain<>(); + + // 获取当前用户 + TbSysUser user = LoginUtil.getCurrentUser(); + if (user == null) { + resultDomain.fail("用户未登录"); + return resultDomain; + } + + // 查询课程 + TbCourse course = courseMapper.selectByCourseId(courseID); + if (course == null) { + resultDomain.fail("课程不存在"); + return resultDomain; + } + + // 从课程实体创建CourseItemVO + CourseItemVO courseItemVO = CourseItemVO.fromCourse(course); + + // 查询课程章节 + TbCourseChapter filter = new TbCourseChapter(); + filter.setCourseID(courseID); + List chapters = courseChapterMapper.selectCourseChapters(filter); + + // 查询并构建章节及节点结构(带进度) + if (!chapters.isEmpty()) { + List chapterIDs = chapters.stream() + .map(TbCourseChapter::getChapterID) + .collect(Collectors.toList()); + + // 查询带进度的节点(传入用户ID) + List nodesWithProgress = courseNodeMapper.selectNodesProgress(chapterIDs, user.getID()); + + // 转换章节为CourseItemVO列表 + List chapterVOs = chapters.stream() + .map(CourseItemVO::fromChapter) + .collect(Collectors.toList()); + + // 按章节ID分组节点 + Map> nodesMap = nodesWithProgress.stream() + .collect(Collectors.groupingBy(CourseItemVO::getChapterID)); + + // 设置章节列表和章节节点映射 + courseItemVO.setChapters(chapterVOs); + courseItemVO.setChapterNodes(nodesMap); + } + + resultDomain.success("获取课程进度成功", courseItemVO); + return resultDomain; + } } diff --git a/schoolNewsServ/study/src/main/java/org/xyzh/study/service/impl/SCLearningTaskServiceImpl.java b/schoolNewsServ/study/src/main/java/org/xyzh/study/service/impl/SCLearningTaskServiceImpl.java index 9570e55..530e28f 100644 --- a/schoolNewsServ/study/src/main/java/org/xyzh/study/service/impl/SCLearningTaskServiceImpl.java +++ b/schoolNewsServ/study/src/main/java/org/xyzh/study/service/impl/SCLearningTaskServiceImpl.java @@ -25,6 +25,7 @@ import org.xyzh.common.vo.TaskItemVO; import org.xyzh.common.vo.TaskVO; import org.xyzh.study.mapper.LearningTaskMapper; import org.xyzh.study.mapper.TaskUserMapper; +import org.xyzh.study.mapper.CourseMapper; import org.xyzh.system.utils.LoginUtil; import org.xyzh.study.mapper.TaskItemMapper; import org.xyzh.api.study.task.LearningTaskService; @@ -48,6 +49,9 @@ public class SCLearningTaskServiceImpl implements LearningTaskService { @Autowired private TaskUserMapper taskUserMapper; + @Autowired + private CourseMapper courseMapper; + @Autowired private TaskItemMapper taskItemMapper; @@ -82,7 +86,8 @@ public class SCLearningTaskServiceImpl implements LearningTaskService { taskUsers.add(taskUser); } int result = taskUserMapper.batchInsertTaskUsers(taskUsers); - if (result > 0) { + int learnCount = courseMapper.incrementLearnCount(taskID, userIDs.size()); + if (result > 0 && learnCount > 0) { resultDomain.success("添加任务用户成功", taskUsers); return resultDomain; } else { @@ -181,6 +186,10 @@ public class SCLearningTaskServiceImpl implements LearningTaskService { item.setCreateTime(now); }); taskUserMapper.batchInsertTaskUsers(taskUsers); + for(TbTaskItem item : taskCourses) { + int learnCount = courseMapper.incrementLearnCount(item.getItemID(), taskUsers.size()); + + } resultDomain.success("创建任务成功", taskVO); return resultDomain; @@ -349,6 +358,7 @@ public class SCLearningTaskServiceImpl implements LearningTaskService { if (!usersToInsert.isEmpty()) { taskUserMapper.batchInsertTaskUsers(usersToInsert); + int learnCount = courseMapper.incrementLearnCount(taskID, usersToInsert.size()); } resultDomain.success("更新任务成功", taskVO); @@ -357,8 +367,17 @@ public class SCLearningTaskServiceImpl implements LearningTaskService { @Override public ResultDomain deleteTask(String taskID) { - // TODO Auto-generated method stub - return null; + ResultDomain resultDomain = new ResultDomain<>(); + TbLearningTask filter = new TbLearningTask(); + filter.setTaskID(taskID); + int result = learningTaskMapper.deleteLearningTask(filter); + if (result > 0) { + resultDomain.success("删除任务成功", true); + return resultDomain; + } else { + resultDomain.fail("删除任务失败"); + return resultDomain; + } } @Override diff --git a/schoolNewsServ/study/src/main/resources/mapper/CourseMapper.xml b/schoolNewsServ/study/src/main/resources/mapper/CourseMapper.xml index 772fab2..5c482f2 100644 --- a/schoolNewsServ/study/src/main/resources/mapper/CourseMapper.xml +++ b/schoolNewsServ/study/src/main/resources/mapper/CourseMapper.xml @@ -184,7 +184,7 @@ teacher, status, view_count, learn_count, order_num, creator, create_time ) VALUES ( #{id}, #{courseID}, #{name}, #{coverImage}, #{description}, #{content}, #{duration}, - #{teacher}, #{status}, #{viewCount}, #{learnCount}, #{orderNum}, #{creator},#{createTime} + #{teacher}, #{status}, 0, 0, #{orderNum}, #{creator},#{createTime} ) @@ -248,15 +248,14 @@ INSERT INTO tb_course ( id, course_id, name, cover_image, description, content, duration, - teacher, status, view_count, learn_count, order_num, creator, updater, - create_time, update_time, delete_time, deleted + teacher, status, view_count, learn_count, order_num, creator, + create_time, delete_time, deleted ) VALUES ( #{item.id}, #{item.courseID}, #{item.name}, #{item.coverImage}, #{item.description}, - #{item.content}, #{item.duration}, #{item.teacher}, #{item.status}, #{item.viewCount}, - #{item.learnCount}, #{item.orderNum}, #{item.creator}, #{item.updater}, - #{item.createTime}, #{item.updateTime}, #{item.deleteTime}, #{item.deleted} + #{item.content}, #{item.duration}, #{item.teacher}, #{item.status}, 0, + 0, #{item.orderNum}, #{item.creator}, #{item.createTime}, #{item.deleteTime}, #{item.deleted} ) @@ -292,4 +291,14 @@ SET view_count = view_count + 1 WHERE course_id = #{courseID} AND deleted = 0 + + + + + UPDATE tb_course + SET learn_count = learn_count + #{count} + WHERE course_id = #{courseID} AND deleted = 0 + + + \ No newline at end of file diff --git a/schoolNewsServ/study/src/main/resources/mapper/CourseNodeMapper.xml b/schoolNewsServ/study/src/main/resources/mapper/CourseNodeMapper.xml index 86362d0..c3afcb5 100644 --- a/schoolNewsServ/study/src/main/resources/mapper/CourseNodeMapper.xml +++ b/schoolNewsServ/study/src/main/resources/mapper/CourseNodeMapper.xml @@ -23,6 +23,27 @@ + + + + + + + + + + + + + + + + + + + + + id, node_id, chapter_id, name, content, node_type, resource_id, video_url, duration, order_num, is_required, @@ -236,4 +257,41 @@ AND deleted = 0 ORDER BY order_num ASC, create_time ASC + + + diff --git a/schoolNewsServ/study/src/main/resources/mapper/LearningRecordMapper.xml b/schoolNewsServ/study/src/main/resources/mapper/LearningRecordMapper.xml index e603ead..968a90f 100644 --- a/schoolNewsServ/study/src/main/resources/mapper/LearningRecordMapper.xml +++ b/schoolNewsServ/study/src/main/resources/mapper/LearningRecordMapper.xml @@ -125,11 +125,9 @@ INSERT INTO tb_learning_record ( - id, user_id, task_id, resource_type, resource_id, course_id, chapter_id, node_id, duration, progress, - is_complete, complete_time, last_learn_time, create_time + id, user_id, task_id, resource_type, resource_id, course_id, chapter_id, node_id, create_time ) VALUES ( - #{id}, #{userID}, #{taskID}, #{resourceType}, #{resourceID}, #{courseID}, #{chapterID}, #{nodeID}, #{duration}, #{progress}, - #{isComplete}, #{completeTime}, #{lastLearnTime}, #{createTime} + #{id}, #{userID}, #{taskID}, #{resourceType}, #{resourceID}, #{courseID}, #{chapterID}, #{nodeID}, #{createTime} ) @@ -171,14 +169,11 @@ INSERT INTO tb_learning_record ( - id, user_id, resource_type, resource_id, task_id, duration, progress, - is_complete, complete_time, last_learn_time, create_time, update_time + id, user_id, resource_type, resource_id, task_id, course_id, chapter_id, node_id, create_time ) VALUES ( - #{item.id}, #{item.userID}, #{item.resourceType}, #{item.resourceID}, #{item.taskID}, - #{item.duration}, #{item.progress}, #{item.isComplete}, #{item.completeTime}, - #{item.lastLearnTime}, #{item.createTime}, #{item.updateTime} + #{item.id}, #{item.userID}, #{item.resourceType}, #{item.resourceID}, #{item.taskID}, #{item.courseID}, #{item.chapterID}, #{item.nodeID}, #{item.createTime} ) diff --git a/schoolNewsServ/study/src/main/resources/mapper/TaskUserMapper.xml b/schoolNewsServ/study/src/main/resources/mapper/TaskUserMapper.xml index 6afd035..bdeb87a 100644 --- a/schoolNewsServ/study/src/main/resources/mapper/TaskUserMapper.xml +++ b/schoolNewsServ/study/src/main/resources/mapper/TaskUserMapper.xml @@ -152,9 +152,9 @@ INSERT INTO tb_task_user ( - id, task_id, user_id, dept_id,creator, create_time + id, task_id, user_id, dept_id,creator, status, progress, create_time ) VALUES ( - #{id}, #{taskID}, #{userID}, #{deptID}, #{creator}, #{createTime} + #{id}, #{taskID}, #{userID}, #{deptID}, #{creator}, 0, 0, #{createTime} ) @@ -193,8 +193,8 @@ ) VALUES ( - #{item.id}, #{item.taskID}, #{item.userID}, #{item.deptID}, #{item.status}, - #{item.progress}, #{item.completeTime}, #{item.creator}, #{item.createTime} + #{item.id}, #{item.taskID}, #{item.userID}, #{item.deptID}, 0, + 0, #{item.completeTime}, #{item.creator}, #{item.createTime} ) diff --git a/schoolNewsWeb/public/img/admin/agent.svg b/schoolNewsWeb/public/img/admin/agent.svg new file mode 100644 index 0000000..a68b7ad --- /dev/null +++ b/schoolNewsWeb/public/img/admin/agent.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/schoolNewsWeb/public/img/admin/course.svg b/schoolNewsWeb/public/img/admin/course.svg new file mode 100644 index 0000000..47040b1 --- /dev/null +++ b/schoolNewsWeb/public/img/admin/course.svg @@ -0,0 +1,4 @@ + + + + diff --git a/schoolNewsWeb/public/img/admin/logs.svg b/schoolNewsWeb/public/img/admin/logs.svg new file mode 100644 index 0000000..6962e6c --- /dev/null +++ b/schoolNewsWeb/public/img/admin/logs.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/schoolNewsWeb/public/img/admin/overview.svg b/schoolNewsWeb/public/img/admin/overview.svg new file mode 100644 index 0000000..0369650 --- /dev/null +++ b/schoolNewsWeb/public/img/admin/overview.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/schoolNewsWeb/public/img/admin/settings.svg b/schoolNewsWeb/public/img/admin/settings.svg new file mode 100644 index 0000000..8f84a82 --- /dev/null +++ b/schoolNewsWeb/public/img/admin/settings.svg @@ -0,0 +1,4 @@ + + + + diff --git a/schoolNewsWeb/public/img/admin/study.svg b/schoolNewsWeb/public/img/admin/study.svg new file mode 100644 index 0000000..8ff1ab5 --- /dev/null +++ b/schoolNewsWeb/public/img/admin/study.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/schoolNewsWeb/public/img/admin/usermange.svg b/schoolNewsWeb/public/img/admin/usermange.svg new file mode 100644 index 0000000..09ada96 --- /dev/null +++ b/schoolNewsWeb/public/img/admin/usermange.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/schoolNewsWeb/src/apis/homepage/banner.ts b/schoolNewsWeb/src/apis/homepage/banner.ts deleted file mode 100644 index 1c12a88..0000000 --- a/schoolNewsWeb/src/apis/homepage/banner.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** - * @description 轮播图相关API - * @author yslg - * @since 2025-10-15 - */ - -import { api } from '@/apis/index'; -import type { Banner, Resource, ResultDomain } from '@/types'; - -/** - * 轮播图API服务 - */ -export const bannerApi = { - /** - * 获取轮播组件数据 - * @returns Promise> - */ - async getBannerList(): Promise> { - const response = await api.get('/homepage/banner/list'); - return response.data; - }, - - /** - * 点击轮播跳转新闻详情 - * @param bannerID Banner ID - * @returns Promise> - */ - async getBannerNewsDetail(bannerID: string): Promise> { - const response = await api.get(`/homepage/banner/click/${bannerID}`); - return response.data; - }, - - /** - * 获取活跃轮播列表 - * @returns Promise> - */ - async getActiveBanners(): Promise> { - const response = await api.get('/homepage/banner/active'); - return response.data; - } -}; diff --git a/schoolNewsWeb/src/apis/homepage/index.ts b/schoolNewsWeb/src/apis/homepage/index.ts index a950aa9..2b105ea 100644 --- a/schoolNewsWeb/src/apis/homepage/index.ts +++ b/schoolNewsWeb/src/apis/homepage/index.ts @@ -5,7 +5,6 @@ */ // 重新导出各个子模块 -export { bannerApi } from './banner'; export { recommendApi } from './recommend'; export { newsApi } from './news'; export { menuApi } from './menu'; diff --git a/schoolNewsWeb/src/apis/resource/banner.ts b/schoolNewsWeb/src/apis/resource/banner.ts new file mode 100644 index 0000000..f2d3c49 --- /dev/null +++ b/schoolNewsWeb/src/apis/resource/banner.ts @@ -0,0 +1,124 @@ +/** + * @description Banner 管理 API 接口 + * @filename banner-manage.ts + * @author yslg + * @copyright xyzh + * @since 2025-10-28 + */ + +import { api } from '@/apis'; +import type { ResultDomain, Banner, PageParam } from '@/types'; + +/** + * Banner 管理 API 服务 + */ +export const bannerApi = { + /** + * 获取横幅列表 + * @param filter 筛选条件 + * @returns Promise> + */ + async getBannerList(filter?: Partial): Promise> { + const response = await api.get('/news/banners/list', filter); + return response.data; + }, + + /** + * 获取横幅分页列表 + * @param pageParam 分页参数 + * @param filter 筛选条件 + * @returns Promise> + */ + async getBannerPage(pageParam: PageParam, filter?: Partial): Promise> { + const response = await api.post('/news/banners/banner/page', { + pageParam, + filter, + }); + return response.data; + }, + + /** + * 创建横幅 + * @param banner 横幅信息 + * @returns Promise> + */ + async createBanner(banner: Banner): Promise> { + const response = await api.post('/news/banners/banner', banner); + return response.data; + }, + + /** + * 更新横幅 + * @param banner 横幅信息 + * @returns Promise> + */ + async updateBanner(banner: Banner): Promise> { + const response = await api.put('/news/banners/banner', banner); + return response.data; + }, + + /** + * 删除横幅 + * @param banner 横幅信息(包含 bannerID) + * @returns Promise> + */ + async deleteBanner(banner: Banner): Promise> { + const response = await api.delete('/news/banners/banner', { + data: banner + }); + return response.data; + }, + + /** + * 根据 ID 删除横幅 + * @param bannerID 横幅ID + * @returns Promise> + */ + async deleteBannerById(bannerID: string): Promise> { + return this.deleteBanner({ id: bannerID }); + }, + + /** + * 更新横幅状态 + * @param bannerID 横幅ID + * @param status 状态值(0禁用 1启用) + * @returns Promise> + */ + async updateBannerStatus(bannerID: string, status: number): Promise> { + const response = await api.put('/news/banners/banner/status', { + id: bannerID, + status, + }); + return response.data; + }, + + /** + * 启用横幅 + * @param bannerID 横幅ID + * @returns Promise> + */ + async enableBanner(bannerID: string): Promise> { + return this.updateBannerStatus(bannerID, 1); + }, + + /** + * 禁用横幅 + * @param bannerID 横幅ID + * @returns Promise> + */ + async disableBanner(bannerID: string): Promise> { + return this.updateBannerStatus(bannerID, 0); + }, + + /** + * 获取首页横幅列表 + * @returns Promise> + */ + async getHomeBannerList(): Promise> { + const response = await api.get('/news/banners/home'); + return response.data; + }, +}; + +export default bannerApi; + diff --git a/schoolNewsWeb/src/apis/resource/index.ts b/schoolNewsWeb/src/apis/resource/index.ts index 67509e5..d079968 100644 --- a/schoolNewsWeb/src/apis/resource/index.ts +++ b/schoolNewsWeb/src/apis/resource/index.ts @@ -4,6 +4,6 @@ * @since 2025-10-15 */ -export * from './resourceCategory'; export * from './resourceTag'; -export * from './resource'; \ No newline at end of file +export * from './resource'; +export { bannerApi} from './banner'; \ No newline at end of file diff --git a/schoolNewsWeb/src/apis/resource/resourceCategory.ts b/schoolNewsWeb/src/apis/resource/resourceCategory.ts deleted file mode 100644 index 68e9c69..0000000 --- a/schoolNewsWeb/src/apis/resource/resourceCategory.ts +++ /dev/null @@ -1,117 +0,0 @@ -/** - * @description 资源分类API接口 - * @filename resourceCategory.ts - * @author yslg - * @copyright xyzh - * @since 2025-10-15 - * - * ⚠️ 注意:此API已废弃! - * - * 从2025-10-27起,资源分类功能已迁移到标签系统(Tag)中。 - * - * 迁移说明: - * - 原 tb_resource_category 表已废弃 - * - 改为使用 tb_tag 表的 tag_type=1 表示文章分类标签 - * - 请使用 resourceTagApi.getTagsByType(1) 替代本 API 的方法 - * - * API 迁移对照: - * - getCategoryList() → resourceTagApi.getTagsByType(1) - * - getCategoryById(id) → resourceTagApi.getTagById(id) - * - createCategory(category) → resourceTagApi.createTag({...category, tagType: 1}) - * - updateCategory(category) → resourceTagApi.updateTag(category) - * - deleteCategory(id) → resourceTagApi.deleteTag(id) - * - * @deprecated 请使用 resourceTagApi 代替 - */ - -import { api } from '@/apis'; -import type { ResultDomain, ResourceCategory } from '@/types'; - -/** - * 资源分类API服务 - * @deprecated 已废弃,请使用 resourceTagApi.getTagsByType(1) 获取文章分类标签 - */ -export const resourceCategoryApi = { - /** - * 获取分类列表 - * @returns Promise> - */ - async getCategoryList(): Promise> { - const response = await api.get('/news/categorys/list'); - return response.data; - }, - - /** - * 根据ID获取分类详情 - * @param tagID 分类ID - * @returns Promise> - */ - async getCategoryById(tagID: string): Promise> { - const response = await api.get(`/news/categorys/category/${tagID}`); - return response.data; - }, - - /** - * 创建分类 - * @param category 分类信息 - * @returns Promise> - */ - async createCategory(category: ResourceCategory): Promise> { - const response = await api.post('/news/categorys/category', category); - return response.data; - }, - - /** - * 更新分类 - * @param category 分类信息 - * @returns Promise> - */ - async updateCategory(category: ResourceCategory): Promise> { - const response = await api.put('/news/categorys/category', category); - return response.data; - }, - - /** - * 删除分类 - * @param tagID 分类ID - * @returns Promise> - */ - async deleteCategory(tagID: string): Promise> { - const response = await api.delete(`/news/categorys/category/${tagID}`); - return response.data; - }, - - /** - * 更新分类状态 - * @param tagID 分类ID - * @param status 状态值 - * @returns Promise> - */ - async updateCategoryStatus(tagID: string, status: number): Promise> { - const response = await api.put(`/news/categorys/category/${tagID}/status`, null, { - params: { status } - }); - return response.data; - }, - - /** - * 获取分类树 - * @returns Promise> - */ - async getCategoryTree(): Promise> { - const response = await api.get('/news/categorys/tree'); - return response.data; - }, - - /** - * 获取子分类 - * @param parentID 父分类ID - * @returns Promise> - */ - async getChildCategories(parentID: string): Promise> { - const response = await api.get(`/news/categorys/category/${parentID}/children`); - return response.data; - } -}; - -export default resourceCategoryApi; diff --git a/schoolNewsWeb/src/apis/study/course.ts b/schoolNewsWeb/src/apis/study/course.ts index 2807058..e02f70d 100644 --- a/schoolNewsWeb/src/apis/study/course.ts +++ b/schoolNewsWeb/src/apis/study/course.ts @@ -5,7 +5,7 @@ */ import { api } from '@/apis/index'; -import type { Course, CourseChapter, ResultDomain,CourseVO,PageRequest, PageParam, CourseNode } from '@/types'; +import type { Course, CourseChapter, ResultDomain, CourseItemVO, PageParam, CourseNode } from '@/types'; /** * 课程API服务 @@ -38,30 +38,40 @@ export const courseApi = { /** * 根据ID获取课程详情 * @param courseID 课程ID - * @returns Promise> + * @returns Promise> */ - async getCourseById(courseID: string): Promise> { - const response = await api.get(`${this.prefixCourse}/${courseID}`); + async getCourseById(courseID: string): Promise> { + const response = await api.get(`${this.prefixCourse}/${courseID}`); + return response.data; + }, + + /** + * 获取课程学习进度 + * @param courseID 课程ID + * @returns Promise> + */ + async getCourseProgress(courseID: string): Promise> { + const response = await api.get(`${this.prefixCourse}/${courseID}/progress`); return response.data; }, /** * 创建课程 - * @param course 课程数据 - * @returns Promise> + * @param courseItemVO 课程数据 + * @returns Promise> */ - async createCourse(course: CourseVO): Promise> { - const response = await api.post(`${this.prefixCourse}/course`, course); + async createCourse(courseItemVO: CourseItemVO): Promise> { + const response = await api.post(`${this.prefixCourse}/course`, courseItemVO); return response.data; }, /** * 更新课程 - * @param course 课程数据 - * @returns Promise> + * @param courseItemVO 课程数据 + * @returns Promise> */ - async updateCourse(courseVO: CourseVO): Promise> { - const response = await api.put(`${this.prefixCourse}/course`, courseVO); + async updateCourse(courseItemVO: CourseItemVO): Promise> { + const response = await api.put(`${this.prefixCourse}/course`, courseItemVO); return response.data; }, diff --git a/schoolNewsWeb/src/apis/study/learning-task.ts b/schoolNewsWeb/src/apis/study/learning-task.ts index 76c6286..1dc27a3 100644 --- a/schoolNewsWeb/src/apis/study/learning-task.ts +++ b/schoolNewsWeb/src/apis/study/learning-task.ts @@ -78,7 +78,7 @@ export const learningTaskApi = { * @returns Promise> */ async deleteTask(taskID: string): Promise> { - const response = await api.delete(`${this.learningTaskPrefix}/task/${taskID}`); + const response = await api.delete(`${this.learningTaskPrefix}/task`, {taskID}); return response.data; }, diff --git a/schoolNewsWeb/src/assets/imgs/article.svg b/schoolNewsWeb/src/assets/imgs/article.svg new file mode 100644 index 0000000..bbec520 --- /dev/null +++ b/schoolNewsWeb/src/assets/imgs/article.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/schoolNewsWeb/src/assets/imgs/book-read.svg b/schoolNewsWeb/src/assets/imgs/book-read.svg new file mode 100644 index 0000000..71bce57 --- /dev/null +++ b/schoolNewsWeb/src/assets/imgs/book-read.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/schoolNewsWeb/src/assets/imgs/clock.svg b/schoolNewsWeb/src/assets/imgs/clock.svg new file mode 100644 index 0000000..b4efa17 --- /dev/null +++ b/schoolNewsWeb/src/assets/imgs/clock.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/schoolNewsWeb/src/assets/imgs/edit.svg b/schoolNewsWeb/src/assets/imgs/edit.svg new file mode 100644 index 0000000..3182139 --- /dev/null +++ b/schoolNewsWeb/src/assets/imgs/edit.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/schoolNewsWeb/src/assets/imgs/eye.svg b/schoolNewsWeb/src/assets/imgs/eye.svg new file mode 100644 index 0000000..6b773c3 --- /dev/null +++ b/schoolNewsWeb/src/assets/imgs/eye.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/schoolNewsWeb/src/assets/imgs/plus.svg b/schoolNewsWeb/src/assets/imgs/plus.svg new file mode 100644 index 0000000..d70a4fd --- /dev/null +++ b/schoolNewsWeb/src/assets/imgs/plus.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/schoolNewsWeb/src/assets/imgs/time-line.svg b/schoolNewsWeb/src/assets/imgs/time-line.svg new file mode 100644 index 0000000..ded5e5c --- /dev/null +++ b/schoolNewsWeb/src/assets/imgs/time-line.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/schoolNewsWeb/src/assets/imgs/trashbin.svg b/schoolNewsWeb/src/assets/imgs/trashbin.svg new file mode 100644 index 0000000..08db61d --- /dev/null +++ b/schoolNewsWeb/src/assets/imgs/trashbin.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/schoolNewsWeb/src/assets/imgs/video.svg b/schoolNewsWeb/src/assets/imgs/video.svg new file mode 100644 index 0000000..bbec520 --- /dev/null +++ b/schoolNewsWeb/src/assets/imgs/video.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/schoolNewsWeb/src/components/base/MenuItem.vue b/schoolNewsWeb/src/components/base/MenuItem.vue index d3e1e81..1bfab0a 100644 --- a/schoolNewsWeb/src/components/base/MenuItem.vue +++ b/schoolNewsWeb/src/components/base/MenuItem.vue @@ -8,7 +8,12 @@ @click="toggleExpanded" > @@ -57,7 +67,7 @@ import { ref, computed } from 'vue'; import { useRoute } from 'vue-router'; import type { SysMenu } from '@/types'; import { MenuType } from '@/types/enums'; - +import { PUBLIC_IMG_PATH} from '@/config' // 递归组件需要声明名称(Vue 3.5+) defineOptions({ name: 'MenuItem' @@ -153,15 +163,12 @@ function handleClick() { line-height: 1.43; } -.menu-icon { - font-size: 16px; +.tab-icon { width: 16px; - text-align: center; + height: 16px; margin-right: 12px; - - .collapsed & { - margin-right: 0; - } + flex-shrink: 0; + object-fit: contain; } .menu-title { @@ -211,12 +218,4 @@ function handleClick() { opacity: 0; transform: translateY(-10px); } - -/* 图标字体类(简单实现,实际项目中使用图标库) */ -.icon-folder::before { content: "📁"; } -.icon-file::before { content: "📄"; } -.icon-dashboard::before { content: "📊"; } -.icon-user::before { content: "👤"; } -.icon-news::before { content: "📰"; } -.icon-settings::before { content: "⚙️"; } diff --git a/schoolNewsWeb/src/layouts/SidebarLayout.vue b/schoolNewsWeb/src/layouts/SidebarLayout.vue index 16fcb74..b2bcaef 100644 --- a/schoolNewsWeb/src/layouts/SidebarLayout.vue +++ b/schoolNewsWeb/src/layouts/SidebarLayout.vue @@ -179,13 +179,10 @@ function handleMenuClick(menu: SysMenu) { flex: 1; background: #F9FAFB; overflow-y: auto; - min-width: 0; - - // 使用 margin 而不是 padding,避免影响滚动高度计算 - // margin 不计入元素尺寸,所以不会导致滚动条 - > * { - margin: 20px; - } + /* min-width: 0; */ + padding: 20px; + box-sizing: border-box; + height: 100vh; // 美化滚动条 &::-webkit-scrollbar { @@ -215,11 +212,8 @@ function handleMenuClick(menu: SysMenu) { background: #F9FAFB; height: 100vh; overflow-y: auto; - - // 使用 margin 而不是 padding,避免影响滚动高度计算 - > * { - margin: 20px; - } + padding: 20px; + box-sizing: border-box; // 美化滚动条 &::-webkit-scrollbar { diff --git a/schoolNewsWeb/src/types/resource/index.ts b/schoolNewsWeb/src/types/resource/index.ts index 57ce8cc..69f54e0 100644 --- a/schoolNewsWeb/src/types/resource/index.ts +++ b/schoolNewsWeb/src/types/resource/index.ts @@ -59,6 +59,7 @@ export interface Resource extends BaseDTO { * Banner实体 */ export interface Banner extends BaseDTO { + bannerID?: string; /** Banner标题 */ title?: string; /** Banner图片URL */ @@ -138,10 +139,8 @@ export interface Tag extends BaseDTO { color?: string; /** 标签类型(1-文章分类标签 2-课程分类标签 3-学习任务分类标签) */ tagType?: number; - /** 排序号 */ - orderNum?: number; - /** 状态(0禁用 1启用) */ - status?: number; + /** 使用计数(被标记的资源数量) */ + usageCount?: number; /** 创建者 */ creator?: string; /** 更新者 */ diff --git a/schoolNewsWeb/src/types/study/index.ts b/schoolNewsWeb/src/types/study/index.ts index 3d04daf..bf4704c 100644 --- a/schoolNewsWeb/src/types/study/index.ts +++ b/schoolNewsWeb/src/types/study/index.ts @@ -118,15 +118,72 @@ export interface CourseNode extends BaseDTO { updater?: string; } -export interface ChapterVO extends BaseDTO { - chapter: CourseChapter; - nodes: CourseNode[]; -} +/** + * 课程项目VO - 统一的课程视图对象 + * 包含课程、章节、学习节点、学习记录的字段 + */ +export interface CourseItemVO extends BaseDTO { + // ========== 课程基本信息 ========== + /** 课程ID */ + courseID?: string; + /** 课程名称 */ + name?: string; + /** 课程封面图片 */ + coverImage?: string; + /** 课程描述 */ + description?: string; + /** 课程时长(分钟) */ + duration?: number; + /** 授课老师 */ + teacher?: string; + /** 课程状态(0未上线 1已上线 2已下架) */ + status?: number; + /** 浏览次数 */ + viewCount?: number; + /** 学习人数 */ + learnCount?: number; + /** 课程创建时间 */ + createTime?: string; -export interface CourseVO extends BaseDTO { - course: Course; - courseChapters: ChapterVO[]; - courseTags: CourseTag[]; + // ========== 学习记录信息 ========== + /** 学习记录ID */ + recordID?: string; + /** 学习进度(0-100) */ + progress?: number; + /** 是否完成 */ + isComplete?: boolean; + /** 学习时长(秒) */ + learningDuration?: number; + /** 最后学习时间 */ + lastLearnTime?: string; + /** 完成时间 */ + completeTime?: string; + + // ========== 章节节点信息 ========== + /** 章节ID(当此对象代表章节或节点时使用) */ + chapterID?: string; + /** 节点ID(当此对象代表节点时使用) */ + nodeID?: string; + /** 父级ID(章节的父章节ID) */ + parentID?: string; + /** 节点类型(1视频 2文档 3音频 4图片 5链接) */ + nodeType?: number; + /** 节点内容(富文本内容) */ + content?: string; + /** 视频URL */ + videoUrl?: string; + /** 资源ID */ + resourceID?: string; + /** 排序号 */ + orderNum?: number; + /** 是否必修(1必修 0选修) */ + isRequired?: number; + + // ========== 层级结构 ========== + /** 章节列表(课程的章节列表,或章节的节点列表) */ + chapters?: CourseItemVO[]; + /** 章节节点映射(key: chapterID, value: 该章节下的节点列表) */ + chapterNodes?: Record; } /** diff --git a/schoolNewsWeb/src/utils/routeUtils.ts b/schoolNewsWeb/src/utils/routeUtils.ts index 7ede8c4..04b005e 100644 --- a/schoolNewsWeb/src/utils/routeUtils.ts +++ b/schoolNewsWeb/src/utils/routeUtils.ts @@ -3,7 +3,6 @@ import { RouteRecordRaw, RouteLocationNormalized } from 'vue-router'; export function getParentChildrenRoutes(route: RouteLocationNormalized): RouteRecordRaw[] { // 判断是否有父节点(至少需要2个匹配的路由) if (route.matched.length < 2) { - console.log('没有父节点,route.matched 长度不足'); return []; } @@ -12,7 +11,6 @@ export function getParentChildrenRoutes(route: RouteLocationNormalized): RouteRe // 检查父路由是否有子路由 if (!parentRoute?.children || parentRoute.children.length === 0) { - console.log('父路由没有子路由'); return []; } diff --git a/schoolNewsWeb/src/views/admin/AdminLayout.vue b/schoolNewsWeb/src/views/admin/AdminLayout.vue new file mode 100644 index 0000000..300c099 --- /dev/null +++ b/schoolNewsWeb/src/views/admin/AdminLayout.vue @@ -0,0 +1,172 @@ + + + + + diff --git a/schoolNewsWeb/src/views/admin/index.ts b/schoolNewsWeb/src/views/admin/index.ts new file mode 100644 index 0000000..9211fb9 --- /dev/null +++ b/schoolNewsWeb/src/views/admin/index.ts @@ -0,0 +1 @@ +export { default as AdminLayout } from './AdminLayout.vue'; \ No newline at end of file diff --git a/schoolNewsWeb/src/views/admin/manage/achievement/AchievementManagementView.vue b/schoolNewsWeb/src/views/admin/manage/achievement/AchievementManagementView.vue index 03b1d50..29cfafe 100644 --- a/schoolNewsWeb/src/views/admin/manage/achievement/AchievementManagementView.vue +++ b/schoolNewsWeb/src/views/admin/manage/achievement/AchievementManagementView.vue @@ -1,12 +1,16 @@ diff --git a/schoolNewsWeb/src/views/admin/manage/ai/KnowledgeManagementView.vue b/schoolNewsWeb/src/views/admin/manage/ai/KnowledgeManagementView.vue index 0e94045..b8a5d6a 100644 --- a/schoolNewsWeb/src/views/admin/manage/ai/KnowledgeManagementView.vue +++ b/schoolNewsWeb/src/views/admin/manage/ai/KnowledgeManagementView.vue @@ -1,58 +1,63 @@ - - diff --git a/schoolNewsWeb/src/views/admin/manage/content/ColumnManagementView.vue b/schoolNewsWeb/src/views/admin/manage/content/ColumnManagementView.vue index a2f8d55..64b3b90 100644 --- a/schoolNewsWeb/src/views/admin/manage/content/ColumnManagementView.vue +++ b/schoolNewsWeb/src/views/admin/manage/content/ColumnManagementView.vue @@ -1,33 +1,43 @@ - diff --git a/schoolNewsWeb/src/views/admin/manage/content/TagManagementView.vue b/schoolNewsWeb/src/views/admin/manage/content/TagManagementView.vue index ea751f8..54b696a 100644 --- a/schoolNewsWeb/src/views/admin/manage/content/TagManagementView.vue +++ b/schoolNewsWeb/src/views/admin/manage/content/TagManagementView.vue @@ -1,53 +1,49 @@ diff --git a/schoolNewsWeb/src/views/admin/manage/crontab/LogManagementView.vue b/schoolNewsWeb/src/views/admin/manage/crontab/LogManagementView.vue index 1e08a6a..af19dd8 100644 --- a/schoolNewsWeb/src/views/admin/manage/crontab/LogManagementView.vue +++ b/schoolNewsWeb/src/views/admin/manage/crontab/LogManagementView.vue @@ -1,218 +1,220 @@ - + - + @@ -36,7 +36,7 @@ v-if="editMode" :as-dialog="false" list-type="cover" - v-model:cover-url="currentCourseVO.course.coverImage" + v-model:cover-url="currentCourseItemVO.coverImage" accept="image/*" :max-size="2" module="course" @@ -45,24 +45,24 @@ @remove="handleCoverRemove" />
- 课程封面 + 课程封面 暂无封面
- + - + 分钟 - + - + - + 未上线 @@ -115,19 +115,19 @@ -
+
暂无章节,请点击"添加章节"按钮添加