feat(content): 增强内容搜索功能支持关键词匹配和标签关联推荐
- 在 CmsContentDto 中新增 tagIdList 和 keyword 字段用于批量查询和全文搜索 - 修改数据库查询映射文件支持基于关键词的标题、描述、标签多字段模糊匹配 - 实现标签 ID 列表批量查询功能,支持多标签条件筛选 - 添加基于关键词搜索的智能关联推荐机制,根据首个结果的标签自动推荐相关内容 - 优化分页查询逻辑,在关键词搜索结果较少时自动补充相关技能内容 - 增强搜索结果排序算法,综合考虑排序权重和创建时间因素
This commit is contained in:
parent
af0ae4bac1
commit
1b0d102ef9
|
|
@ -46,4 +46,14 @@ public class CmsContentDto extends BaseQueryDto {
|
||||||
|
|
||||||
private Long tagId;
|
private Long tagId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标签 ID 列表,用于批量查询
|
||||||
|
*/
|
||||||
|
private List<Long> tagIdList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 搜索关键字,同时搜索 title、description、tags
|
||||||
|
*/
|
||||||
|
private String keyword;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.Date;
|
import java.util.*;
|
||||||
import java.util.List;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
|
||||||
|
|
@ -58,10 +58,83 @@ public class CmsContentServiceImpl implements CmsContentService {
|
||||||
queryDto.setPageSize(BaseQueryDto.DEFAULT_PAGE_SIZE);
|
queryDto.setPageSize(BaseQueryDto.DEFAULT_PAGE_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用PageHelper进行分页
|
// 使用 PageHelper 进行分页
|
||||||
PageHelper.startPage(queryDto.getPageNum(), queryDto.getPageSize());
|
PageHelper.startPage(queryDto.getPageNum(), queryDto.getPageSize());
|
||||||
List<CmsContent> list = this.cmsContentMapper.getPageList(queryDto);
|
List<CmsContent> list = this.cmsContentMapper.getPageList(queryDto);
|
||||||
PageInfo<CmsContent> pageInfo = new PageInfo<>(list);
|
PageInfo<CmsContent> pageInfo = new PageInfo<>(list);
|
||||||
|
|
||||||
|
// 如果使用了 keyword 搜索且返回结果小于等于 3 条,则根据第一个 skill 的标签查询相关 skill
|
||||||
|
if (queryDto.getKeyword() != null && !queryDto.getKeyword().trim().isEmpty()
|
||||||
|
&& list.size() <= 3 && !list.isEmpty()) {
|
||||||
|
// 获取第一个 skill 的标签
|
||||||
|
CmsContent firstSkill = list.get(0);
|
||||||
|
String tagsStr = firstSkill.getTags();
|
||||||
|
if (tagsStr != null && !tagsStr.trim().isEmpty()) {
|
||||||
|
// 解析标签(逗号分隔)
|
||||||
|
String[] tags = tagsStr.split(",");
|
||||||
|
if (tags.length > 0) {
|
||||||
|
// 构建标签 ID 列表
|
||||||
|
Set<Long> tagIdSet = new HashSet<>();
|
||||||
|
for (String tag : tags) {
|
||||||
|
if (tag != null && !tag.trim().isEmpty()) {
|
||||||
|
try {
|
||||||
|
Long tagId = Long.parseLong(tag.trim());
|
||||||
|
tagIdSet.add(tagId);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// 忽略格式不正确的标签
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果有有效的标签 ID,查询相关内容
|
||||||
|
if (!tagIdSet.isEmpty()) {
|
||||||
|
// 排除已返回的 contentId
|
||||||
|
Set<Long> existingIds = list.stream()
|
||||||
|
.map(CmsContent::getContentId)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
// 构建查询条件,使用 tagIdList 一次性查询
|
||||||
|
CmsContentDto tagQueryDto = new CmsContentDto();
|
||||||
|
tagQueryDto.setDeleteFlag(0);
|
||||||
|
tagQueryDto.setPublishStatus(2); // 已发布
|
||||||
|
tagQueryDto.setTagIdList(new ArrayList<>(tagIdSet));
|
||||||
|
tagQueryDto.setPageNum(1);
|
||||||
|
tagQueryDto.setPageSize(1000);
|
||||||
|
|
||||||
|
// 查询包含这些标签的所有 skill
|
||||||
|
List<CmsContent> taggedContents = this.cmsContentMapper.getPageList(tagQueryDto);
|
||||||
|
|
||||||
|
// 过滤掉已返回的内容
|
||||||
|
List<CmsContent> relatedContents = taggedContents.stream()
|
||||||
|
.filter(content -> !existingIds.contains(content.getContentId()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// 如果有相关 skill,添加到结果中
|
||||||
|
if (!relatedContents.isEmpty()) {
|
||||||
|
// 按 sort 和 create_time 排序
|
||||||
|
relatedContents.sort((a, b) -> {
|
||||||
|
int sortCompare = Integer.compare(
|
||||||
|
a.getSort() != null ? a.getSort() : 0,
|
||||||
|
b.getSort() != null ? b.getSort() : 0);
|
||||||
|
if (sortCompare != 0) {
|
||||||
|
return sortCompare;
|
||||||
|
}
|
||||||
|
if (a.getCreateTime() == null || b.getCreateTime() == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return b.getCreateTime().compareTo(a.getCreateTime());
|
||||||
|
});
|
||||||
|
|
||||||
|
list.addAll(relatedContents);
|
||||||
|
// 重新构建 PageInfo
|
||||||
|
pageInfo = new PageInfo<>(list);
|
||||||
|
pageInfo.setTotal(list.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return pageInfo;
|
return pageInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@
|
||||||
and content_id = #{contentId}
|
and content_id = #{contentId}
|
||||||
</if>
|
</if>
|
||||||
<if test="title != null and title != ''">
|
<if test="title != null and title != ''">
|
||||||
and (title like concat('%', #{title}, '%') or summary like concat('%', #{title}, '%'))
|
and (title like concat('%', #{title}, '%') or description like concat('%', #{title}, '%'))
|
||||||
</if>
|
</if>
|
||||||
<if test="contentType != null">
|
<if test="contentType != null">
|
||||||
and content_type = #{contentType}
|
and content_type = #{contentType}
|
||||||
|
|
@ -94,6 +94,13 @@
|
||||||
<if test="tagId != null">
|
<if test="tagId != null">
|
||||||
and find_in_set(#{tagId}, tags)
|
and find_in_set(#{tagId}, tags)
|
||||||
</if>
|
</if>
|
||||||
|
<if test="tagIdList != null and tagIdList.size() > 0">
|
||||||
|
and (
|
||||||
|
<foreach collection="tagIdList" item="tagId" separator=" or ">
|
||||||
|
find_in_set(#{tagId}, tags)
|
||||||
|
</foreach>
|
||||||
|
)
|
||||||
|
</if>
|
||||||
</where>
|
</where>
|
||||||
<if test="sortBy != null and sortBy != ''">
|
<if test="sortBy != null and sortBy != ''">
|
||||||
order by ${sortBy} ${sortDesc ? 'desc' : 'asc'}
|
order by ${sortBy} ${sortDesc ? 'desc' : 'asc'}
|
||||||
|
|
@ -115,7 +122,12 @@
|
||||||
and content_id = #{queryDto.contentId}
|
and content_id = #{queryDto.contentId}
|
||||||
</if>
|
</if>
|
||||||
<if test="queryDto.title != null and queryDto.title != ''">
|
<if test="queryDto.title != null and queryDto.title != ''">
|
||||||
and (title like concat('%', #{queryDto.title}, '%') or summary like concat('%', #{queryDto.title}, '%'))
|
and (title like concat('%', #{queryDto.title}, '%') or description like concat('%', #{queryDto.title}, '%'))
|
||||||
|
</if>
|
||||||
|
<if test="queryDto.keyword != null and queryDto.keyword != ''">
|
||||||
|
and (title like concat('%', #{queryDto.keyword}, '%')
|
||||||
|
or description like concat('%', #{queryDto.keyword}, '%')
|
||||||
|
or tags like concat('%', #{queryDto.keyword}, '%'))
|
||||||
</if>
|
</if>
|
||||||
<if test="queryDto.contentType != null">
|
<if test="queryDto.contentType != null">
|
||||||
and content_type = #{queryDto.contentType}
|
and content_type = #{queryDto.contentType}
|
||||||
|
|
@ -144,6 +156,13 @@
|
||||||
<if test="queryDto.tagId != null">
|
<if test="queryDto.tagId != null">
|
||||||
and find_in_set(#{queryDto.tagId}, tags)
|
and find_in_set(#{queryDto.tagId}, tags)
|
||||||
</if>
|
</if>
|
||||||
|
<if test="queryDto.tagIdList != null and queryDto.tagIdList.size() > 0">
|
||||||
|
and (
|
||||||
|
<foreach collection="queryDto.tagIdList" item="tagId" separator=" or ">
|
||||||
|
find_in_set(#{tagId}, tags)
|
||||||
|
</foreach>
|
||||||
|
)
|
||||||
|
</if>
|
||||||
</where>
|
</where>
|
||||||
<if test="queryDto.sortBy != null and queryDto.sortBy != ''">
|
<if test="queryDto.sortBy != null and queryDto.sortBy != ''">
|
||||||
order by ${queryDto.sortBy} ${queryDto.sortDesc ? 'desc' : 'asc'}
|
order by ${queryDto.sortBy} ${queryDto.sortDesc ? 'desc' : 'asc'}
|
||||||
|
|
@ -209,6 +228,11 @@
|
||||||
<if test="title != null and title != ''">
|
<if test="title != null and title != ''">
|
||||||
and (title like concat('%', #{title}, '%') or summary like concat('%', #{title}, '%'))
|
and (title like concat('%', #{title}, '%') or summary like concat('%', #{title}, '%'))
|
||||||
</if>
|
</if>
|
||||||
|
<if test="keyword != null and keyword != ''">
|
||||||
|
and (title like concat('%', #{keyword}, '%')
|
||||||
|
or description like concat('%', #{keyword}, '%')
|
||||||
|
or tags like concat('%', #{keyword}, '%'))
|
||||||
|
</if>
|
||||||
<if test="contentType != null">
|
<if test="contentType != null">
|
||||||
and content_type = #{contentType}
|
and content_type = #{contentType}
|
||||||
</if>
|
</if>
|
||||||
|
|
@ -230,6 +254,13 @@
|
||||||
<if test="tagId != null">
|
<if test="tagId != null">
|
||||||
and find_in_set(#{tagId}, tags)
|
and find_in_set(#{tagId}, tags)
|
||||||
</if>
|
</if>
|
||||||
|
<if test="tagIdList != null and tagIdList.size() > 0">
|
||||||
|
and (
|
||||||
|
<foreach collection="tagIdList" item="tagId" separator=" or ">
|
||||||
|
find_in_set(#{tagId}, tags)
|
||||||
|
</foreach>
|
||||||
|
)
|
||||||
|
</if>
|
||||||
</where>
|
</where>
|
||||||
order by sort asc, create_time desc
|
order by sort asc, create_time desc
|
||||||
</select>
|
</select>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue