diff --git a/schoolNewsServ/.bin/mysql/sql/createTableLearning.sql b/schoolNewsServ/.bin/mysql/sql/createTableLearning.sql index 9203a1f..590e8d8 100644 --- a/schoolNewsServ/.bin/mysql/sql/createTableLearning.sql +++ b/schoolNewsServ/.bin/mysql/sql/createTableLearning.sql @@ -21,6 +21,23 @@ CREATE TABLE `tb_learning_task` ( KEY `idx_time` (`start_time`, `end_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='学习任务表'; +DROP TABLE IF EXISTS `tb_learning_task_tag`; +CREATE TABLE `tb_learning_task_tag` ( + `id` VARCHAR(50) NOT NULL COMMENT '关联ID', + `task_id` VARCHAR(50) NOT NULL COMMENT '任务ID', + `tag_id` VARCHAR(50) NOT NULL COMMENT '标签ID', + `creator` VARCHAR(50) DEFAULT NULL COMMENT '创建者', + `create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_task_tag` (`task_id`, `tag_id`), + KEY `idx_task_id` (`task_id`), + KEY `idx_tag_id` (`tag_id`), + CONSTRAINT `fk_task_tag_task` FOREIGN KEY (`task_id`) REFERENCES `tb_learning_task` (`task_id`) ON DELETE CASCADE, + CONSTRAINT `fk_task_tag_tag` FOREIGN KEY (`tag_id`) REFERENCES `tb_tag` (`tag_id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='学习任务标签关联表'; + + + -- 任务项关联表(统一管理资源和课程) DROP TABLE IF EXISTS `tb_task_item`; CREATE TABLE `tb_task_item` ( diff --git a/schoolNewsServ/.bin/mysql/sql/createTableResource.sql b/schoolNewsServ/.bin/mysql/sql/createTableResource.sql index 2ee047a..1aa7628 100644 --- a/schoolNewsServ/.bin/mysql/sql/createTableResource.sql +++ b/schoolNewsServ/.bin/mysql/sql/createTableResource.sql @@ -63,6 +63,7 @@ DROP TABLE IF EXISTS `tb_resource_recommend`; CREATE TABLE `tb_resource_recommend` ( `id` VARCHAR(50) NOT NULL COMMENT '推荐ID', `resource_id` VARCHAR(50) NOT NULL COMMENT '资源ID', + `recommend_type` INT(4) DEFAULT 1 COMMENT '推荐类型(1-热门资源,2-思政资源)', `order_num` INT(4) DEFAULT 0 COMMENT '排序号', `reason` VARCHAR(255) DEFAULT NULL COMMENT '推荐理由', `creator` VARCHAR(50) DEFAULT NULL COMMENT '创建者', @@ -72,8 +73,9 @@ CREATE TABLE `tb_resource_recommend` ( `delete_time` TIMESTAMP NULL DEFAULT NULL COMMENT '删除时间', `deleted` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否删除', PRIMARY KEY (`id`), - UNIQUE KEY `uk_resource` (`resource_id`), - KEY `idx_order` (`order_num`) + KEY `idx_resource_id` (`resource_id`), + KEY `idx_recommend_type` (`recommend_type`), + CONSTRAINT `fk_resource_recommend_resource` FOREIGN KEY (`resource_id`) REFERENCES `tb_resource` (`resource_id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='资源推荐表'; -- 标签表 diff --git a/schoolNewsServ/.bin/mysql/sql/initMenuData.sql b/schoolNewsServ/.bin/mysql/sql/initMenuData.sql index 62be18f..cfbc7ae 100644 --- a/schoolNewsServ/.bin/mysql/sql/initMenuData.sql +++ b/schoolNewsServ/.bin/mysql/sql/initMenuData.sql @@ -89,6 +89,7 @@ INSERT INTO `tb_sys_role_permission` (id, role_id, permission_id, creator, creat INSERT INTO `tb_sys_menu` VALUES -- 用户前端菜单 (100-699) ('100', 'menu_home', '首页', NULL, '/home', 'user/home/HomeView', NULL, 1, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0), +('101', 'menu_resource_hot', '热门资源', NULL, '/resource-hot', 'user/resource-center/HotResourceView', NULL, 2, 3, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0), ('200', 'menu_resource_center', '资源中心', NULL, '/resource-center', 'user/resource-center/ResourceCenterView', NULL, 2, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0), ('300', 'menu_study_plan', '学习计划', NULL, '/study-plan', 'user/study-plan/StudyPlanView', NULL, 3, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), ('301', 'menu_study_tasks', '学习任务', 'menu_study_plan', '/study-plan/tasks', 'user/study-plan/StudyTasksView', NULL, 1, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), @@ -146,6 +147,7 @@ INSERT INTO `tb_sys_menu` VALUES INSERT INTO `tb_sys_menu_permission` (id, permission_id, menu_id, creator, create_time) VALUES -- 前端菜单权限关联 ('100', 'perm_default', 'menu_home', '1', now()), +('102', 'perm_default', 'menu_resource_hot', '1', now()), ('101', 'perm_default', 'menu_resource_center', '1', now()), ('108', 'perm_default', 'menu_study_plan', '1', now()), ('109', 'perm_default', 'menu_study_tasks', '1', now()), diff --git a/schoolNewsServ/api/api-news/src/main/java/org/xyzh/api/news/recommend/ResourceRecommendService.java b/schoolNewsServ/api/api-news/src/main/java/org/xyzh/api/news/recommend/ResourceRecommendService.java index 72ebf47..9e8b3d5 100644 --- a/schoolNewsServ/api/api-news/src/main/java/org/xyzh/api/news/recommend/ResourceRecommendService.java +++ b/schoolNewsServ/api/api-news/src/main/java/org/xyzh/api/news/recommend/ResourceRecommendService.java @@ -1,7 +1,9 @@ package org.xyzh.api.news.recommend; import org.xyzh.common.core.domain.ResultDomain; +import org.xyzh.common.core.page.PageParam; import org.xyzh.common.dto.resource.TbResourceRecommend; +import org.xyzh.common.vo.ResourceRecommendVO; import java.util.List; @@ -15,12 +17,12 @@ import java.util.List; public interface ResourceRecommendService { /** - * @description 获取推荐资源列表 - * @return ResultDomain 推荐资源列表 + * @description 获取推荐资源列表(包含资源详情和权限信息) + * @return ResultDomain 推荐资源列表 * @author yslg * @since 2025-10-15 */ - ResultDomain getRecommendList(); + ResultDomain getRecommendList(); /** * @description 添加推荐资源 @@ -89,12 +91,13 @@ public interface ResourceRecommendService { /** * @description 批量添加推荐资源 * @param resourceIDs 资源ID列表 + * @param recommendType 推荐类型 * @param reason 推荐理由 * @return ResultDomain 添加结果 * @author yslg * @since 2025-10-15 */ - ResultDomain batchAddRecommends(List resourceIDs, String reason); + ResultDomain batchAddRecommends(List resourceIDs, Integer recommendType, String reason); /** * @description 批量移除推荐资源 @@ -113,4 +116,43 @@ public interface ResourceRecommendService { * @since 2025-10-15 */ ResultDomain getRecommendDetail(String recommendID); + + /** + * @description 分页查询推荐资源列表(包含资源详情) + * @param filter 过滤条件 + * @param pageParam 分页参数 + * @return ResultDomain 推荐资源分页列表 + * @author yslg + * @since 2025-01-XX + */ + ResultDomain getRecommendPage(TbResourceRecommend filter, PageParam pageParam); + + /** + * @description 统计推荐资源总数 + * @param filter 过滤条件 + * @return ResultDomain 总数 + * @author yslg + * @since 2025-01-XX + */ + ResultDomain countRecommends(TbResourceRecommend filter); + + /** + * @description 根据推荐类型获取推荐资源列表(包含资源详情) + * @param recommendType 推荐类型(1-热门资源,2-思政资源) + * @param limit 限制数量 + * @return ResultDomain 推荐资源列表 + * @author yslg + * @since 2025-01-XX + */ + ResultDomain getRecommendsByType(Integer recommendType, Integer limit); + + /** + * @description 根据推荐类型和资源ID检查是否已推荐 + * @param resourceID 资源ID + * @param recommendType 推荐类型 + * @return ResultDomain 是否已推荐 + * @author yslg + * @since 2025-01-XX + */ + ResultDomain isResourceRecommendedByType(String resourceID, Integer recommendType); } diff --git a/schoolNewsServ/api/api-news/src/main/java/org/xyzh/api/news/resource/ResourceService.java b/schoolNewsServ/api/api-news/src/main/java/org/xyzh/api/news/resource/ResourceService.java index fcb8041..f7312ac 100644 --- a/schoolNewsServ/api/api-news/src/main/java/org/xyzh/api/news/resource/ResourceService.java +++ b/schoolNewsServ/api/api-news/src/main/java/org/xyzh/api/news/resource/ResourceService.java @@ -35,6 +35,15 @@ public interface ResourceService { */ ResultDomain getResourcePage(TbResource filter, PageParam pageParam); + /** + * @description 获取资源分页(按浏览次数排序) + * @param filter 过滤条件 + * @param pageParam 分页参数 + * @return ResultDomain 资源分页 + * @author yslg + * @since 2025-10-15 + */ + ResultDomain getResourcePageOrderByViewCount(TbResource filter, PageParam pageParam); /** * @description 根据ID获取资源详情 * @param resourceID 资源ID diff --git a/schoolNewsServ/api/api-news/src/main/java/org/xyzh/api/news/tag/TagService.java b/schoolNewsServ/api/api-news/src/main/java/org/xyzh/api/news/tag/TagService.java index 5f8f545..66ac502 100644 --- a/schoolNewsServ/api/api-news/src/main/java/org/xyzh/api/news/tag/TagService.java +++ b/schoolNewsServ/api/api-news/src/main/java/org/xyzh/api/news/tag/TagService.java @@ -21,7 +21,7 @@ public interface TagService { * @author yslg * @since 2025-10-15 */ - ResultDomain getAllTags(); + ResultDomain getTagList(TbTag filter); /** * @description 根据ID获取标签详情 diff --git a/schoolNewsServ/api/api-usercenter/src/main/java/org/xyzh/api/usercenter/collection/UserCollectionService.java b/schoolNewsServ/api/api-usercenter/src/main/java/org/xyzh/api/usercenter/collection/UserCollectionService.java index 30a91bc..7f68001 100644 --- a/schoolNewsServ/api/api-usercenter/src/main/java/org/xyzh/api/usercenter/collection/UserCollectionService.java +++ b/schoolNewsServ/api/api-usercenter/src/main/java/org/xyzh/api/usercenter/collection/UserCollectionService.java @@ -2,6 +2,7 @@ package org.xyzh.api.usercenter.collection; import org.xyzh.common.core.domain.ResultDomain; import org.xyzh.common.dto.usercenter.TbUserCollection; +import org.xyzh.common.vo.UserCollectionVO; /** * @description 用户收藏服务接口 @@ -42,14 +43,14 @@ public interface UserCollectionService { ResultDomain isCollected(String userID, Integer collectionType, String collectionID); /** - * @description 获取用户收藏列表 + * @description 获取用户收藏列表(扁平化VO,包含详情) * @param userID 用户ID * @param collectionType 收藏类型(可选) - * @return ResultDomain 收藏列表 + * @return ResultDomain 收藏列表(包含资源/课程详情) * @author yslg - * @since 2025-10-15 + * @since 2025-10-31 */ - ResultDomain getUserCollections(String userID, Integer collectionType); + ResultDomain getUserCollections(String userID, Integer collectionType); /** * @description 获取收藏详情 diff --git a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/resource/TbResourceRecommend.java b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/resource/TbResourceRecommend.java index 6480364..dbd9ae4 100644 --- a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/resource/TbResourceRecommend.java +++ b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/resource/TbResourceRecommend.java @@ -23,6 +23,11 @@ public class TbResourceRecommend extends BaseDTO { */ private Integer orderNum; + /** + * @description 推荐类型(1-热门资源,2-思政资源) + */ + private Integer recommendType; + /** * @description 推荐理由 */ @@ -54,6 +59,14 @@ public class TbResourceRecommend extends BaseDTO { this.orderNum = orderNum; } + public Integer getRecommendType() { + return recommendType; + } + + public void setRecommendType(Integer recommendType) { + this.recommendType = recommendType; + } + public String getReason() { return reason; } @@ -83,6 +96,7 @@ public class TbResourceRecommend extends BaseDTO { return "TbResourceRecommend{" + "id=" + getID() + ", resourceID='" + resourceID + '\'' + + ", recommendType=" + recommendType + ", orderNum=" + orderNum + ", reason='" + reason + '\'' + ", createTime=" + getCreateTime() + diff --git a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/study/TbLearningTaskTag.java b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/study/TbLearningTaskTag.java new file mode 100644 index 0000000..fcc5735 --- /dev/null +++ b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/study/TbLearningTaskTag.java @@ -0,0 +1,65 @@ +package org.xyzh.common.dto.study; + +import org.xyzh.common.dto.BaseDTO; + +/** + * @description 学习任务标签关联实体 + * @filename TbLearningTaskTag.java + * @author yslg + * @copyright xyzh + * @since 2025-10-31 + */ +public class TbLearningTaskTag extends BaseDTO { + private static final long serialVersionUID = 1L; + + /** + * 任务ID + */ + private String taskID; + + /** + * 标签ID + */ + private String tagID; + + /** + * 创建者 + */ + private String creator; + + public String getTaskID() { + return taskID; + } + + public void setTaskID(String taskID) { + this.taskID = taskID; + } + + public String getTagID() { + return tagID; + } + + public void setTagID(String tagID) { + this.tagID = tagID; + } + + public String getCreator() { + return creator; + } + + public void setCreator(String creator) { + this.creator = creator; + } + + @Override + public String toString() { + return "TbLearningTaskTag{" + + "id='" + getID() + '\'' + + ", taskID='" + taskID + '\'' + + ", tagID='" + tagID + '\'' + + ", creator='" + creator + '\'' + + ", createTime=" + getCreateTime() + + '}'; + } +} + diff --git a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/ResourceRecommendVO.java b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/ResourceRecommendVO.java new file mode 100644 index 0000000..5827691 --- /dev/null +++ b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/ResourceRecommendVO.java @@ -0,0 +1,422 @@ +package org.xyzh.common.vo; + +import java.io.Serializable; +import java.util.Date; + +/** + * @description 资源推荐VO(用于管理端,平铺结构) + * @filename ResourceRecommendVO.java + * @author yslg + * @copyright xyzh + * @since 2025-01-XX + */ +public class ResourceRecommendVO implements Serializable { + private static final long serialVersionUID = 1L; + + // ==================== 推荐表字段 ==================== + /** + * 推荐ID + */ + private String id; + + /** + * 资源ID + */ + private String resourceID; + + /** + * 推荐类型(1-热门资源,2-思政资源) + */ + private Integer recommendType; + + /** + * 排序号 + */ + private Integer orderNum; + + /** + * 推荐理由 + */ + private String reason; + + /** + * 推荐创建者 + */ + private String creator; + + /** + * 推荐更新者 + */ + private String updater; + + /** + * 推荐创建时间 + */ + private Date createTime; + + /** + * 推荐更新时间 + */ + private Date updateTime; + + /** + * 推荐删除时间 + */ + private Date deleteTime; + + /** + * 推荐是否删除 + */ + private Boolean deleted; + + // ==================== 资源表字段 ==================== + /** + * 资源标题 + */ + private String title; + + /** + * 资源简介 + */ + private String summary; + + /** + * 封面图片 + */ + private String coverImage; + + /** + * 标签ID(文章分类标签,tagType=1) + */ + private String tagID; + + /** + * 作者 + */ + private String author; + + /** + * 来源 + */ + private String source; + + /** + * 来源URL + */ + private String sourceUrl; + + /** + * 浏览次数 + */ + private Integer viewCount; + + /** + * 点赞次数 + */ + private Integer likeCount; + + /** + * 收藏次数 + */ + private Integer collectCount; + + /** + * 状态(0草稿 1已发布 2下架) + */ + private Integer status; + + /** + * 是否推荐 + */ + private Boolean isRecommend; + + /** + * 是否轮播 + */ + private Boolean isBanner; + + /** + * 发布时间 + */ + private Date publishTime; + + /** + * 资源创建者 + */ + private String resourceCreator; + + /** + * 资源更新者 + */ + private String resourceUpdater; + + /** + * 资源创建时间 + */ + private Date resourceCreateTime; + + /** + * 资源更新时间 + */ + private Date resourceUpdateTime; + + /** + * 当前用户是否有读权限 + */ + private Boolean canRead; + + /** + * 当前用户是否有写权限 + */ + private Boolean canWrite; + + // ==================== Getter and Setter ==================== + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getResourceID() { + return resourceID; + } + + public void setResourceID(String resourceID) { + this.resourceID = resourceID; + } + + public Integer getRecommendType() { + return recommendType; + } + + public void setRecommendType(Integer recommendType) { + this.recommendType = recommendType; + } + + public Integer getOrderNum() { + return orderNum; + } + + public void setOrderNum(Integer orderNum) { + this.orderNum = orderNum; + } + + public String getReason() { + return reason; + } + + public void setReason(String reason) { + this.reason = reason; + } + + public String getCreator() { + return creator; + } + + public void setCreator(String creator) { + this.creator = creator; + } + + public String getUpdater() { + return updater; + } + + public void setUpdater(String updater) { + this.updater = updater; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + public Date getDeleteTime() { + return deleteTime; + } + + public void setDeleteTime(Date deleteTime) { + this.deleteTime = deleteTime; + } + + public Boolean getDeleted() { + return deleted; + } + + public void setDeleted(Boolean deleted) { + this.deleted = deleted; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getSummary() { + return summary; + } + + public void setSummary(String summary) { + this.summary = summary; + } + + public String getCoverImage() { + return coverImage; + } + + public void setCoverImage(String coverImage) { + this.coverImage = coverImage; + } + + public String getTagID() { + return tagID; + } + + public void setTagID(String tagID) { + this.tagID = tagID; + } + + public String getAuthor() { + return author; + } + + public void setAuthor(String author) { + this.author = author; + } + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } + + public String getSourceUrl() { + return sourceUrl; + } + + public void setSourceUrl(String sourceUrl) { + this.sourceUrl = sourceUrl; + } + + public Integer getViewCount() { + return viewCount; + } + + public void setViewCount(Integer viewCount) { + this.viewCount = viewCount; + } + + public Integer getLikeCount() { + return likeCount; + } + + public void setLikeCount(Integer likeCount) { + this.likeCount = likeCount; + } + + public Integer getCollectCount() { + return collectCount; + } + + public void setCollectCount(Integer collectCount) { + this.collectCount = collectCount; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Boolean getIsRecommend() { + return isRecommend; + } + + public void setIsRecommend(Boolean isRecommend) { + this.isRecommend = isRecommend; + } + + public Boolean getIsBanner() { + return isBanner; + } + + public void setIsBanner(Boolean isBanner) { + this.isBanner = isBanner; + } + + public Date getPublishTime() { + return publishTime; + } + + public void setPublishTime(Date publishTime) { + this.publishTime = publishTime; + } + + public String getResourceCreator() { + return resourceCreator; + } + + public void setResourceCreator(String resourceCreator) { + this.resourceCreator = resourceCreator; + } + + public String getResourceUpdater() { + return resourceUpdater; + } + + public void setResourceUpdater(String resourceUpdater) { + this.resourceUpdater = resourceUpdater; + } + + public Date getResourceCreateTime() { + return resourceCreateTime; + } + + public void setResourceCreateTime(Date resourceCreateTime) { + this.resourceCreateTime = resourceCreateTime; + } + + public Date getResourceUpdateTime() { + return resourceUpdateTime; + } + + public void setResourceUpdateTime(Date resourceUpdateTime) { + this.resourceUpdateTime = resourceUpdateTime; + } + + public Boolean getCanRead() { + return canRead; + } + + public void setCanRead(Boolean canRead) { + this.canRead = canRead; + } + + public Boolean getCanWrite() { + return canWrite; + } + + public void setCanWrite(Boolean canWrite) { + this.canWrite = canWrite; + } +} diff --git a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/ResourceVO.java b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/ResourceVO.java index 3ebccb9..35eb5cc 100644 --- a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/ResourceVO.java +++ b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/ResourceVO.java @@ -23,6 +23,16 @@ public class ResourceVO implements Serializable{ * 资源标签列表(包含文章分类标签 tag_type=1) */ private List tags; + + /** + * 是否推荐(用于首页展示) + */ + private Boolean isRecommended; + + /** + * 推荐类型(1-热门资源,2-思政资源) + */ + private Integer recommendType; public TbResource getResource() { return resource; @@ -39,4 +49,20 @@ public class ResourceVO implements Serializable{ public void setTags(List tags) { this.tags = tags; } + + public Boolean getIsRecommended() { + return isRecommended; + } + + public void setIsRecommended(Boolean isRecommended) { + this.isRecommended = isRecommended; + } + + public Integer getRecommendType() { + return recommendType; + } + + public void setRecommendType(Integer recommendType) { + this.recommendType = recommendType; + } } diff --git a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/TaskVO.java b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/TaskVO.java index cf10df3..fc8a0c4 100644 --- a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/TaskVO.java +++ b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/TaskVO.java @@ -4,6 +4,7 @@ import org.xyzh.common.dto.BaseDTO; import org.xyzh.common.dto.study.TbLearningTask; import org.xyzh.common.dto.study.TbTaskItem; import org.xyzh.common.dto.study.TbTaskUser; +import org.xyzh.common.dto.resource.TbTag; import java.util.List; import java.util.stream.Collectors; @@ -15,6 +16,7 @@ public class TaskVO extends BaseDTO{ private List taskCourses; private List taskResources; private List taskUsers; + private List taskTags; private Integer totalTaskNum; private Integer completedTaskNum; private Integer learningTaskNum; @@ -85,6 +87,14 @@ public class TaskVO extends BaseDTO{ public List toTaskUsers() { return getTaskUsers().stream().map(TaskItemVO::toTaskUser).collect(Collectors.toList()); } + + public List getTaskTags() { + return taskTags; + } + + public void setTaskTags(List taskTags) { + this.taskTags = taskTags; + } public void setTaskItemList(List taskItems) { if (taskItems == null) { diff --git a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/UserCollectionVO.java b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/UserCollectionVO.java new file mode 100644 index 0000000..1b8d48a --- /dev/null +++ b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/UserCollectionVO.java @@ -0,0 +1,359 @@ +package org.xyzh.common.vo; + +import org.xyzh.common.dto.BaseDTO; +import java.util.Date; + +/** + * @description 用户收藏VO - 扁平化收藏信息和关联资源/课程详情 + * @filename UserCollectionVO.java + * @author yslg + * @copyright xyzh + * @since 2025-10-31 + */ +public class UserCollectionVO extends BaseDTO { + + private static final long serialVersionUID = 1L; + + // ========== 收藏基本信息 ========== + /** + * @description 用户ID + */ + private String userID; + + /** + * @description 收藏类型(1资源 2课程) + */ + private Integer collectionType; + + /** + * @description 收藏对象ID + */ + private String collectionID; + + private Integer collectionValue; + + /** + * @description 收藏时间 + */ + private Date collectionTime; + + // ========== 资源详情(collectionType=1时有效) ========== + /** + * @description 资源ID + */ + private String resourceID; + + /** + * @description 资源标题 + */ + private String title; + + /** + * @description 资源内容 + */ + private String content; + + /** + * @description 资源简介 + */ + private String summary; + + /** + * @description 封面图片 + */ + private String coverImage; + + /** + * @description 标签ID + */ + private String tagID; + + /** + * @description 作者 + */ + private String author; + + /** + * @description 来源 + */ + private String source; + + /** + * @description 浏览次数 + */ + private Integer viewCount; + + /** + * @description 点赞次数 + */ + private Integer likeCount; + + /** + * @description 收藏次数 + */ + private Integer collectCount; + + /** + * @description 资源状态 + */ + private Integer status; + + /** + * @description 发布时间 + */ + private Date publishTime; + + // ========== 课程详情(collectionType=2时有效) ========== + /** + * @description 课程ID + */ + private String courseID; + + /** + * @description 课程名称 + */ + private String courseName; + + /** + * @description 课程描述 + */ + private String description; + + /** + * @description 课程时长(分钟) + */ + private Integer duration; + + /** + * @description 授课老师 + */ + private String teacher; + + /** + * @description 课程状态 + */ + private Integer courseStatus; + + /** + * @description 学习人数 + */ + private Integer learnCount; + + // ========== Getters and Setters ========== + + public String getUserID() { + return userID; + } + + public void setUserID(String userID) { + this.userID = userID; + } + + public Integer getCollectionType() { + return collectionType; + } + + public void setCollectionType(Integer collectionType) { + this.collectionType = collectionType; + } + + public String getCollectionID() { + return collectionID; + } + + public void setCollectionID(String collectionID) { + this.collectionID = collectionID; + } + + public Integer getCollectionValue() { + return collectionValue; + } + + public void setCollectionValue(Integer collectionValue) { + this.collectionValue = collectionValue; + } + + public Date getCollectionTime() { + return collectionTime; + } + + public void setCollectionTime(Date collectionTime) { + this.collectionTime = collectionTime; + } + + public String getResourceID() { + return resourceID; + } + + public void setResourceID(String resourceID) { + this.resourceID = resourceID; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getSummary() { + return summary; + } + + public void setSummary(String summary) { + this.summary = summary; + } + + public String getCoverImage() { + return coverImage; + } + + public void setCoverImage(String coverImage) { + this.coverImage = coverImage; + } + + public String getTagID() { + return tagID; + } + + public void setTagID(String tagID) { + this.tagID = tagID; + } + + public String getAuthor() { + return author; + } + + public void setAuthor(String author) { + this.author = author; + } + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } + + public Integer getViewCount() { + return viewCount; + } + + public void setViewCount(Integer viewCount) { + this.viewCount = viewCount; + } + + public Integer getLikeCount() { + return likeCount; + } + + public void setLikeCount(Integer likeCount) { + this.likeCount = likeCount; + } + + public Integer getCollectCount() { + return collectCount; + } + + public void setCollectCount(Integer collectCount) { + this.collectCount = collectCount; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Date getPublishTime() { + return publishTime; + } + + public void setPublishTime(Date publishTime) { + this.publishTime = publishTime; + } + + public String getCourseID() { + return courseID; + } + + public void setCourseID(String courseID) { + this.courseID = courseID; + } + + public String getCourseName() { + return courseName; + } + + public void setCourseName(String courseName) { + this.courseName = courseName; + } + + 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 getCourseStatus() { + return courseStatus; + } + + public void setCourseStatus(Integer courseStatus) { + this.courseStatus = courseStatus; + } + + public Integer getLearnCount() { + return learnCount; + } + + public void setLearnCount(Integer learnCount) { + this.learnCount = learnCount; + } + + @Override + public String toString() { + return "UserCollectionVO{" + + "id=" + getID() + + ", userID='" + userID + '\'' + + ", collectionType=" + collectionType + + ", collectionID='" + collectionID + '\'' + + ", collectionTime=" + collectionTime + + ", resourceID='" + resourceID + '\'' + + ", title='" + title + '\'' + + ", courseID='" + courseID + '\'' + + ", courseName='" + courseName + '\'' + + '}'; + } +} + diff --git a/schoolNewsServ/news/src/main/java/org/xyzh/news/controller/HomePageController.java b/schoolNewsServ/news/src/main/java/org/xyzh/news/controller/HomePageController.java index 8e8cb5b..d97d357 100644 --- a/schoolNewsServ/news/src/main/java/org/xyzh/news/controller/HomePageController.java +++ b/schoolNewsServ/news/src/main/java/org/xyzh/news/controller/HomePageController.java @@ -2,10 +2,13 @@ package org.xyzh.news.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import org.xyzh.api.news.recommend.ResourceRecommendService; import org.xyzh.common.core.domain.ResultDomain; import org.xyzh.common.dto.resource.TbResource; import org.xyzh.common.dto.resource.TbBanner; +import org.xyzh.common.vo.ResourceRecommendVO; import java.util.Map; @@ -21,197 +24,23 @@ import java.util.Map; public class HomePageController { private static final Logger logger = LoggerFactory.getLogger(HomePageController.class); - // ==================== 轮播组件管理 ==================== + @Autowired + private ResourceRecommendService resourceRecommendService; /** - * 获取轮播组件数据 + * 获取热门资源列表 */ - @GetMapping("/banner/list") - public ResultDomain getBannerList() { - // TODO: 实现获取轮播组件数据(自动轮播核心新闻,支持手动切换) - return null; + @GetMapping("/recommend/hot") + public ResultDomain getHotResources(@RequestParam(required = false, name = "limit") Integer limit) { + return resourceRecommendService.getRecommendsByType(1, limit); // 1-热门资源 } - /** - * 点击轮播跳转新闻详情 - */ - @GetMapping("/banner/click/{bannerID}") - public ResultDomain getBannerNewsDetail(@PathVariable String bannerID) { - // TODO: 实现点击跳转新闻详情 - return null; - } /** - * 获取活跃轮播列表 + * 获取思政资源列表 */ - @GetMapping("/banner/active") - public ResultDomain getActiveBanners() { - // TODO: 实现获取活跃轮播列表 - return null; - } - - // ==================== TOP资源推荐 ==================== - - /** - * 获取TOP资源推荐列表 - */ - @GetMapping("/recommend/top-list") - public ResultDomain getTopRecommendList() { - // TODO: 实现获取TOP资源推荐列表(按浏览数据展示高热度新闻) - return null; - } - - /** - * 后台调控展示顺序 - */ - @PutMapping("/recommend/order") - public ResultDomain updateRecommendOrder(@RequestBody Map orderData) { - // TODO: 实现后台调控展示顺序与内容 - return null; - } - - /** - * 获取高热度新闻 - */ - @GetMapping("/recommend/hot-news") - public ResultDomain getHotNews(@RequestParam(required = false) Integer limit) { - // TODO: 实现获取高热度新闻 - return null; - } - - // ==================== 思政新闻概览 ==================== - - /** - * 获取思政新闻概览 - */ - @GetMapping("/news/overview") - public ResultDomain getNewsOverview( - @RequestParam(required = false) Integer pageNum, - @RequestParam(required = false) Integer pageSize) { - // TODO: 实现获取思政新闻概览(以卡片形式展示新闻标题、发布时间、简介) - return null; - } - - /** - * 点击跳转二级详情页 - */ - @GetMapping("/news/detail/{newsID}") - public ResultDomain getNewsDetail(@PathVariable String newsID) { - // TODO: 实现点击跳转二级详情页 - return null; - } - - /** - * 获取最新思政新闻 - */ - @GetMapping("/news/latest") - public ResultDomain getLatestNews(@RequestParam(required = false) Integer limit) { - // TODO: 实现获取最新思政新闻 - return null; - } - - // ==================== 顶部菜单栏 ==================== - - /** - * 获取顶部菜单栏配置 - */ - @GetMapping("/menu/top-menu") - public ResultDomain> getTopMenuConfig() { - // TODO: 实现获取顶部菜单栏(包含首页、资源中心、学习计划、专题活动、红色常信) - return null; - } - - /** - * 后台修改菜单名称 - */ - @PutMapping("/menu/update-name") - public ResultDomain updateMenuName(@RequestBody Map menuData) { - // TODO: 实现支持后台修改菜单名称 - return null; - } - - /** - * 获取菜单项列表 - */ - @GetMapping("/menu/list") - public ResultDomain> getMenuList() { - // TODO: 实现获取菜单项列表 - return null; - } - - // ==================== 模糊检索 ==================== - - /** - * 模糊检索资源 - */ - @GetMapping("/search") - public ResultDomain searchResources(@RequestParam String keyword) { - // TODO: 实现模糊检索(输入关键词实时匹配资源(新闻、课程)) - return null; - } - - /** - * 实时搜索建议 - */ - @GetMapping("/search/suggestions") - public ResultDomain> getSearchSuggestions(@RequestParam String keyword) { - // TODO: 实现实时搜索建议 - return null; - } - - /** - * 获取热门搜索词 - */ - @GetMapping("/search/hot-keywords") - public ResultDomain> getHotKeywords() { - // TODO: 实现获取热门搜索词 - return null; - } - - /** - * 搜索新闻 - */ - @GetMapping("/search/news") - public ResultDomain searchNews(@RequestParam String keyword) { - // TODO: 实现搜索新闻 - return null; - } - - /** - * 搜索课程 - */ - @GetMapping("/search/courses") - public ResultDomain> searchCourses(@RequestParam String keyword) { - // TODO: 实现搜索课程 - return null; - } - - // ==================== 首页统计数据 ==================== - - /** - * 获取首页统计数据 - */ - @GetMapping("/statistics") - public ResultDomain> getHomePageStatistics() { - // TODO: 实现获取首页统计数据 - return null; - } - - /** - * 获取今日访问量 - */ - @GetMapping("/statistics/today-visits") - public ResultDomain> getTodayVisits() { - // TODO: 实现获取今日访问量 - return null; - } - - /** - * 获取资源总数 - */ - @GetMapping("/statistics/total-resources") - public ResultDomain> getTotalResources() { - // TODO: 实现获取资源总数 - return null; + @GetMapping("/recommend/ideological") + public ResultDomain getIdeologicalResources(@RequestParam(required = false, name = "limit") Integer limit) { + return resourceRecommendService.getRecommendsByType(2, limit); // 2-思政资源 } } diff --git a/schoolNewsServ/news/src/main/java/org/xyzh/news/controller/ResourceController.java b/schoolNewsServ/news/src/main/java/org/xyzh/news/controller/ResourceController.java index 0740004..b0d1291 100644 --- a/schoolNewsServ/news/src/main/java/org/xyzh/news/controller/ResourceController.java +++ b/schoolNewsServ/news/src/main/java/org/xyzh/news/controller/ResourceController.java @@ -51,6 +51,16 @@ public class ResourceController { return resourceService.getResourcePage(filter, pageParam); } + /** + * 获取资源分页(按浏览次数排序) + */ + @PostMapping("/page/view-count") + public ResultDomain getResourcePageOrderByViewCount(@RequestBody PageRequest request) { + TbResource filter = request.getFilter(); + PageParam pageParam = request.getPageParam(); + return resourceService.getResourcePageOrderByViewCount(filter, pageParam); + } + /** * 根据ID获取资源详情 */ @@ -136,14 +146,6 @@ public class ResourceController { return resourceService.incrementLikeCount(resourceID); } - /** - * 增加收藏次数 - */ - @PostMapping("/resource/collect") - public ResultDomain resourceCollect(@RequestBody TbUserCollection collection) { - return resourceService.resourceCollect(collection); - } - /** * 设置资源推荐 */ diff --git a/schoolNewsServ/news/src/main/java/org/xyzh/news/controller/ResourceRecommendController.java b/schoolNewsServ/news/src/main/java/org/xyzh/news/controller/ResourceRecommendController.java index daf6ec5..3903635 100644 --- a/schoolNewsServ/news/src/main/java/org/xyzh/news/controller/ResourceRecommendController.java +++ b/schoolNewsServ/news/src/main/java/org/xyzh/news/controller/ResourceRecommendController.java @@ -6,7 +6,15 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.xyzh.api.news.recommend.ResourceRecommendService; 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.resource.TbResourceRecommend; +import org.xyzh.common.dto.user.TbSysUser; +import org.xyzh.common.vo.ResourceRecommendVO; +import org.xyzh.system.utils.LoginUtil; + +import java.util.List; +import java.util.Map; /** * @description 资源推荐控制器 @@ -24,19 +32,36 @@ public class ResourceRecommendController { private ResourceRecommendService resourceRecommendService; /** - * 获取推荐列表 + * 获取推荐列表(返回VO,包含权限信息) */ @GetMapping("/list") - public ResultDomain getRecommendList() { - return resourceRecommendService.getRecommendList(); + public ResultDomain getRecommendList() { + ResultDomain result = resourceRecommendService.getRecommendList(); + + return result; } /** - * 根据ID获取推荐详情 + * 根据ID获取推荐详情(返回VO,包含权限信息) */ @GetMapping("/recommend/{recommendID}") - public ResultDomain getRecommendById(@PathVariable String recommendID) { - return resourceRecommendService.getRecommendDetail(recommendID); + public ResultDomain getRecommendById(@PathVariable("recommendID") String recommendID) { + TbResourceRecommend filter = new TbResourceRecommend(); + filter.setID(recommendID); + PageParam pageParam = new PageParam(); + pageParam.setPageNumber(1); + pageParam.setPageSize(1); + ResultDomain result = resourceRecommendService.getRecommendPage(filter, pageParam); + // 从PageDomain中提取dataList的第一个元素 + if (result.getCode() == 200 && result.getPageDomain() != null + && result.getPageDomain().getDataList() != null && !result.getPageDomain().getDataList().isEmpty()) { + ResultDomain singleResult = new ResultDomain<>(); + singleResult.setCode(200); + singleResult.setMessage(result.getMessage()); + singleResult.setData(result.getPageDomain().getDataList().get(0)); + return singleResult; + } + return result; } /** @@ -44,6 +69,12 @@ public class ResourceRecommendController { */ @PostMapping("/recommend") public ResultDomain createRecommend(@RequestBody TbResourceRecommend recommend) { + TbSysUser user = LoginUtil.getCurrentUser(); + if (user != null) { + if (recommend.getCreator() == null) { + recommend.setCreator(user.getID()); + } + } return resourceRecommendService.addRecommend(recommend); } @@ -59,15 +90,26 @@ public class ResourceRecommendController { * 删除推荐 */ @DeleteMapping("/recommend/{recommendID}") - public ResultDomain deleteRecommend(@PathVariable String recommendID) { + public ResultDomain deleteRecommend(@PathVariable("recommendID") String recommendID) { return resourceRecommendService.deleteRecommend(recommendID); } + /** + * 批量添加推荐资源 + */ + @PostMapping("/recommend/batch") + public ResultDomain batchAddRecommends(@RequestBody Map params) { + List resourceIDs = (List) params.get("resourceIDs"); + Integer recommendType = (Integer) params.get("recommendType"); + String reason = (String) params.get("reason"); + return resourceRecommendService.batchAddRecommends(resourceIDs, recommendType, reason); + } + /** * 更新推荐状态 */ @PutMapping("/recommend/{recommendID}/status") - public ResultDomain updateRecommendStatus(@PathVariable String recommendID, @RequestParam Integer status) { + public ResultDomain updateRecommendStatus(@PathVariable("recommendID") String recommendID, @RequestParam("status") Integer status) { return null; // return resourceRecommendService.updateRecommendStatus(recommendID, status); } @@ -76,7 +118,7 @@ public class ResourceRecommendController { * 更新推荐排序 */ @PutMapping("/recommend/{recommendID}/order") - public ResultDomain updateRecommendOrder(@PathVariable String recommendID, @RequestParam Integer orderNum) { + public ResultDomain updateRecommendOrder(@PathVariable("recommendID") String recommendID, @RequestParam("orderNum") Integer orderNum) { return resourceRecommendService.updateRecommendOrder(recommendID, orderNum); } @@ -84,8 +126,46 @@ public class ResourceRecommendController { * 获取活跃推荐 */ @GetMapping("/active") - public ResultDomain getActiveRecommends(@RequestParam(required = false) Integer limit) { + public ResultDomain getActiveRecommends(@RequestParam(required = false, name = "limit") Integer limit) { return null; // return resourceRecommendService.getActiveRecommends(limit); } + + /** + * 分页查询推荐资源列表 + */ + @PostMapping("/page") + public ResultDomain getRecommendPage(@RequestBody PageRequest request) { + TbResourceRecommend filter = request.getFilter(); + PageParam pageParam = request.getPageParam(); + return resourceRecommendService.getRecommendPage(filter, pageParam); + } + + /** + * 统计推荐资源总数 + */ + @PostMapping("/count") + public ResultDomain countRecommends(@RequestBody TbResourceRecommend filter) { + return resourceRecommendService.countRecommends(filter); + } + + /** + * 根据推荐类型获取推荐资源列表 + */ + @GetMapping("/type/{recommendType}") + public ResultDomain getRecommendsByType( + @PathVariable("recommendType") Integer recommendType, + @RequestParam(required = false, name = "limit") Integer limit) { + return resourceRecommendService.getRecommendsByType(recommendType, limit); + } + + /** + * 检查资源是否已推荐(按类型) + */ + @GetMapping("/check/{resourceID}") + public ResultDomain isResourceRecommendedByType( + @PathVariable("resourceID") String resourceID, + @RequestParam(required = true, name = "recommendType") Integer recommendType) { + return resourceRecommendService.isResourceRecommendedByType(resourceID, recommendType); + } } diff --git a/schoolNewsServ/news/src/main/java/org/xyzh/news/controller/TagController.java b/schoolNewsServ/news/src/main/java/org/xyzh/news/controller/TagController.java index a53abc5..3bffdc6 100644 --- a/schoolNewsServ/news/src/main/java/org/xyzh/news/controller/TagController.java +++ b/schoolNewsServ/news/src/main/java/org/xyzh/news/controller/TagController.java @@ -30,8 +30,8 @@ public class TagController { * 获取标签列表 */ @GetMapping("/list") - public ResultDomain getTagList() { - return tagService.getAllTags(); + public ResultDomain getTagList(TbTag filter) { + return tagService.getTagList(filter); } /** diff --git a/schoolNewsServ/news/src/main/java/org/xyzh/news/mapper/ResourceMapper.java b/schoolNewsServ/news/src/main/java/org/xyzh/news/mapper/ResourceMapper.java index 5d69fcc..3cf8072 100644 --- a/schoolNewsServ/news/src/main/java/org/xyzh/news/mapper/ResourceMapper.java +++ b/schoolNewsServ/news/src/main/java/org/xyzh/news/mapper/ResourceMapper.java @@ -165,6 +165,18 @@ public interface ResourceMapper extends BaseMapper { */ List selectResourcesPage(@Param("filter") TbResource filter, @Param("pageParam") PageParam pageParam, @Param("userDeptRoles") List userDeptRoles); + /** + * @description 分页查询资源 + * @param filter 过滤条件 + * @param pageParam 分页参数 + * @param userDeptRoles 用户部门角色列表 + * @return List 资源列表 + * @author yslg + * @since 2025-10-15 + */ + List selectResourcesPageOrderByViewCount(@Param("filter") TbResource filter, @Param("pageParam") PageParam pageParam, @Param("userDeptRoles") List userDeptRoles); + + /** * @description 统计资源总数 * @param filter 过滤条件 diff --git a/schoolNewsServ/news/src/main/java/org/xyzh/news/mapper/ResourceRecommendMapper.java b/schoolNewsServ/news/src/main/java/org/xyzh/news/mapper/ResourceRecommendMapper.java index 509d026..cf962af 100644 --- a/schoolNewsServ/news/src/main/java/org/xyzh/news/mapper/ResourceRecommendMapper.java +++ b/schoolNewsServ/news/src/main/java/org/xyzh/news/mapper/ResourceRecommendMapper.java @@ -5,6 +5,8 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.xyzh.common.core.page.PageParam; import org.xyzh.common.dto.resource.TbResourceRecommend; +import org.xyzh.common.vo.ResourceRecommendVO; +import org.xyzh.common.vo.UserDeptRoleVO; import java.util.List; @@ -21,11 +23,12 @@ public interface ResourceRecommendMapper extends BaseMapper /** * @description 查询资源推荐列表 * @param filter 过滤条件 - * @return List 资源推荐列表 + * @param userDeptRoles 用户部门角色列表(用于权限过滤) + * @return List 资源推荐列表(包含资源信息和权限) * @author yslg * @since 2025-10-15 */ - List selectResourceRecommends(TbResourceRecommend filter); + List selectResourceRecommends(@Param("filter") TbResourceRecommend filter, @Param("userDeptRoles") List userDeptRoles); /** * @description 根据推荐ID查询推荐信息 @@ -55,13 +58,25 @@ public interface ResourceRecommendMapper extends BaseMapper List selectByStatus(@Param("status") Integer status); /** - * @description 根据类型查询推荐列表 - * @param type 类型 - * @return List 推荐列表 + * @description 根据推荐类型查询推荐列表 + * @param recommendType 推荐类型(1-热门资源,2-思政资源) + * @param limit 限制数量 + * @param userDeptRoles 用户部门角色列表(用于权限过滤) + * @return List 推荐列表(包含资源信息和权限) * @author yslg * @since 2025-10-15 */ - List selectByType(@Param("type") Integer type); + List selectByRecommendType(@Param("recommendType") Integer recommendType, @Param("limit") Integer limit, @Param("userDeptRoles") List userDeptRoles); + + /** + * @description 根据资源ID和推荐类型查询 + * @param resourceId 资源ID + * @param recommendType 推荐类型 + * @return List 推荐列表 + * @author yslg + * @since 2025-01-XX + */ + List selectByResourceIdAndType(@Param("resourceId") String resourceId, @Param("recommendType") Integer recommendType); /** * @description 查询热门推荐列表 @@ -130,18 +145,20 @@ public interface ResourceRecommendMapper extends BaseMapper * @description 分页查询资源推荐 * @param filter 过滤条件 * @param pageParam 分页参数 - * @return List 资源推荐列表 + * @param userDeptRoles 用户部门角色列表(用于权限过滤) + * @return List 资源推荐列表(包含资源信息和权限) * @author yslg * @since 2025-10-15 */ - List selectResourceRecommendsPage(@Param("filter") TbResourceRecommend filter, @Param("pageParam") PageParam pageParam); + List selectResourceRecommendsPage(@Param("filter") TbResourceRecommend filter, @Param("pageParam") PageParam pageParam, @Param("userDeptRoles") List userDeptRoles); /** * @description 统计资源推荐总数 * @param filter 过滤条件 + * @param userDeptRoles 用户部门角色列表(用于权限过滤) * @return long 总数 * @author yslg * @since 2025-10-15 */ - long countResourceRecommends(@Param("filter") TbResourceRecommend filter); + long countResourceRecommends(@Param("filter") TbResourceRecommend filter, @Param("userDeptRoles") List userDeptRoles); } diff --git a/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCResourceRecommendServiceImpl.java b/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCResourceRecommendServiceImpl.java index 063d5b3..93865f8 100644 --- a/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCResourceRecommendServiceImpl.java +++ b/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCResourceRecommendServiceImpl.java @@ -11,10 +11,15 @@ 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.TbResourceRecommend; import org.xyzh.common.utils.IDUtils; +import org.xyzh.common.vo.ResourceRecommendVO; +import org.xyzh.common.vo.UserDeptRoleVO; import org.xyzh.news.mapper.ResourceRecommendMapper; import org.xyzh.api.news.recommend.ResourceRecommendService; +import org.xyzh.system.utils.LoginUtil; /** * @description 资源推荐服务实现类 @@ -32,11 +37,13 @@ public class NCResourceRecommendServiceImpl implements ResourceRecommendService private ResourceRecommendMapper resourceRecommendMapper; @Override - public ResultDomain getRecommendList() { - ResultDomain resultDomain = new ResultDomain<>(); + public ResultDomain getRecommendList() { + ResultDomain resultDomain = new ResultDomain<>(); try { - List list = resourceRecommendMapper.selectResourceRecommends(new TbResourceRecommend()); - resultDomain.success("获取推荐列表成功", list); + // 获取当前用户的部门角色(用于权限过滤) + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + List voList = resourceRecommendMapper.selectResourceRecommends(new TbResourceRecommend(), userDeptRoles); + resultDomain.success("获取推荐列表成功", voList); return resultDomain; } catch (Exception e) { logger.error("获取推荐列表异常: {}", e.getMessage(), e); @@ -56,11 +63,21 @@ public class NCResourceRecommendServiceImpl implements ResourceRecommendService return resultDomain; } - // 检查资源是否已被推荐 - List existingList = resourceRecommendMapper.selectByResourceId(recommend.getResourceID()); - if (existingList != null && !existingList.isEmpty()) { - resultDomain.fail("该资源已被推荐"); - return resultDomain; + // 检查资源是否已在该类型下被推荐 + if (recommend.getRecommendType() != null) { + List existingList = resourceRecommendMapper.selectByResourceIdAndType( + recommend.getResourceID(), recommend.getRecommendType()); + if (existingList != null && !existingList.isEmpty()) { + resultDomain.fail("该资源已在此类型下被推荐"); + return resultDomain; + } + } else { + // 如果没有指定类型,检查是否在任何类型下已被推荐 + List existingList = resourceRecommendMapper.selectByResourceId(recommend.getResourceID()); + if (existingList != null && !existingList.isEmpty()) { + resultDomain.fail("该资源已被推荐"); + return resultDomain; + } } // 设置默认值 @@ -178,7 +195,7 @@ public class NCResourceRecommendServiceImpl implements ResourceRecommendService @Override @Transactional(rollbackFor = Exception.class) - public ResultDomain batchAddRecommends(List resourceIDs, String reason) { + public ResultDomain batchAddRecommends(List resourceIDs, Integer recommendType, String reason) { ResultDomain resultDomain = new ResultDomain<>(); try { // 参数验证 @@ -195,15 +212,24 @@ public class NCResourceRecommendServiceImpl implements ResourceRecommendService continue; } - // 检查是否已推荐 - List existingList = resourceRecommendMapper.selectByResourceId(resourceID); - if (existingList != null && !existingList.isEmpty()) { - continue; + // 检查是否已在该类型下推荐 + if (recommendType != null) { + List existingList = resourceRecommendMapper.selectByResourceIdAndType(resourceID, recommendType); + if (existingList != null && !existingList.isEmpty()) { + continue; + } + } else { + // 如果没有指定类型,检查是否在任何类型下已被推荐 + List existingList = resourceRecommendMapper.selectByResourceId(resourceID); + if (existingList != null && !existingList.isEmpty()) { + continue; + } } TbResourceRecommend recommend = new TbResourceRecommend(); recommend.setID(IDUtils.generateID()); recommend.setResourceID(resourceID); + recommend.setRecommendType(recommendType); recommend.setReason(reason); recommend.setOrderNum(0); recommend.setCreateTime(now); @@ -416,4 +442,104 @@ public class NCResourceRecommendServiceImpl implements ResourceRecommendService return resultDomain; } } + + @Override + public ResultDomain getRecommendPage(TbResourceRecommend filter, PageParam pageParam) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + if (filter == null) { + filter = new TbResourceRecommend(); + } + + // 获取当前用户的部门角色(用于权限过滤) + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + + // 计算偏移量 + if (pageParam.getOffset() == 0 && pageParam.getPageNumber() > 0) { + pageParam.setOffset((long) (pageParam.getPageNumber() - 1) * pageParam.getPageSize()); + } + + List voList = resourceRecommendMapper.selectResourceRecommendsPage(filter, pageParam, userDeptRoles); + long total = resourceRecommendMapper.countResourceRecommends(filter, userDeptRoles); + + pageParam.setTotalElements(total); + pageParam.setTotalPages((int) Math.ceil((double) total / pageParam.getPageSize())); + + resultDomain.success("获取推荐资源分页成功", new PageDomain(pageParam, voList)); + return resultDomain; + } catch (Exception e) { + logger.error("获取推荐资源分页异常: {}", e.getMessage(), e); + resultDomain.fail("获取推荐资源分页失败: " + e.getMessage()); + return resultDomain; + } + } + + @Override + public ResultDomain countRecommends(TbResourceRecommend filter) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + if (filter == null) { + filter = new TbResourceRecommend(); + } + + // 获取当前用户的部门角色(用于权限过滤) + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + + long count = resourceRecommendMapper.countResourceRecommends(filter, userDeptRoles); + resultDomain.success("统计成功", count); + return resultDomain; + } catch (Exception e) { + logger.error("统计推荐资源异常: {}", e.getMessage(), e); + resultDomain.fail("统计失败: " + e.getMessage()); + return resultDomain; + } + } + + @Override + public ResultDomain getRecommendsByType(Integer recommendType, Integer limit) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + if (recommendType == null) { + resultDomain.fail("推荐类型不能为空"); + return resultDomain; + } + + // 获取当前用户的部门角色(用于权限过滤) + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + + List voList = resourceRecommendMapper.selectByRecommendType(recommendType, limit, userDeptRoles); + + resultDomain.success("获取推荐资源列表成功", voList); + return resultDomain; + } catch (Exception e) { + logger.error("根据类型获取推荐资源异常: {}", e.getMessage(), e); + resultDomain.fail("获取推荐资源列表失败: " + e.getMessage()); + return resultDomain; + } + } + + @Override + public ResultDomain isResourceRecommendedByType(String resourceID, Integer recommendType) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + if (!StringUtils.hasText(resourceID)) { + resultDomain.fail("资源ID不能为空"); + return resultDomain; + } + if (recommendType == null) { + resultDomain.fail("推荐类型不能为空"); + return resultDomain; + } + + List recommendList = resourceRecommendMapper.selectByResourceIdAndType(resourceID, recommendType); + boolean isRecommended = recommendList != null && !recommendList.isEmpty(); + + resultDomain.success("检查成功", isRecommended); + return resultDomain; + } catch (Exception e) { + logger.error("检查资源是否推荐异常: {}", e.getMessage(), e); + resultDomain.fail("检查失败: " + e.getMessage()); + return resultDomain; + } + } } diff --git a/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCResourceServiceImpl.java b/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCResourceServiceImpl.java index 9784e46..f5b8fb9 100644 --- a/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCResourceServiceImpl.java +++ b/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCResourceServiceImpl.java @@ -21,7 +21,6 @@ import org.xyzh.news.mapper.ResourceMapper; import org.xyzh.news.mapper.ResourceTagMapper; import org.xyzh.system.utils.LoginUtil; import org.xyzh.api.news.resource.ResourceService; -import org.xyzh.api.usercenter.collection.UserCollectionService; import org.xyzh.api.system.permission.ResourcePermissionService; import org.xyzh.common.vo.UserDeptRoleVO; import org.xyzh.common.core.enums.ResourceType; @@ -49,9 +48,6 @@ public class NCResourceServiceImpl implements ResourceService { @Autowired private ResourceTagMapper resourceTagMapper; - @Autowired - private UserCollectionService userCollectionService; - @Autowired private ResourcePermissionService resourcePermissionService; @@ -98,6 +94,29 @@ public class NCResourceServiceImpl implements ResourceService { } } + @Override + public ResultDomain getResourcePageOrderByViewCount(TbResource filter, PageParam pageParam) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + if (filter == null) { + filter = new TbResource(); + } + // 获取当前用户的部门角色 + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + List list = resourceMapper.selectResourcesPageOrderByViewCount(filter, pageParam, userDeptRoles); + long total = resourceMapper.countResources(filter, userDeptRoles); + pageParam.setTotalElements(total); + pageParam.setTotalPages((int) Math.ceil((double) total / pageParam.getPageSize())); + resultDomain.success("获取资源分页(按浏览次数排序)成功", new PageDomain(pageParam, list)); + return resultDomain; + } + catch (Exception e) { + logger.error("获取资源分页(按浏览次数排序)异常: {}", e.getMessage(), e); + resultDomain.fail("获取资源分页(按浏览次数排序)失败: " + e.getMessage()); + return resultDomain; + } + } + @Override public ResultDomain getResourceById(String resourceID) { ResultDomain resultDomain = new ResultDomain<>(); @@ -560,54 +579,20 @@ public class NCResourceServiceImpl implements ResourceService { return resultDomain; } collection.setUserID(user.getID()); - try { - // 参数验证 - if (!StringUtils.hasText(resourceID)) { - resultDomain.fail("资源ID不能为空"); - return resultDomain; - } - if (collectionValue != 1 && collectionValue != -1) { - resultDomain.fail("收藏值错误"); - return resultDomain; - } - ResultDomain isCollected = userCollectionService.isCollected(user.getID(), collection.getCollectionType(), resourceID); - TbResource resource = new TbResource(); - resource.setResourceID(resourceID); - if (isCollected.isSuccess() && isCollected.getData() && collectionValue == 1) { - resultDomain.success("已收藏", resource); - return resultDomain; - } - else if (isCollected.isSuccess() && isCollected.getData() && collectionValue == -1) { - ResultDomain removeCollection = userCollectionService.removeCollection(collection); - if (removeCollection.isSuccess()) { - resourceMapper.updateResourceCollectCount(resourceID, -1); - resultDomain.success("已取消收藏", resource); - return resultDomain; - } else { - resultDomain.fail("取消收藏失败"); - return resultDomain; - } - }else if (isCollected.isSuccess() && !isCollected.getData() && collectionValue == 1) { - collection.setID(IDUtils.generateID()); - collection.setCreateTime(new Date()); - ResultDomain addCollection = userCollectionService.addCollection(collection); - if (addCollection.isSuccess() && addCollection.getData() != null) { - resourceMapper.updateResourceCollectCount(resourceID, 1); - resultDomain.success("已收藏", resource); - return resultDomain; - } else { - resultDomain.fail("收藏失败"); - return resultDomain; - } - }else { - resultDomain.success("未收藏", resource); - return resultDomain; - } - } catch (Exception e) { - logger.error("增加资源收藏次数异常: {}", e.getMessage(), e); - resultDomain.fail("增加收藏次数失败: " + e.getMessage()); + // 参数验证 + if (!StringUtils.hasText(resourceID)) { + resultDomain.fail("资源ID不能为空"); return resultDomain; } + if (collectionValue != 1 && collectionValue != -1) { + resultDomain.fail("收藏值错误"); + return resultDomain; + } + TbResource resource = new TbResource(); + resource.setResourceID(resourceID); + resourceMapper.updateResourceCollectCount(resourceID, collectionValue); + resultDomain.success("更新资源收藏次数成功", resource); + return resultDomain; } @Override 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 2ffeb55..c185f65 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 @@ -215,12 +215,12 @@ public class NCTagServiceImpl implements TagService { } @Override - public ResultDomain getAllTags() { + public ResultDomain getTagList(TbTag filter) { ResultDomain resultDomain = new ResultDomain<>(); try { // 获取当前用户的部门角色 List userDeptRoles = LoginUtil.getCurrentDeptRole(); - List tags = tagMapper.selectTags(new TbTag(), userDeptRoles); + List tags = tagMapper.selectTags(filter, userDeptRoles); resultDomain.success("查询成功", tags); return resultDomain; } catch (Exception e) { diff --git a/schoolNewsServ/news/src/main/resources/mapper/ResourceMapper.xml b/schoolNewsServ/news/src/main/resources/mapper/ResourceMapper.xml index 375978b..805cb5e 100644 --- a/schoolNewsServ/news/src/main/resources/mapper/ResourceMapper.xml +++ b/schoolNewsServ/news/src/main/resources/mapper/ResourceMapper.xml @@ -336,6 +336,33 @@ LIMIT #{pageParam.pageSize} OFFSET #{pageParam.offset} + + - SELECT - - FROM tb_resource_recommend + + + INNER JOIN tb_resource_permission rp ON r.resource_id = rp.resource_id + AND rp.resource_type = 1 + AND rp.deleted = 0 + AND rp.can_read = 1 + AND ( + -- 全局权限:所有用户可访问 + (rp.dept_id IS NULL AND rp.role_id IS NULL) + + OR EXISTS ( + SELECT 1 + FROM ( + + SELECT #{udr.deptID} AS dept_id, #{udr.deptPath} AS dept_path, #{udr.roleID} AS role_id + + ) user_roles + LEFT JOIN tb_sys_dept perm_dept ON perm_dept.dept_id = rp.dept_id AND perm_dept.deleted = 0 + WHERE + (rp.role_id IS NULL AND rp.dept_id IS NOT NULL + AND user_roles.dept_path LIKE CONCAT(perm_dept.dept_path, '%')) + OR (rp.dept_id IS NULL AND rp.role_id = user_roles.role_id) + OR (rp.dept_id = user_roles.dept_id AND rp.role_id = user_roles.role_id) + ) + + ) + + + + @@ -70,12 +158,43 @@ ORDER BY order_num ASC, create_time DESC - - + SELECT DISTINCT + rr.id, rr.resource_id, rr.recommend_type, rr.order_num, rr.reason, + rr.creator, rr.updater, rr.create_time, rr.update_time, rr.delete_time, rr.deleted, + r.title, r.summary, r.cover_image, r.tag_id, r.author, r.source, r.source_url, + r.view_count, r.like_count, r.collect_count, r.status, r.is_recommend, r.is_banner, + r.publish_time, r.creator AS resource_creator, r.updater AS resource_updater, + r.create_time AS resource_create_time, r.update_time AS resource_update_time, + MAX(rp.can_read) AS can_read, MAX(rp.can_write) AS can_write + FROM tb_resource_recommend rr + INNER JOIN tb_resource r ON rr.resource_id = r.resource_id AND r.deleted = 0 + + WHERE rr.deleted = 0 + + AND rr.recommend_type = #{recommendType} + + GROUP BY rr.id, rr.resource_id, rr.recommend_type, rr.order_num, rr.reason, + rr.creator, rr.updater, rr.create_time, rr.update_time, rr.delete_time, rr.deleted, + r.title, r.summary, r.cover_image, r.tag_id, r.author, r.source, r.source_url, + r.view_count, r.like_count, r.collect_count, r.status, r.is_recommend, r.is_banner, + r.publish_time, r.creator, r.updater, r.create_time, r.update_time + ORDER BY rr.order_num ASC, rr.create_time DESC + + LIMIT #{limit} + + + + + @@ -106,10 +225,10 @@ INSERT INTO tb_resource_recommend ( - id, resource_id, order_num, reason, creator, updater, create_time, + id, resource_id, recommend_type, order_num, reason, creator, updater, create_time, update_time, delete_time, deleted ) VALUES ( - #{id}, #{resourceID}, #{orderNum}, #{reason}, #{creator}, #{updater}, #{createTime}, + #{id}, #{resourceID}, #{recommendType}, #{orderNum}, #{reason}, #{creator}, #{updater}, #{createTime}, #{updateTime}, #{deleteTime}, #{deleted} ) @@ -121,6 +240,9 @@ resource_id = #{resourceID}, + + recommend_type = #{recommendType}, + order_num = #{orderNum}, @@ -152,12 +274,12 @@ INSERT INTO tb_resource_recommend ( - id, resource_id, order_num, reason, creator, updater, create_time, + id, resource_id, recommend_type, order_num, reason, creator, updater, create_time, update_time, delete_time, deleted ) VALUES ( - #{item.id}, #{item.resourceID}, #{item.orderNum}, #{item.reason}, #{item.creator}, + #{item.id}, #{item.resourceID}, #{item.recommendType}, #{item.orderNum}, #{item.reason}, #{item.creator}, #{item.updater}, #{item.createTime}, #{item.updateTime}, #{item.deleteTime}, #{item.deleted} ) @@ -172,21 +294,37 @@ - - + SELECT DISTINCT + rr.id, rr.resource_id, rr.recommend_type, rr.order_num, rr.reason, + rr.creator, rr.updater, rr.create_time, rr.update_time, rr.delete_time, rr.deleted, + r.title, r.summary, r.cover_image, r.tag_id, r.author, r.source, r.source_url, + r.view_count, r.like_count, r.collect_count, r.status, r.is_recommend, r.is_banner, + r.publish_time, r.creator AS resource_creator, r.updater AS resource_updater, + r.create_time AS resource_create_time, r.update_time AS resource_update_time, + MAX(rp.can_read) AS can_read, MAX(rp.can_write) AS can_write + FROM tb_resource_recommend rr + INNER JOIN tb_resource r ON rr.resource_id = r.resource_id AND r.deleted = 0 + - ORDER BY order_num ASC, create_time DESC + GROUP BY rr.id, rr.resource_id, rr.recommend_type, rr.order_num, rr.reason, + rr.creator, rr.updater, rr.create_time, rr.update_time, rr.delete_time, rr.deleted, + r.title, r.summary, r.cover_image, r.tag_id, r.author, r.source, r.source_url, + r.view_count, r.like_count, r.collect_count, r.status, r.is_recommend, r.is_banner, + r.publish_time, r.creator, r.updater, r.create_time, r.update_time + ORDER BY rr.order_num ASC, rr.create_time DESC LIMIT #{pageParam.pageSize} OFFSET #{pageParam.offset} - + + diff --git a/schoolNewsServ/study/src/main/java/org/xyzh/study/mapper/LearningTaskTagMapper.java b/schoolNewsServ/study/src/main/java/org/xyzh/study/mapper/LearningTaskTagMapper.java new file mode 100644 index 0000000..f1bc892 --- /dev/null +++ b/schoolNewsServ/study/src/main/java/org/xyzh/study/mapper/LearningTaskTagMapper.java @@ -0,0 +1,46 @@ +package org.xyzh.study.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.xyzh.common.dto.study.TbLearningTaskTag; +import org.xyzh.common.vo.TagVO; + +import java.util.List; + +/** + * @description 学习任务标签关联Mapper + * @filename LearningTaskTagMapper.java + * @author yslg + * @copyright xyzh + * @since 2025-10-31 + */ +@Mapper +public interface LearningTaskTagMapper extends BaseMapper { + + /** + * 根据任务ID查询标签VO列表(包含标签详细信息) + */ + List selectByTaskId(@Param("taskId") String taskId); + + /** + * 根据标签ID查询任务关联列表 + */ + List selectByTagId(@Param("tagId") String tagId); + + /** + * 批量插入任务标签关联 + */ + int batchInsert(@Param("list") List list); + + /** + * 根据任务ID删除所有标签关联 + */ + int deleteByTaskId(@Param("taskId") String taskId); + + /** + * 删除指定任务的指定标签 + */ + int deleteByTaskIdAndTagId(@Param("taskId") String taskId, @Param("tagId") String tagId); +} + 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 b4d0516..56e27d1 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 @@ -29,7 +29,10 @@ 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.study.mapper.LearningTaskTagMapper; import org.xyzh.api.study.task.LearningTaskService; +import org.xyzh.common.dto.study.TbLearningTaskTag; +import org.xyzh.common.vo.TagVO; import org.xyzh.common.core.enums.TaskItemType; import org.xyzh.api.system.permission.ResourcePermissionService; import org.xyzh.common.vo.UserDeptRoleVO; @@ -59,6 +62,9 @@ public class SCLearningTaskServiceImpl implements LearningTaskService { @Autowired private TaskItemMapper taskItemMapper; + @Autowired + private LearningTaskTagMapper learningTaskTagMapper; + @Autowired private ResourcePermissionService resourcePermissionService; @@ -237,6 +243,22 @@ public class SCLearningTaskServiceImpl implements LearningTaskService { int learnCount = courseMapper.incrementLearnCount(item.getItemID(), taskUsers.size()); } + // 绑定标签 + List taskTags = taskVO.getTaskTags(); + if (taskTags != null && !taskTags.isEmpty()) { + List taskTagList = new ArrayList<>(); + for (TagVO tag : taskTags) { + TbLearningTaskTag taskTag = new TbLearningTaskTag(); + taskTag.setID(IDUtils.generateID()); + taskTag.setTaskID(taskID); + taskTag.setTagID(tag.getTagID()); + taskTag.setCreator(currentUser.getID()); + taskTag.setCreateTime(now); + taskTagList.add(taskTag); + } + learningTaskTagMapper.batchInsert(taskTagList); + } + // 创建任务资源权限 try { List userDeptRoles = LoginUtil.getCurrentDeptRole(); @@ -423,6 +445,57 @@ public class SCLearningTaskServiceImpl implements LearningTaskService { int learnCount = courseMapper.incrementLearnCount(taskID, usersToInsert.size()); } + // 4. 处理标签关联 + List newTags = taskVO.getTaskTags(); + if (newTags == null) { + newTags = new ArrayList<>(); + } + + // 获取现有的标签关联 + List existingTags = learningTaskTagMapper.selectByTaskId(taskID); + Map existingTagMap = existingTags.stream() + .collect(Collectors.toMap(TagVO::getTagID, tag -> tag)); + + Set newTagIDs = new HashSet<>(); + List tagsToInsert = new ArrayList<>(); + + // 处理新的标签关联 + for (TagVO tag : newTags) { + String tagID = tag.getTagID(); + if (tagID == null || tagID.isEmpty()) { + continue; + } + + newTagIDs.add(tagID); + + // 如果不存在,则新增 + if (!existingTagMap.containsKey(tagID)) { + TbLearningTaskTag taskTag = new TbLearningTaskTag(); + taskTag.setID(IDUtils.generateID()); + taskTag.setTaskID(taskID); + taskTag.setTagID(tagID); + taskTag.setCreator(user.getID()); + taskTag.setCreateTime(now); + tagsToInsert.add(taskTag); + } + } + + // 找出要删除的标签关联 + List tagIDsToDelete = existingTagMap.values().stream() + .filter(taskTag -> !newTagIDs.contains(taskTag.getTagID())) + .map(TagVO::getTagID) + .collect(Collectors.toList()); + + // 删除不再需要的标签 + for (String tagID : tagIDsToDelete) { + learningTaskTagMapper.deleteByTaskIdAndTagId(taskID, tagID); + } + + // 插入新标签 + if (!tagsToInsert.isEmpty()) { + learningTaskTagMapper.batchInsert(tagsToInsert); + } + resultDomain.success("更新任务成功", taskVO); return resultDomain; } @@ -494,11 +567,13 @@ public class SCLearningTaskServiceImpl implements LearningTaskService { taskVO.setTaskCourses(taskCourses); taskVO.setTaskResources(taskResources); taskVO.setTaskUsers(taskUsers); + + // 获取任务标签 + List taskTags = learningTaskTagMapper.selectByTaskId(taskID); + taskVO.setTaskTags(taskTags); taskVO.setTotalTaskNum(allTaskItems.size()); - - resultDomain.success("获取任务详情成功", taskVO); return resultDomain; } @@ -556,11 +631,13 @@ public class SCLearningTaskServiceImpl implements LearningTaskService { taskVO.setTaskCourses(taskCourses); taskVO.setTaskResources(taskResources); taskVO.setTaskUsers(taskUsers); + + // 获取任务标签 + List taskTags = learningTaskTagMapper.selectByTaskId(taskID); + taskVO.setTaskTags(taskTags); taskVO.setTotalTaskNum(allTaskItems.size()); - - resultDomain.success("获取任务详情成功", taskVO); return resultDomain; } diff --git a/schoolNewsServ/study/src/main/resources/mapper/LearningTaskTagMapper.xml b/schoolNewsServ/study/src/main/resources/mapper/LearningTaskTagMapper.xml new file mode 100644 index 0000000..73618a2 --- /dev/null +++ b/schoolNewsServ/study/src/main/resources/mapper/LearningTaskTagMapper.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + id, task_id, tag_id, creator, create_time + + + + + + + + + + + INSERT INTO tb_learning_task_tag ( + id, task_id, tag_id, creator, create_time + ) VALUES + + ( + #{item.id}, #{item.taskID}, #{item.tagID}, + #{item.creator}, #{item.createTime} + ) + + + + + + DELETE FROM tb_learning_task_tag + WHERE task_id = #{taskId} + + + + + DELETE FROM tb_learning_task_tag + WHERE task_id = #{taskId} AND tag_id = #{tagId} + + + diff --git a/schoolNewsServ/usercenter/pom.xml b/schoolNewsServ/usercenter/pom.xml index c0645ab..db9061c 100644 --- a/schoolNewsServ/usercenter/pom.xml +++ b/schoolNewsServ/usercenter/pom.xml @@ -32,6 +32,16 @@ api-system ${school-news.version} + + org.xyzh + api-news + ${school-news.version} + + + org.xyzh + api-study + ${school-news.version} + org.xyzh diff --git a/schoolNewsServ/usercenter/src/main/java/org/xyzh/usercenter/controller/UserCollectionController.java b/schoolNewsServ/usercenter/src/main/java/org/xyzh/usercenter/controller/UserCollectionController.java index 0dea047..cce48d3 100644 --- a/schoolNewsServ/usercenter/src/main/java/org/xyzh/usercenter/controller/UserCollectionController.java +++ b/schoolNewsServ/usercenter/src/main/java/org/xyzh/usercenter/controller/UserCollectionController.java @@ -8,7 +8,7 @@ import org.xyzh.api.usercenter.collection.UserCollectionService; import org.xyzh.common.core.domain.ResultDomain; import org.xyzh.common.dto.user.TbSysUser; import org.xyzh.common.dto.usercenter.TbUserCollection; -import org.xyzh.common.dto.user.TbSysUser; +import org.xyzh.common.vo.UserCollectionVO; import org.xyzh.system.utils.LoginUtil; /** * @description 用户收藏控制器 @@ -26,14 +26,13 @@ public class UserCollectionController { private UserCollectionService userCollectionService; /** - * 获取用户收藏列表 + * 获取用户收藏列表(扁平化VO,包含资源/课程详情) */ @GetMapping("/user/{userID}") - public ResultDomain getUserCollections( + public ResultDomain getUserCollections( @PathVariable("userID") String userID, - @RequestParam(required = false) Integer resourceType, - @RequestParam(required = false) String resourceID) { - return userCollectionService.getUserCollections(userID, resourceType); + @RequestParam(required = false, name = "collectionType") Integer collectionType) { + return userCollectionService.getUserCollections(userID, collectionType); } /** @@ -57,8 +56,8 @@ public class UserCollectionController { */ @GetMapping("/check") public ResultDomain isCollected( - @RequestParam(value = "collectionID", required = false) String collectionID, - @RequestParam(value = "collectionType", required = false) Integer collectionType) { + @RequestParam(name = "collectionID", required = false) String collectionID, + @RequestParam(name = "collectionType", required = false) Integer collectionType) { ResultDomain resultDomain = new ResultDomain<>(); TbSysUser user = LoginUtil.getCurrentUser(); if (user == null) { @@ -74,8 +73,8 @@ public class UserCollectionController { @GetMapping("/count/{userID}") public ResultDomain getCollectionCount( @PathVariable("userID") String userID, - @RequestParam(required = false) Integer resourceType, - @RequestParam(required = false) String resourceID) { - return userCollectionService.getCollectionCount(userID, resourceType, resourceID); + @RequestParam(name = "collectionType", required = false) Integer collectionType, + @RequestParam(name = "collectionID", required = false) String collectionID) { + return userCollectionService.getCollectionCount(userID, collectionType, collectionID); } } diff --git a/schoolNewsServ/usercenter/src/main/java/org/xyzh/usercenter/mapper/UserCollectionMapper.java b/schoolNewsServ/usercenter/src/main/java/org/xyzh/usercenter/mapper/UserCollectionMapper.java index 9a0ba09..519557a 100644 --- a/schoolNewsServ/usercenter/src/main/java/org/xyzh/usercenter/mapper/UserCollectionMapper.java +++ b/schoolNewsServ/usercenter/src/main/java/org/xyzh/usercenter/mapper/UserCollectionMapper.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.usercenter.TbUserCollection; +import org.xyzh.common.vo.UserCollectionVO; import java.util.List; @@ -26,6 +27,16 @@ public interface UserCollectionMapper extends BaseMapper { * @since 2025-10-15 */ List selectUserCollections(TbUserCollection filter); + + /** + * @description 查询用户收藏列表(包含资源/课程详情) + * @param userID 用户ID + * @param collectionType 收藏类型(可选) + * @return List 用户收藏VO列表 + * @author yslg + * @since 2025-10-31 + */ + List selectUserCollectionsVO(@Param("userID") String userID, @Param("collectionType") Integer collectionType); /** * @description 根据用户ID查询收藏记录 diff --git a/schoolNewsServ/usercenter/src/main/java/org/xyzh/usercenter/service/impl/UCUserCollectionServiceImpl.java b/schoolNewsServ/usercenter/src/main/java/org/xyzh/usercenter/service/impl/UCUserCollectionServiceImpl.java index 7d47400..f97818c 100644 --- a/schoolNewsServ/usercenter/src/main/java/org/xyzh/usercenter/service/impl/UCUserCollectionServiceImpl.java +++ b/schoolNewsServ/usercenter/src/main/java/org/xyzh/usercenter/service/impl/UCUserCollectionServiceImpl.java @@ -1,5 +1,6 @@ package org.xyzh.usercenter.service.impl; +import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -7,9 +8,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.xyzh.api.news.resource.ResourceService; import org.xyzh.common.core.domain.ResultDomain; +import org.xyzh.common.dto.resource.TbResource; import org.xyzh.common.dto.user.TbSysUser; import org.xyzh.common.dto.usercenter.TbUserCollection; +import org.xyzh.common.vo.UserCollectionVO; import org.xyzh.system.utils.LoginUtil; import org.xyzh.usercenter.mapper.UserCollectionMapper; import org.xyzh.usercenter.service.UCUserCollectionService; @@ -29,26 +34,31 @@ public class UCUserCollectionServiceImpl implements UCUserCollectionService { @Autowired private UserCollectionMapper userCollectionMapper; + @Autowired + private ResourceService resourceService; + @Override + @Transactional(rollbackFor = Exception.class) public ResultDomain addCollection(TbUserCollection userCollection) { + ResultDomain resultDomain = new ResultDomain<>(); TbSysUser user = LoginUtil.getCurrentUser(); if (user == null) { - ResultDomain resultDomain = new ResultDomain<>(); - resultDomain.fail("请先登录"); - return resultDomain; + throw new RuntimeException("请先登录"); } userCollection.setUserID(user.getID()); userCollection.setCreateTime(new Date()); int result = userCollectionMapper.insertUserCollection(userCollection); if (result > 0) { - ResultDomain resultDomain = new ResultDomain<>(); + userCollection.setCollectionValue(1); + ResultDomain resultDomainResource = resourceService.resourceCollect(userCollection); + if (!resultDomainResource.isSuccess()) { + throw new RuntimeException(resultDomainResource.getMessage()); + } resultDomain.success("添加收藏成功", userCollection); return resultDomain; - } else { - ResultDomain resultDomain = new ResultDomain<>(); - resultDomain.fail("添加收藏失败"); - return resultDomain; } + resultDomain.fail("添加收藏失败"); + return resultDomain; } @Override @@ -58,20 +68,24 @@ public class UCUserCollectionServiceImpl implements UCUserCollectionService { } @Override - public ResultDomain getUserCollections(String userID, Integer collectionType) { - TbUserCollection filter = new TbUserCollection(); - filter.setUserID(userID); - filter.setCollectionType(collectionType); - List list = userCollectionMapper.selectUserCollections(filter); - if (list != null && list.size() > 0) { - ResultDomain resultDomain = new ResultDomain<>(); - resultDomain.success("获取收藏列表成功", list); - return resultDomain; - } else { - ResultDomain resultDomain = new ResultDomain<>(); + public ResultDomain getUserCollections(String userID, Integer collectionType) { + ResultDomain resultDomain = new ResultDomain<>(); + + try { + // 直接通过SQL JOIN一次性查询所有数据,避免N+1查询问题 + List voList = userCollectionMapper.selectUserCollectionsVO(userID, collectionType); + + if (voList == null || voList.isEmpty()) { + resultDomain.success("获取收藏列表成功", new ArrayList<>()); + } else { + resultDomain.success("获取收藏列表成功", voList); + } + } catch (Exception e) { + logger.error("获取收藏列表失败: userID={}, collectionType={}", userID, collectionType, e); resultDomain.fail("获取收藏列表失败"); - return resultDomain; } + + return resultDomain; } @Override @@ -93,9 +107,22 @@ public class UCUserCollectionServiceImpl implements UCUserCollectionService { } @Override + @Transactional(rollbackFor = Exception.class) public ResultDomain removeCollection(TbUserCollection userCollection) { + TbSysUser user = LoginUtil.getCurrentUser(); + if (user == null) { + throw new RuntimeException("请先登录"); + } + userCollection.setUserID(user.getID()); + userCollection.setCollectionValue(-1); int result = userCollectionMapper.deleteUserCollection(userCollection); + if (result > 0) { + userCollection.setCollectionValue(-1); + ResultDomain resultDomainResource = resourceService.resourceCollect(userCollection); + if (!resultDomainResource.isSuccess()) { + throw new RuntimeException(resultDomainResource.getMessage()); + } ResultDomain resultDomain = new ResultDomain<>(); resultDomain.success("删除收藏成功", true); return resultDomain; diff --git a/schoolNewsServ/usercenter/src/main/resources/mapper/UserCollectionMapper.xml b/schoolNewsServ/usercenter/src/main/resources/mapper/UserCollectionMapper.xml index 9d11d54..0b86149 100644 --- a/schoolNewsServ/usercenter/src/main/resources/mapper/UserCollectionMapper.xml +++ b/schoolNewsServ/usercenter/src/main/resources/mapper/UserCollectionMapper.xml @@ -5,11 +5,47 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -291,5 +327,50 @@ FROM tb_user_collection + + + \ No newline at end of file diff --git a/schoolNewsWeb/src/apis/homepage/recommend.ts b/schoolNewsWeb/src/apis/homepage/recommend.ts index acf80e2..1710712 100644 --- a/schoolNewsWeb/src/apis/homepage/recommend.ts +++ b/schoolNewsWeb/src/apis/homepage/recommend.ts @@ -5,7 +5,7 @@ */ import { api } from '@/apis/index'; -import type { Resource, ResultDomain } from '@/types'; +import type { Resource, ResourceRecommendVO, ResultDomain } from '@/types'; /** * 推荐API服务 @@ -38,5 +38,27 @@ export const recommendApi = { async getHotNews(limit?: number): Promise> { const response = await api.get('/homepage/recommend/hot-news', { limit }); return response.data; + }, + + /** + * 获取热门资源列表(推荐类型:1) + * @param limit 限制数量 + * @returns Promise> + */ + async getHotResources(limit?: number): Promise> { + + const response = await api.get('/homepage/recommend/hot', { limit }); + return response.data; + }, + + /** + * 获取思政资源列表(推荐类型:2) + * @param limit 限制数量 + * @returns Promise> + */ + async getIdeologicalResources(limit?: number): Promise> { + + const response = await api.get('/homepage/recommend/ideological', { limit }); + return response.data; } }; diff --git a/schoolNewsWeb/src/apis/resource/index.ts b/schoolNewsWeb/src/apis/resource/index.ts index d079968..6ea465b 100644 --- a/schoolNewsWeb/src/apis/resource/index.ts +++ b/schoolNewsWeb/src/apis/resource/index.ts @@ -6,4 +6,5 @@ export * from './resourceTag'; export * from './resource'; -export { bannerApi} from './banner'; \ No newline at end of file +export * from './resourceRecommend'; +export { bannerApi } from './banner'; \ No newline at end of file diff --git a/schoolNewsWeb/src/apis/resource/resource.ts b/schoolNewsWeb/src/apis/resource/resource.ts index ed71693..173cb39 100644 --- a/schoolNewsWeb/src/apis/resource/resource.ts +++ b/schoolNewsWeb/src/apis/resource/resource.ts @@ -136,16 +136,6 @@ export const resourceApi = { return response.data; }, - /** - * 收藏次数增减 - * @param resourceID 资源ID - * @returns Promise> - */ - async resourceCollect(collect: UserCollection): Promise> { - const response = await api.post(`/news/resources/resource/collect`, collect); - return response.data; - }, - // ==================== 资源推荐和轮播操作 ==================== /** diff --git a/schoolNewsWeb/src/apis/resource/resourceRecommend.ts b/schoolNewsWeb/src/apis/resource/resourceRecommend.ts new file mode 100644 index 0000000..f24c5b6 --- /dev/null +++ b/schoolNewsWeb/src/apis/resource/resourceRecommend.ts @@ -0,0 +1,155 @@ +/** + * @description 资源推荐管理API接口 + * @filename resourceRecommend.ts + * @author yslg + * @copyright xyzh + * @since 2025-10-31 + */ + +import { api } from '@/apis'; +import type { ResultDomain, ResourceRecommendVO, PageParam } from '@/types'; + +/** + * 推荐类型枚举 + */ +export enum RecommendType { + /** 热门资源推荐 */ + HOT = 1, + /** 思政资源推荐 */ + IDEOLOGICAL = 2 +} + +/** + * 资源推荐API服务 + */ +export const resourceRecommendApi = { + /** + * 获取推荐列表 + * @returns Promise> + */ + async getRecommendList(): Promise> { + const response = await api.get('/news/recommends/list'); + return response.data; + }, + + /** + * 根据推荐类型获取推荐资源列表 + * @param recommendType 推荐类型(1-热门资源,2-思政资源) + * @param limit 限制数量 + * @returns Promise> + */ + async getRecommendsByType(recommendType: number, limit?: number): Promise> { + const params = limit ? { limit } : null; + const response = await api.get(`/news/recommends/type/${recommendType}`, params); + return response.data; + }, + + /** + * 分页查询推荐资源列表 + * @param filter 筛选条件 + * @param pageParam 分页参数 + * @returns Promise> + */ + async getRecommendPage(pageParam: PageParam, filter?: any): Promise> { + const response = await api.post('/news/recommends/page', { + pageParam, + filter, + }); + return response.data; + }, + + /** + * 根据ID获取推荐详情 + * @param recommendID 推荐ID + * @returns Promise> + */ + async getRecommendById(recommendID: string): Promise> { + const response = await api.get(`/news/recommends/recommend/${recommendID}`); + return response.data; + }, + + /** + * 创建推荐 + * @param recommend 推荐信息 + * @returns Promise> + */ + async createRecommend(recommend: any): Promise> { + const response = await api.post('/news/recommends/recommend', recommend); + return response.data; + }, + + /** + * 批量添加推荐资源 + * @param resourceIDs 资源ID列表 + * @param recommendType 推荐类型 + * @param reason 推荐理由(可选) + * @returns Promise> + */ + async batchAddRecommends(resourceIDs: string[], recommendType: number, reason?: string): Promise> { + const response = await api.post('/news/recommends/recommend/batch', { + resourceIDs, + recommendType, + reason + }); + return response.data; + }, + + /** + * 更新推荐 + * @param recommend 推荐信息 + * @returns Promise> + */ + async updateRecommend(recommend: any): Promise> { + const response = await api.put('/news/recommends/recommend', recommend); + return response.data; + }, + + /** + * 删除推荐 + * @param recommendID 推荐ID + * @returns Promise> + */ + async deleteRecommend(recommendID: string): Promise> { + const response = await api.delete(`/news/recommends/recommend/${recommendID}`); + return response.data; + }, + + /** + * 更新推荐排序 + * @param recommendID 推荐ID + * @param orderNum 排序号 + * @returns Promise> + */ + async updateRecommendOrder(recommendID: string, orderNum: number): Promise> { + const response = await api.put(`/news/recommends/recommend/${recommendID}/order`, null, { + params: { orderNum } + }); + return response.data; + }, + + /** + * 检查资源是否已推荐(按类型) + * @param resourceID 资源ID + * @param recommendType 推荐类型 + * @returns Promise> + */ + async isResourceRecommendedByType(resourceID: string, recommendType: number): Promise> { + const response = await api.get(`/news/recommends/check/${resourceID}`, { + recommendType + }); + return response.data; + }, + + /** + * 统计推荐资源总数 + * @param filter 筛选条件 + * @returns Promise> + */ + async countRecommends(filter?: any): Promise> { + const response = await api.post('/news/recommends/count', filter); + return response.data; + } +}; + +export default resourceRecommendApi; + diff --git a/schoolNewsWeb/src/apis/resource/resourceTag.ts b/schoolNewsWeb/src/apis/resource/resourceTag.ts index 828dbd1..e94c57d 100644 --- a/schoolNewsWeb/src/apis/resource/resourceTag.ts +++ b/schoolNewsWeb/src/apis/resource/resourceTag.ts @@ -19,8 +19,8 @@ export const resourceTagApi = { * 获取标签列表(获取所有标签) * @returns Promise> */ - async getTagList(): Promise> { - const response = await api.get('/news/tags/list'); + async getTagList(filter: Tag): Promise> { + const response = await api.get('/news/tags/list', filter); return response.data; }, diff --git a/schoolNewsWeb/src/apis/usercenter/collection.ts b/schoolNewsWeb/src/apis/usercenter/collection.ts index acf100e..fa9530d 100644 --- a/schoolNewsWeb/src/apis/usercenter/collection.ts +++ b/schoolNewsWeb/src/apis/usercenter/collection.ts @@ -5,7 +5,7 @@ */ import { api } from '@/apis/index'; -import type { UserCollection, ResultDomain } from '@/types'; +import type { UserCollection, UserCollectionVO, ResultDomain } from '@/types'; /** * 用户收藏API服务 @@ -13,14 +13,13 @@ import type { UserCollection, ResultDomain } from '@/types'; export const userCollectionApi = { baseUrl: '/usercenter/collections', /** - * 获取用户收藏列表 + * 获取用户收藏列表(扁平化VO,包含资源/课程详情) * @param userID 用户ID * @param collectionType 收藏类型 - * @returns Promise> + * @returns Promise> */ - async getUserCollections(userID: string, collectionType?: number): Promise> { - const response = await api.get(`${this.baseUrl}/list`, { - userID, + async getUserCollections(userID: string, collectionType?: number): Promise> { + const response = await api.get(`${this.baseUrl}/user/${userID}`, { collectionType }); return response.data; @@ -43,12 +42,8 @@ export const userCollectionApi = { * @param collectionID 收藏对象ID * @returns Promise> */ - async removeCollection(userID: string, collectionType: number, collectionID: string): Promise> { - const response = await api.delete(`${this.baseUrl}/collect`, { - userID, - collectionType, - collectionID - }); + async removeCollection(collection: UserCollection): Promise> { + const response = await api.delete(`${this.baseUrl}/collect`, collection); return response.data; }, diff --git a/schoolNewsWeb/src/assets/imgs/hot.svg b/schoolNewsWeb/src/assets/imgs/hot.svg new file mode 100644 index 0000000..6462499 --- /dev/null +++ b/schoolNewsWeb/src/assets/imgs/hot.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/schoolNewsWeb/src/assets/styles/common.scss b/schoolNewsWeb/src/assets/styles/common.scss index c2578d3..174ac3c 100644 --- a/schoolNewsWeb/src/assets/styles/common.scss +++ b/schoolNewsWeb/src/assets/styles/common.scss @@ -37,7 +37,15 @@ $spacing-xl: 20px; $spacing-xxl: 24px; // ============ 按钮样式 ============ - +.btn-default { + background: #409eff; + border: none; + border-radius: $border-radius-medium; + color: $color-bg-white; + font-size: $font-size-base; + font-weight: 500; + cursor: pointer; +} // 主要操作按钮 .btn-primary { align-items: center; @@ -188,7 +196,7 @@ $spacing-xxl: 24px; align-items: center; padding: $spacing-md 0; margin-top: 32px; - background: #F9FAFB; + // background: #F9FAFB; border-radius: $border-radius-large; // Element Plus 分页组件自定义样式 diff --git a/schoolNewsWeb/src/types/resource/index.ts b/schoolNewsWeb/src/types/resource/index.ts index 69f54e0..4223188 100644 --- a/schoolNewsWeb/src/types/resource/index.ts +++ b/schoolNewsWeb/src/types/resource/index.ts @@ -178,6 +178,79 @@ export interface ResourceRecommend extends BaseDTO { status?: number; } +/** + * 资源推荐VO(平铺结构,包含资源信息和权限信息) + */ +export interface ResourceRecommendVO extends BaseDTO { + // ==================== 推荐表字段 ==================== + /** 推荐ID */ + id?: string; + /** 资源ID */ + resourceID?: string; + /** 推荐类型(1-热门资源,2-思政资源) */ + recommendType?: number; + /** 排序号 */ + orderNum?: number; + /** 推荐理由 */ + reason?: string; + /** 推荐创建者 */ + creator?: string; + /** 推荐更新者 */ + updater?: string; + /** 推荐创建时间 */ + createTime?: string; + /** 推荐更新时间 */ + updateTime?: string; + /** 推荐删除时间 */ + deleteTime?: string; + /** 推荐是否删除 */ + deleted?: boolean; + + // ==================== 资源表字段 ==================== + /** 资源标题 */ + title?: string; + /** 资源简介 */ + summary?: string; + /** 封面图片 */ + coverImage?: string; + /** 标签ID(文章分类标签,tagType=1) */ + tagID?: string; + /** 作者 */ + author?: string; + /** 来源 */ + source?: string; + /** 来源URL */ + sourceUrl?: string; + /** 浏览次数 */ + viewCount?: number; + /** 点赞次数 */ + likeCount?: number; + /** 收藏次数 */ + collectCount?: number; + /** 状态(0草稿 1已发布 2下架) */ + status?: number; + /** 是否推荐 */ + isRecommend?: boolean; + /** 是否轮播 */ + isBanner?: boolean; + /** 发布时间 */ + publishTime?: string; + /** 资源创建者 */ + resourceCreator?: string; + /** 资源更新者 */ + resourceUpdater?: string; + /** 资源创建时间 */ + resourceCreateTime?: string; + /** 资源更新时间 */ + resourceUpdateTime?: string; + + // ==================== 权限字段 ==================== + /** 是否可读 */ + canRead?: boolean; + /** 是否可写 */ + canWrite?: boolean; +} + /** * 数据采集配置实体 */ diff --git a/schoolNewsWeb/src/types/study/index.ts b/schoolNewsWeb/src/types/study/index.ts index 5f206d1..5033d2a 100644 --- a/schoolNewsWeb/src/types/study/index.ts +++ b/schoolNewsWeb/src/types/study/index.ts @@ -5,6 +5,7 @@ */ import type { BaseDTO } from '@/types'; +import type { Tag } from '@/types/resource'; /** @@ -344,6 +345,8 @@ export interface TaskVO extends BaseDTO { taskResources: TaskItemVO[]; /** 任务关联的用户列表 */ taskUsers: TaskItemVO[]; + /** 任务关联的标签列表 */ + taskTags?: Tag[]; /** 总任务数 */ totalTaskNum?: number; /** 已完成任务数 */ diff --git a/schoolNewsWeb/src/types/usercenter/index.ts b/schoolNewsWeb/src/types/usercenter/index.ts index 8eaf9ea..8e28325 100644 --- a/schoolNewsWeb/src/types/usercenter/index.ts +++ b/schoolNewsWeb/src/types/usercenter/index.ts @@ -20,6 +20,67 @@ export interface UserCollection extends BaseDTO { collectionValue?: number; } +/** + * 用户收藏VO - 扁平化收藏信息和关联资源/课程详情 + */ +export interface UserCollectionVO extends BaseDTO { + // ========== 收藏基本信息 ========== + /** 用户ID */ + userID?: string; + /** 收藏类型(1资源 2课程) */ + collectionType?: CollectionType; + /** 收藏对象ID */ + collectionID?: string; + /** 是否新增收藏(1是 -1否) */ + collectionValue?: number; + /** 收藏时间 */ + collectionTime?: string; + + // ========== 资源详情(collectionType=1时有效) ========== + /** 资源ID */ + resourceID?: string; + /** 资源标题 */ + title?: string; + /** 资源内容 */ + content?: string; + /** 资源简介 */ + summary?: string; + /** 封面图片 */ + coverImage?: string; + /** 标签ID */ + tagID?: string; + /** 作者 */ + author?: string; + /** 来源 */ + source?: string; + /** 浏览次数 */ + viewCount?: number; + /** 点赞次数 */ + likeCount?: number; + /** 收藏次数 */ + collectCount?: number; + /** 资源状态 */ + status?: number; + /** 发布时间 */ + publishTime?: string; + + // ========== 课程详情(collectionType=2时有效) ========== + /** 课程ID */ + courseID?: string; + /** 课程名称 */ + courseName?: string; + /** 课程描述 */ + description?: string; + /** 课程时长(分钟) */ + duration?: number; + /** 授课老师 */ + teacher?: string; + /** 课程状态 */ + courseStatus?: number; + /** 学习人数 */ + learnCount?: number; +} + /** * 用户浏览记录实体 */ diff --git a/schoolNewsWeb/src/views/admin/manage/content/ColumnManagementView.vue b/schoolNewsWeb/src/views/admin/manage/content/ColumnManagementView.vue index 64b3b90..4a53098 100644 --- a/schoolNewsWeb/src/views/admin/manage/content/ColumnManagementView.vue +++ b/schoolNewsWeb/src/views/admin/manage/content/ColumnManagementView.vue @@ -1,76 +1,1106 @@ diff --git a/schoolNewsWeb/src/views/admin/manage/content/TagManagementView.vue b/schoolNewsWeb/src/views/admin/manage/content/TagManagementView.vue index 54b696a..290fe48 100644 --- a/schoolNewsWeb/src/views/admin/manage/content/TagManagementView.vue +++ b/schoolNewsWeb/src/views/admin/manage/content/TagManagementView.vue @@ -13,89 +13,171 @@ - -
-
+
+ +
+
+

文章分类标签

+ ({{ articleTags.length }}) +
+
+
+
+
+
+ {{ tag.name }} +
+
+
+ + +
+
+ +
+ 暂无文章分类标签 +
+
+
+ + +
+
+

课程分类标签

+ ({{ courseTags.length }}) +
+
+
+
+
+
+ {{ tag.name }} +
+
+
+ + +
+
+ +
+ 暂无课程分类标签 +
+
+
+ + +
+
+

学习任务分类标签

+ ({{ taskTags.length }}) +
+
+
+
+
+
+ {{ tag.name }} +
+
+
+ + +
+
+ +
+ 暂无学习任务分类标签 +
+
+
+ + +
+ 暂无标签数据 +
+
+ + + -
-
-
- {{ tag.name }} -
- -
+ + + + + + + + + + + + -
- - -
-
+ +
+ + +
+
- -
- 暂无标签数据 -
-
+ + + + - - - - - - - - - - - - - - - - -
- - -
-
- - - - -
- - -
+ + diff --git a/schoolNewsWeb/src/views/user/home/HomeView.vue b/schoolNewsWeb/src/views/user/home/HomeView.vue index a9b529a..a4a5549 100644 --- a/schoolNewsWeb/src/views/user/home/HomeView.vue +++ b/schoolNewsWeb/src/views/user/home/HomeView.vue @@ -31,13 +31,24 @@

热门资源推荐

- -
- +
+
+

加载中...

+
+
+ +
+
+

暂无热门资源

@@ -45,13 +56,24 @@

思政新闻概览

- -
- +
+
+

加载中...

+
+
+ +
+
+

暂无思政资源

@@ -59,7 +81,7 @@

我的学习数据

- -
+
@@ -63,7 +65,7 @@ const resources = ref([]); const loading = ref(false); const total = ref(0); const currentPage = ref(1); -const pageSize = 10; +const pageSize = ref(10); const listContainerRef = ref(); onMounted(() => { @@ -89,7 +91,7 @@ async function loadResources() { const pageParam: PageParam = { pageNumber: currentPage.value, - pageSize: pageSize + pageSize: pageSize.value }; const res = await resourceApi.getResourcePage(pageParam, filter); @@ -119,6 +121,12 @@ function handlePageChange(page: number) { loadResources(); } +function handleSizeChange(size: number) { + pageSize.value = size; + currentPage.value = 1; + loadResources(); +} + function getResources() { return resources.value; } @@ -128,7 +136,7 @@ function getPageInfo() { } async function loadNextPage() { - const totalPages = Math.ceil(total.value / pageSize); + const totalPages = Math.ceil(total.value / pageSize.value); if (currentPage.value < totalPages) { currentPage.value++; await loadResources(); diff --git a/schoolNewsWeb/src/views/user/user-center/MyAchievementsView.vue b/schoolNewsWeb/src/views/user/user-center/MyAchievementsView.vue index 7b1640a..1d3b617 100644 --- a/schoolNewsWeb/src/views/user/user-center/MyAchievementsView.vue +++ b/schoolNewsWeb/src/views/user/user-center/MyAchievementsView.vue @@ -243,13 +243,15 @@ onMounted(() => { diff --git a/schoolNewsWeb/src/views/user/user-center/UserCenterView.vue b/schoolNewsWeb/src/views/user/user-center/UserCenterView.vue index 93963f0..b78f3ce 100644 --- a/schoolNewsWeb/src/views/user/user-center/UserCenterView.vue +++ b/schoolNewsWeb/src/views/user/user-center/UserCenterView.vue @@ -51,24 +51,27 @@ const menus = computed(() => {