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;
|
||||
|
||||
/**
|
||||
* 标签 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 javax.annotation.Resource;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
|
|
@ -62,6 +62,79 @@ public class CmsContentServiceImpl implements CmsContentService {
|
|||
PageHelper.startPage(queryDto.getPageNum(), queryDto.getPageSize());
|
||||
List<CmsContent> list = this.cmsContentMapper.getPageList(queryDto);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@
|
|||
and content_id = #{contentId}
|
||||
</if>
|
||||
<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 test="contentType != null">
|
||||
and content_type = #{contentType}
|
||||
|
|
@ -94,6 +94,13 @@
|
|||
<if test="tagId != null">
|
||||
and find_in_set(#{tagId}, tags)
|
||||
</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>
|
||||
<if test="sortBy != null and sortBy != ''">
|
||||
order by ${sortBy} ${sortDesc ? 'desc' : 'asc'}
|
||||
|
|
@ -115,7 +122,12 @@
|
|||
and content_id = #{queryDto.contentId}
|
||||
</if>
|
||||
<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 test="queryDto.contentType != null">
|
||||
and content_type = #{queryDto.contentType}
|
||||
|
|
@ -144,6 +156,13 @@
|
|||
<if test="queryDto.tagId != null">
|
||||
and find_in_set(#{queryDto.tagId}, tags)
|
||||
</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>
|
||||
<if test="queryDto.sortBy != null and queryDto.sortBy != ''">
|
||||
order by ${queryDto.sortBy} ${queryDto.sortDesc ? 'desc' : 'asc'}
|
||||
|
|
@ -209,6 +228,11 @@
|
|||
<if test="title != null and title != ''">
|
||||
and (title like concat('%', #{title}, '%') or summary like concat('%', #{title}, '%'))
|
||||
</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">
|
||||
and content_type = #{contentType}
|
||||
</if>
|
||||
|
|
@ -230,6 +254,13 @@
|
|||
<if test="tagId != null">
|
||||
and find_in_set(#{tagId}, tags)
|
||||
</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>
|
||||
order by sort asc, create_time desc
|
||||
</select>
|
||||
|
|
|
|||
Loading…
Reference in New Issue