From 049b6f2cf3c815fb70fda077ddb4e24cee82eff8 Mon Sep 17 00:00:00 2001 From: wangys <3401275564@qq.com> Date: Tue, 18 Nov 2025 11:48:01 +0800 Subject: [PATCH] =?UTF-8?q?=E6=90=9C=E7=B4=A2=E3=80=81=E5=B0=8F=E5=8A=A9?= =?UTF-8?q?=E6=89=8B=E6=8E=A8=E8=8D=90=E4=BD=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../.bin/mysql/sql/initMenuData.sql | 3 +- .../api/news/resource/ResourceService.java | 10 + .../java/org/xyzh/common/vo/TaskItemVO.java | 39 +- .../news/controller/ResourceController.java | 6 + .../org/xyzh/news/mapper/ResourceMapper.java | 3 + .../service/impl/NCResourceServiceImpl.java | 21 + .../main/resources/mapper/ResourceMapper.xml | 115 +++ .../resources/mapper/LearningRecordMapper.xml | 4 +- schoolNewsWeb/src/apis/resource/resource.ts | 18 +- .../src/components/base/TopNavigation.vue | 8 +- schoolNewsWeb/src/config/index.ts | 1 + schoolNewsWeb/src/types/study/index.ts | 10 + schoolNewsWeb/src/views/public/ai/AIAgent.vue | 3 +- .../src/views/public/ai/AIRecommend.vue | 271 ++++++ schoolNewsWeb/src/views/public/ai/index.ts | 3 +- .../views/user/resource-center/SearchView.vue | 769 ++++++++++++++++++ .../components/ResourceList.vue | 6 +- .../user/user-center/LearningRecordsView.vue | 13 +- 18 files changed, 1280 insertions(+), 23 deletions(-) create mode 100644 schoolNewsWeb/src/views/public/ai/AIRecommend.vue create mode 100644 schoolNewsWeb/src/views/user/resource-center/SearchView.vue diff --git a/schoolNewsServ/.bin/mysql/sql/initMenuData.sql b/schoolNewsServ/.bin/mysql/sql/initMenuData.sql index fca4d62..86f5213 100644 --- a/schoolNewsServ/.bin/mysql/sql/initMenuData.sql +++ b/schoolNewsServ/.bin/mysql/sql/initMenuData.sql @@ -131,6 +131,7 @@ INSERT INTO `tb_sys_menu` VALUES ('500', 'menu_profile', '账号中心', 'menu_user_dropdown', '/profile', 'user/profile/ProfileView', NULL, 5, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0), ('501', 'menu_personal_info', '个人信息', 'menu_profile', '/profile/personal-info', 'user/profile/PersonalInfoView', NULL, 1, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), ('502', 'menu_account_settings', '账号设置', 'menu_profile', '/profile/account-settings', 'user/profile/AccountSettingsView', NULL, 2, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('503', 'menu_search', '搜索', NULL, '/search', 'user/resource-center/SearchView', NULL, 3, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), -- 管理后台菜单 (1000-8999) ('1000', 'menu_admin_overview', '系统总览', NULL, '/admin/overview', 'admin/overview/SystemOverviewView', 'admin/overview.svg', 1, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:52:32', NULL, 0), ('2000', 'menu_sys_manage', '系统管理', NULL, '', '', 'admin/settings.svg', 2, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:52:35', NULL, 0), @@ -191,7 +192,7 @@ INSERT INTO `tb_sys_menu_permission` (id, permission_id, menu_id, creator, creat ('122', 'perm_default', 'menu_course_detail', '1', now()), ('123', 'perm_default', 'menu_course_study', '1', now()), ('124', 'perm_default', 'menu_article_show', '1', now()), - +('125', 'perm_default', 'menu_search', '1', now()), -- 后端管理菜单权限关联 ('200', 'perm_system_manage', 'menu_admin_overview', '1', now()), 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 1ab165f..7e8f930 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 @@ -2,9 +2,11 @@ package org.xyzh.api.news.resource; 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.TbResource; import org.xyzh.common.dto.usercenter.TbUserCollection; import org.xyzh.common.vo.ResourceVO; +import org.xyzh.common.vo.TaskItemVO; import java.util.List; @@ -186,4 +188,12 @@ public interface ResourceService { ResultDomain getResourceCount(TbResource filter); + /** + * @description 搜索资源 + * @param filter 过滤条件 + * @return ResultDomain 搜索结果 + * @author yslg + * @since 2025-10-15 + */ + ResultDomain searchItem(PageRequest filter); } diff --git a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/TaskItemVO.java b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/TaskItemVO.java index 934f25f..4ea20de 100644 --- a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/TaskItemVO.java +++ b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/TaskItemVO.java @@ -24,6 +24,13 @@ public class TaskItemVO extends TbLearningTask { private Integer status; private BigDecimal progress; private Date completeTime; + + // 搜索结果展示字段 + private String coverImage; + private String summary; + private String author; + private Integer viewCount; + private Date publishTime; public String getDeptID() { return deptID; @@ -183,5 +190,35 @@ public class TaskItemVO extends TbLearningTask { this.completeTime = completeTime; } - + // 搜索结果展示字段的 getter 和 setter + public String getCoverImage() { + return coverImage; + } + public void setCoverImage(String coverImage) { + this.coverImage = coverImage; + } + public String getSummary() { + return summary; + } + public void setSummary(String summary) { + this.summary = summary; + } + public String getAuthor() { + return author; + } + public void setAuthor(String author) { + this.author = author; + } + public Integer getViewCount() { + return viewCount; + } + public void setViewCount(Integer viewCount) { + this.viewCount = viewCount; + } + public Date getPublishTime() { + return publishTime; + } + public void setPublishTime(Date publishTime) { + this.publishTime = publishTime; + } } 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 78c2b49..c29e98e 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 @@ -17,6 +17,7 @@ import org.xyzh.common.dto.user.TbSysUser; import org.xyzh.common.dto.usercenter.TbUserCollection; import org.xyzh.common.utils.TimeUtils; import org.xyzh.common.vo.ResourceVO; +import org.xyzh.common.vo.TaskItemVO; import org.xyzh.system.utils.LoginUtil; /** * @description 资源控制器 @@ -192,4 +193,9 @@ public class ResourceController { @RequestParam(value = "status", required = false) Integer status) { return resourceService.searchResources(keyword, tagID, status); } + + @PostMapping("/search") + public ResultDomain searchResources(@RequestBody PageRequest filter) { + return resourceService.searchItem(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 0ed41e2..b171102 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 @@ -6,6 +6,7 @@ import org.apache.ibatis.annotations.Param; import org.xyzh.common.core.page.PageParam; import org.xyzh.common.dto.resource.TbResource; import org.xyzh.common.vo.ResourceVO; +import org.xyzh.common.vo.TaskItemVO; import org.xyzh.common.vo.UserDeptRoleVO; import java.util.List; @@ -205,4 +206,6 @@ public interface ResourceMapper extends BaseMapper { * @since 2025-10-15 */ int incrementViewCount(@Param("resourceID") String resourceID); + + List selectItem(@Param("filter") TbResource filter, @Param("pageParam") PageParam pageParam, @Param("userDeptRoles") List userDeptRoles); } 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 8f4a6dd..7a23504 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 @@ -9,6 +9,7 @@ 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.core.page.PageRequest; import org.xyzh.common.dto.resource.TbResource; import org.xyzh.common.dto.resource.TbResourceTag; import org.xyzh.common.dto.resource.TbTag; @@ -17,6 +18,7 @@ import org.xyzh.common.dto.usercenter.TbUserCollection; import org.xyzh.common.utils.IDUtils; import org.xyzh.common.vo.ResourceVO; import org.xyzh.common.vo.TagVO; +import org.xyzh.common.vo.TaskItemVO; import org.xyzh.news.mapper.ResourceMapper; import org.xyzh.news.mapper.ResourceTagMapper; import org.xyzh.system.utils.LoginUtil; @@ -771,4 +773,23 @@ public class NCResourceServiceImpl implements ResourceService { return resultDomain; } } + + @Override + public ResultDomain searchItem(PageRequest filter) { + ResultDomain resultDomain = new ResultDomain<>(); + try { + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + List list = resourceMapper.selectItem(filter.getFilter(), filter.getPageParam(), userDeptRoles); + if (list == null || list.isEmpty()) { + resultDomain.fail("搜索资源失败: 没有找到资源"); + return resultDomain; + } + resultDomain.success("搜索资源成功", list); + return resultDomain; + } catch (Exception e) { + logger.error("搜索资源异常: {}", e.getMessage(), e); + resultDomain.fail("搜索资源失败: " + e.getMessage()); + return resultDomain; + } + } } diff --git a/schoolNewsServ/news/src/main/resources/mapper/ResourceMapper.xml b/schoolNewsServ/news/src/main/resources/mapper/ResourceMapper.xml index b1f6663..464ac83 100644 --- a/schoolNewsServ/news/src/main/resources/mapper/ResourceMapper.xml +++ b/schoolNewsServ/news/src/main/resources/mapper/ResourceMapper.xml @@ -323,6 +323,9 @@ AND r.author LIKE CONCAT('%', #{filter.author}, '%') + + AND r.title LIKE CONCAT('%', #{filter.title}, '%') + AND r.status = #{filter.status} @@ -451,4 +454,116 @@ + + + diff --git a/schoolNewsServ/study/src/main/resources/mapper/LearningRecordMapper.xml b/schoolNewsServ/study/src/main/resources/mapper/LearningRecordMapper.xml index 4039bb9..51189d1 100644 --- a/schoolNewsServ/study/src/main/resources/mapper/LearningRecordMapper.xml +++ b/schoolNewsServ/study/src/main/resources/mapper/LearningRecordMapper.xml @@ -237,8 +237,8 @@ lr.complete_time AS complete_time FROM tb_learning_record lr LEFT JOIN tb_resource r ON lr.resource_type = 1 AND lr.resource_id = r.resource_id - LEFT JOIN tb_course c ON lr.resource_type = 2 AND lr.course_id = c.id - LEFT JOIN tb_course_chapter ch ON lr.resource_type = 3 AND lr.chapter_id = ch.id + LEFT JOIN tb_course c ON lr.resource_type = 2 AND lr.course_id = c.course_id + LEFT JOIN tb_course_chapter ch ON lr.resource_type = 3 AND lr.chapter_id = ch.chapter_id AND lr.user_id = #{filter.userID} diff --git a/schoolNewsWeb/src/apis/resource/resource.ts b/schoolNewsWeb/src/apis/resource/resource.ts index 8b2bdf2..23bbef4 100644 --- a/schoolNewsWeb/src/apis/resource/resource.ts +++ b/schoolNewsWeb/src/apis/resource/resource.ts @@ -7,7 +7,7 @@ */ import { api } from '@/apis'; -import type { ResultDomain, Resource, ResourceSearchParams, PageParam, ResourceVO, UserCollection } from '@/types'; +import type { ResultDomain, Resource, PageParam, ResourceVO, UserCollection, TaskItemVO } from '@/types'; /** * 资源API服务 @@ -20,7 +20,7 @@ export const resourceApi = { * @param filter 筛选条件 * @returns Promise> */ - async getResourceList(filter?: ResourceSearchParams): Promise> { + async getResourceList(filter?: Resource): Promise> { const response = await api.get('/news/resources/list', filter); return response.data; }, @@ -31,7 +31,7 @@ export const resourceApi = { * @param pageParam 分页参数 * @returns Promise> */ - async getResourcePage(pageParam: PageParam, filter?: ResourceSearchParams): Promise> { + async getResourcePage(pageParam: PageParam, filter?: Resource): Promise> { const response = await api.post('/news/resources/page', { pageParam, filter, @@ -45,7 +45,7 @@ export const resourceApi = { * @param pageParam 分页参数 * @returns Promise> */ - async getResourcePageOrderByViewCount(pageParam: PageParam, filter?: ResourceSearchParams): Promise> { + async getResourcePageOrderByViewCount(pageParam: PageParam, filter?: Resource): Promise> { const response = await api.post('/news/resources/page/view-count', { pageParam, filter, @@ -218,6 +218,16 @@ export const resourceApi = { const response = await api.get('/news/resources/search', params); return response.data; + }, + + /** + * 联合搜索文章和课程 + * @param request 搜索请求参数(包含pageParam和filter) + * @returns Promise> + */ + async searchItems(request: { pageParam: PageParam; filter: Resource }): Promise> { + const response = await api.post('/news/resources/search', request); + return response.data; } }; diff --git a/schoolNewsWeb/src/components/base/TopNavigation.vue b/schoolNewsWeb/src/components/base/TopNavigation.vue index 1020f50..04cc166 100644 --- a/schoolNewsWeb/src/components/base/TopNavigation.vue +++ b/schoolNewsWeb/src/components/base/TopNavigation.vue @@ -208,10 +208,10 @@ function handleNavClick(menu: SysMenu) { } // 处理搜索 -function handleSearch() { - if (searchKeyword.value.trim()) { - // 这里可以跳转到搜索页面或触发搜索功能 - router.push(`/search?keyword=${encodeURIComponent(searchKeyword.value.trim())}`); +function handleSearch(keyword: string) { + if (keyword && keyword.trim()) { + // 跳转到搜索页面 + router.push(`/search?keyword=${encodeURIComponent(keyword.trim())}`); } } diff --git a/schoolNewsWeb/src/config/index.ts b/schoolNewsWeb/src/config/index.ts index 1374531..48cf8fa 100644 --- a/schoolNewsWeb/src/config/index.ts +++ b/schoolNewsWeb/src/config/index.ts @@ -52,5 +52,6 @@ export const APP_CONFIG = { } }; export const PUBLIC_IMG_PATH = 'http://localhost:8080/schoolNewsWeb/img'; +export const PUBLIC_WEB_PATH = 'http://localhost:8080/schoolNewsWeb'; export default APP_CONFIG; diff --git a/schoolNewsWeb/src/types/study/index.ts b/schoolNewsWeb/src/types/study/index.ts index 5033d2a..ed29caa 100644 --- a/schoolNewsWeb/src/types/study/index.ts +++ b/schoolNewsWeb/src/types/study/index.ts @@ -331,6 +331,16 @@ export interface TaskItemVO extends LearningTask { progress?: number; /** 完成时间 */ completeTime?: string; + /** 封面图片(用于搜索结果展示) */ + coverImage?: string; + /** 简介(用于搜索结果展示) */ + summary?: string; + /** 作者(用于搜索结果展示) */ + author?: string; + /** 浏览次数(用于搜索结果展示) */ + viewCount?: number; + /** 发布时间(用于搜索结果展示) */ + publishTime?: Date | string; } /** diff --git a/schoolNewsWeb/src/views/public/ai/AIAgent.vue b/schoolNewsWeb/src/views/public/ai/AIAgent.vue index fa393a8..8f896fe 100644 --- a/schoolNewsWeb/src/views/public/ai/AIAgent.vue +++ b/schoolNewsWeb/src/views/public/ai/AIAgent.vue @@ -71,7 +71,7 @@ AI助手

你好!我是{{ agentConfig?.name || 'AI助手' }}

-

{{ agentConfig?.systemPrompt || '有什么可以帮助你的吗?' }}

+ @@ -189,6 +189,7 @@ import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue'; import { ElMessage, ElMessageBox } from 'element-plus'; import { chatApi, chatHistoryApi, aiAgentConfigApi, fileUploadApi } from '@/apis/ai'; import type { AiConversation, AiMessage, AiAgentConfig, AiUploadFile } from '@/types/ai'; +import { AIRecommend } from '@/views/public/ai'; interface AIAgentProps { agentId?: string; diff --git a/schoolNewsWeb/src/views/public/ai/AIRecommend.vue b/schoolNewsWeb/src/views/public/ai/AIRecommend.vue new file mode 100644 index 0000000..7f322a8 --- /dev/null +++ b/schoolNewsWeb/src/views/public/ai/AIRecommend.vue @@ -0,0 +1,271 @@ + + + \ No newline at end of file diff --git a/schoolNewsWeb/src/views/public/ai/index.ts b/schoolNewsWeb/src/views/public/ai/index.ts index 85132b6..3827907 100644 --- a/schoolNewsWeb/src/views/public/ai/index.ts +++ b/schoolNewsWeb/src/views/public/ai/index.ts @@ -1 +1,2 @@ -export { default as AIAgent} from './AIAgent.vue'; \ No newline at end of file +export { default as AIAgent} from './AIAgent.vue'; +export { default as AIRecommend} from './AIRecommend.vue'; \ No newline at end of file diff --git a/schoolNewsWeb/src/views/user/resource-center/SearchView.vue b/schoolNewsWeb/src/views/user/resource-center/SearchView.vue new file mode 100644 index 0000000..b31f829 --- /dev/null +++ b/schoolNewsWeb/src/views/user/resource-center/SearchView.vue @@ -0,0 +1,769 @@ + + + + + diff --git a/schoolNewsWeb/src/views/user/resource-center/components/ResourceList.vue b/schoolNewsWeb/src/views/user/resource-center/components/ResourceList.vue index eafe479..9dd551a 100644 --- a/schoolNewsWeb/src/views/user/resource-center/components/ResourceList.vue +++ b/schoolNewsWeb/src/views/user/resource-center/components/ResourceList.vue @@ -45,7 +45,7 @@ import { ref, watch, onMounted, nextTick } from 'vue'; import { resourceApi } from '@/apis/resource'; import { FILE_DOWNLOAD_URL } from '@/config'; -import type { Resource, ResourceSearchParams } from '@/types/resource'; +import type { Resource } from '@/types/resource'; import type { PageParam } from '@/types'; import defaultArticleImg from '@/assets/imgs/article-default.png'; @@ -83,9 +83,9 @@ async function loadResources() { loading.value = true; try { - const filter: ResourceSearchParams = { + const filter: Resource = { tagID: props.tagID, - keyword: props.searchKeyword, + title: props.searchKeyword, status: 1 // 只加载已发布的 }; diff --git a/schoolNewsWeb/src/views/user/user-center/LearningRecordsView.vue b/schoolNewsWeb/src/views/user/user-center/LearningRecordsView.vue index 4aa59bf..ab0b6ce 100644 --- a/schoolNewsWeb/src/views/user/user-center/LearningRecordsView.vue +++ b/schoolNewsWeb/src/views/user/user-center/LearningRecordsView.vue @@ -17,20 +17,20 @@

学习记录明细

- - - + + + - - + + - + @@ -38,6 +38,7 @@