feat(search): 实现用户搜索功能
- 添加 Elasticsearch 客户端配置 - 创建用户搜索接口和实现类 - 定义搜索请求和响应 VO 类 - 集成分页查询和关键字匹配逻辑 - 添加 Elasticsearch 测试用例 - 更新 pom.xml 依赖和版本管理 - 添加 HTTP 客户端测试脚本
This commit is contained in:
@@ -1,31 +1,40 @@
|
||||
package com.hanserwei.hannote.search.config;
|
||||
|
||||
import co.elastic.clients.elasticsearch.ElasticsearchClient;
|
||||
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
|
||||
import co.elastic.clients.transport.ElasticsearchTransport;
|
||||
import co.elastic.clients.transport.rest_client.RestClientTransport;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||
import org.apache.http.HttpHost;
|
||||
import org.elasticsearch.client.RestClient;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
|
||||
@Configuration
|
||||
public class ElasticsearchConfig {
|
||||
|
||||
@Value("${elasticsearch.host}")
|
||||
private String host;
|
||||
|
||||
@Value("${elasticsearch.port}")
|
||||
private int port;
|
||||
@Bean
|
||||
public ElasticsearchClient elasticsearchClient() {
|
||||
// 1. 创建底层 RestClient(低级客户端)
|
||||
RestClient restClient = RestClient
|
||||
.builder(HttpHost.create(host))
|
||||
.build();
|
||||
|
||||
@Value("${elasticsearch.scheme:http}")
|
||||
private String scheme;
|
||||
// 2. 创建 JSON 映射器
|
||||
ObjectMapper mapper = JsonMapper.builder().build();
|
||||
JacksonJsonpMapper jsonpMapper = new JacksonJsonpMapper(mapper);
|
||||
|
||||
@Value("${elasticsearch.username:}")
|
||||
private String username;
|
||||
|
||||
@Value("${elasticsearch.password:}")
|
||||
private String password;
|
||||
|
||||
@Value("${elasticsearch.api-key:}")
|
||||
private String apiKey;
|
||||
|
||||
@Value("${elasticsearch.use-api-key:false}")
|
||||
private boolean useApiKey;
|
||||
// 3. 构建传输层
|
||||
ElasticsearchTransport transport = new RestClientTransport(restClient, jsonpMapper);
|
||||
|
||||
// 4. 创建高层次的 Elasticsearch 客户端
|
||||
return new ElasticsearchClient(transport);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.hanserwei.hannote.search.controller;
|
||||
|
||||
import com.hanserwei.framework.biz.operationlog.aspect.ApiOperationLog;
|
||||
import com.hanserwei.framework.common.response.PageResponse;
|
||||
import com.hanserwei.hannote.search.model.vo.SearchUserReqVO;
|
||||
import com.hanserwei.hannote.search.model.vo.SearchUserRspVO;
|
||||
import com.hanserwei.hannote.search.service.UserService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/search")
|
||||
@Slf4j
|
||||
public class UserController {
|
||||
|
||||
@Resource
|
||||
private UserService userService;
|
||||
|
||||
@PostMapping("/user")
|
||||
@ApiOperationLog(description = "搜索用户")
|
||||
public PageResponse<SearchUserRspVO> searchUser(@RequestBody @Validated SearchUserReqVO searchUserReqVO) {
|
||||
return userService.searchUser(searchUserReqVO);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.hanserwei.hannote.search.model.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
@@ -9,11 +11,13 @@ import lombok.NoArgsConstructor;
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class SearchUserRspVO {
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
@JsonProperty("id")
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
@@ -29,16 +33,19 @@ public class SearchUserRspVO {
|
||||
/**
|
||||
* 小憨书ID
|
||||
*/
|
||||
@JsonProperty("han_note_id")
|
||||
private String hanNoteId;
|
||||
|
||||
/**
|
||||
* 笔记发布总数
|
||||
*/
|
||||
@JsonProperty("note_total")
|
||||
private Integer noteTotal;
|
||||
|
||||
/**
|
||||
* 粉丝总数
|
||||
*/
|
||||
@JsonProperty("fans_total")
|
||||
private Integer fansTotal;
|
||||
|
||||
}
|
||||
@@ -1,14 +1,104 @@
|
||||
package com.hanserwei.hannote.search.service.impl;
|
||||
|
||||
import co.elastic.clients.elasticsearch.ElasticsearchClient;
|
||||
import co.elastic.clients.elasticsearch._types.SortOrder;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.TextQueryType;
|
||||
import co.elastic.clients.elasticsearch.core.SearchRequest;
|
||||
import co.elastic.clients.elasticsearch.core.SearchResponse;
|
||||
import co.elastic.clients.elasticsearch.core.search.Hit;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.hanserwei.framework.common.response.PageResponse;
|
||||
import com.hanserwei.hannote.search.index.UserIndex;
|
||||
import com.hanserwei.hannote.search.model.vo.SearchUserReqVO;
|
||||
import com.hanserwei.hannote.search.model.vo.SearchUserRspVO;
|
||||
import com.hanserwei.hannote.search.service.UserService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class UserServiceImpl implements UserService {
|
||||
|
||||
@Resource
|
||||
private ElasticsearchClient client;
|
||||
|
||||
/**
|
||||
* 获取 SearchUserRspVO
|
||||
*
|
||||
* @param hit 搜索结果
|
||||
* @return SearchUserRspVO
|
||||
*/
|
||||
private static SearchUserRspVO getSearchUserRspVO(Hit<SearchUserRspVO> hit) {
|
||||
SearchUserRspVO searchUserRspVO = new SearchUserRspVO();
|
||||
|
||||
SearchUserRspVO source = hit.source();
|
||||
if (source != null) {
|
||||
searchUserRspVO.setUserId(source.getUserId());
|
||||
searchUserRspVO.setNickname(source.getNickname());
|
||||
searchUserRspVO.setAvatar(source.getAvatar());
|
||||
searchUserRspVO.setHanNoteId(source.getHanNoteId());
|
||||
searchUserRspVO.setNoteTotal(source.getNoteTotal());
|
||||
searchUserRspVO.setFansTotal(source.getFansTotal());
|
||||
}
|
||||
return searchUserRspVO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResponse<SearchUserRspVO> searchUser(SearchUserReqVO searchUserReqVO) {
|
||||
return null;
|
||||
// 查询关键字
|
||||
String keyword = searchUserReqVO.getKeyword();
|
||||
// 当前页码
|
||||
Integer pageNo = searchUserReqVO.getPageNo();
|
||||
|
||||
int pageSize = 10;
|
||||
|
||||
// 构建SearchRequest,指定索引
|
||||
SearchRequest searchRequest = new SearchRequest.Builder()
|
||||
.index(UserIndex.NAME)
|
||||
.query(query -> query
|
||||
.multiMatch(multiMatch -> multiMatch
|
||||
.query(keyword)
|
||||
.fields(UserIndex.FIELD_USER_NICKNAME, UserIndex.FIELD_USER_HAN_NOTE_ID)
|
||||
.type(TextQueryType.PhrasePrefix)))
|
||||
.sort(sort -> sort
|
||||
.field(filedSort -> filedSort.field(UserIndex.FIELD_USER_FANS_TOTAL).order(SortOrder.Desc)))
|
||||
.from((pageNo - 1) * pageSize)
|
||||
.size(pageSize)
|
||||
.build();
|
||||
|
||||
// 返参 VO 集合
|
||||
List<SearchUserRspVO> searchUserRspVOS = null;
|
||||
// 总文档数,默认为 0
|
||||
long total = 0;
|
||||
try {
|
||||
log.info("==> SearchRequest:{}", searchRequest);
|
||||
|
||||
// 执行查询请求
|
||||
SearchResponse<SearchUserRspVO> searchResponse = client.search(searchRequest, SearchUserRspVO.class);
|
||||
|
||||
searchUserRspVOS = Lists.newArrayList();
|
||||
|
||||
// 处理搜索结果
|
||||
List<Hit<SearchUserRspVO>> hits = searchResponse.hits().hits();
|
||||
if (searchResponse.hits().total() != null) {
|
||||
total = searchResponse.hits().total().value();
|
||||
}
|
||||
|
||||
for (Hit<SearchUserRspVO> hit : hits) {
|
||||
log.info("==> 文档数据: {}", hit.toString());
|
||||
if (hit.source() != null) {
|
||||
SearchUserRspVO searchUserRspVO = getSearchUserRspVO(hit);
|
||||
searchUserRspVOS.add(searchUserRspVO);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("==> 查询 Elasticsearch 异常: ", e);
|
||||
}
|
||||
return PageResponse.success(searchUserRspVOS, pageNo, total);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user