From 3437c2bff42299902a8900c9f2d6b058969ce140 Mon Sep 17 00:00:00 2001 From: Hanserwei <2628273921@qq.com> Date: Wed, 29 Oct 2025 22:37:08 +0800 Subject: [PATCH] =?UTF-8?q?feat(search):=20=E5=88=9D=E5=A7=8B=E5=8C=96?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E6=9C=8D=E5=8A=A1=E6=A8=A1=E5=9D=97-=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=90=9C=E7=B4=A2=E6=9C=8D=E5=8A=A1=E5=9F=BA?= =?UTF-8?q?=E7=A1=80=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6=20application.yml?= =?UTF-8?q?=20=E5=92=8C=20bootstrap.yml=20-=20=E9=85=8D=E7=BD=AE=20Nacos?= =?UTF-8?q?=20=E6=9C=8D=E5=8A=A1=E5=8F=91=E7=8E=B0=E4=B8=8E=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E4=B8=AD=E5=BF=83=E9=9B=86=E6=88=90=20-=20=E5=BC=95?= =?UTF-8?q?=E5=85=A5=20Elasticsearch=20=E5=AE=A2=E6=88=B7=E7=AB=AF?= =?UTF-8?q?=E4=BE=9D=E8=B5=96=E5=B9=B6=E9=85=8D=E7=BD=AE=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=E5=8F=82=E6=95=B0=20-=20=E5=88=9B=E5=BB=BA=E5=85=A8=E5=B1=80?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E5=A4=84=E7=90=86=E5=99=A8=20GlobalException?= =?UTF-8?q?Handler=20-=20=E5=AE=9A=E4=B9=89=E5=93=8D=E5=BA=94=E7=A0=81?= =?UTF-8?q?=E6=9E=9A=E4=B8=BE=20ResponseCodeEnum=20-=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E6=90=9C=E7=B4=A2=E8=AF=B7=E6=B1=82/?= =?UTF-8?q?=E5=93=8D=E5=BA=94=20VO=20=E7=B1=BB=20-=20=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E7=B4=A2=E5=BC=95=E5=B8=B8=E9=87=8F=20UserIn?= =?UTF-8?q?dex=20-=20=E5=88=9B=E5=BB=BA=20UserService=20=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E5=8F=8A=E5=AE=9E=E7=8E=B0=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/compiler.xml | 1 + .idea/encodings.xml | 2 + han-note-search/pom.xml | 73 +++++++++++++ .../search/HannoteSearchApplication.java | 11 ++ .../search/config/ElasticsearchConfig.java | 31 ++++++ .../search/enums/ResponseCodeEnum.java | 23 ++++ .../exception/GlobalExceptionHandler.java | 103 ++++++++++++++++++ .../hannote/search/index/UserIndex.java | 40 +++++++ .../search/model/vo/SearchUserReqVO.java | 22 ++++ .../search/model/vo/SearchUserRspVO.java | 44 ++++++++ .../hannote/search/service/UserService.java | 16 +++ .../search/service/impl/UserServiceImpl.java | 14 +++ .../src/main/resources/application.yml | 9 ++ .../src/main/resources/bootstrap.yml | 19 ++++ .../src/main/resources/logback-spring.xml | 58 ++++++++++ pom.xml | 9 +- 16 files changed, 474 insertions(+), 1 deletion(-) create mode 100644 han-note-search/pom.xml create mode 100644 han-note-search/src/main/java/com/hanserwei/hannote/search/HannoteSearchApplication.java create mode 100644 han-note-search/src/main/java/com/hanserwei/hannote/search/config/ElasticsearchConfig.java create mode 100644 han-note-search/src/main/java/com/hanserwei/hannote/search/enums/ResponseCodeEnum.java create mode 100644 han-note-search/src/main/java/com/hanserwei/hannote/search/exception/GlobalExceptionHandler.java create mode 100644 han-note-search/src/main/java/com/hanserwei/hannote/search/index/UserIndex.java create mode 100644 han-note-search/src/main/java/com/hanserwei/hannote/search/model/vo/SearchUserReqVO.java create mode 100644 han-note-search/src/main/java/com/hanserwei/hannote/search/model/vo/SearchUserRspVO.java create mode 100644 han-note-search/src/main/java/com/hanserwei/hannote/search/service/UserService.java create mode 100644 han-note-search/src/main/java/com/hanserwei/hannote/search/service/impl/UserServiceImpl.java create mode 100644 han-note-search/src/main/resources/application.yml create mode 100644 han-note-search/src/main/resources/bootstrap.yml create mode 100644 han-note-search/src/main/resources/logback-spring.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 0d5519e..5090716 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -27,6 +27,7 @@ + diff --git a/.idea/encodings.xml b/.idea/encodings.xml index b80d928..becad6c 100755 --- a/.idea/encodings.xml +++ b/.idea/encodings.xml @@ -37,6 +37,8 @@ + + diff --git a/han-note-search/pom.xml b/han-note-search/pom.xml new file mode 100644 index 0000000..70aec9b --- /dev/null +++ b/han-note-search/pom.xml @@ -0,0 +1,73 @@ + + 4.0.0 + + + com.hanserwei + han-note + ${revision} + + + + jar + han-note-search + ${project.artifactId} + 搜索服务 + + + + com.hanserwei + hanserwei-common + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + com.hanserwei + hanserwei-spring-boot-starter-biz-operationlog + + + + + com.hanserwei + hanserwei-spring-boot-starter-jackson + + + + org.springframework.cloud + spring-cloud-starter-bootstrap + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + co.elastic.clients + elasticsearch-java + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/han-note-search/src/main/java/com/hanserwei/hannote/search/HannoteSearchApplication.java b/han-note-search/src/main/java/com/hanserwei/hannote/search/HannoteSearchApplication.java new file mode 100644 index 0000000..e67042a --- /dev/null +++ b/han-note-search/src/main/java/com/hanserwei/hannote/search/HannoteSearchApplication.java @@ -0,0 +1,11 @@ +package com.hanserwei.hannote.search; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class HannoteSearchApplication { + public static void main(String[] args) { + SpringApplication.run(HannoteSearchApplication.class, args); + } +} diff --git a/han-note-search/src/main/java/com/hanserwei/hannote/search/config/ElasticsearchConfig.java b/han-note-search/src/main/java/com/hanserwei/hannote/search/config/ElasticsearchConfig.java new file mode 100644 index 0000000..39aed72 --- /dev/null +++ b/han-note-search/src/main/java/com/hanserwei/hannote/search/config/ElasticsearchConfig.java @@ -0,0 +1,31 @@ +package com.hanserwei.hannote.search.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ElasticsearchConfig { + + @Value("${elasticsearch.host}") + private String host; + + @Value("${elasticsearch.port}") + private int port; + + @Value("${elasticsearch.scheme:http}") + private String scheme; + + @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; + + +} diff --git a/han-note-search/src/main/java/com/hanserwei/hannote/search/enums/ResponseCodeEnum.java b/han-note-search/src/main/java/com/hanserwei/hannote/search/enums/ResponseCodeEnum.java new file mode 100644 index 0000000..2e1b65a --- /dev/null +++ b/han-note-search/src/main/java/com/hanserwei/hannote/search/enums/ResponseCodeEnum.java @@ -0,0 +1,23 @@ +package com.hanserwei.hannote.search.enums; + +import com.hanserwei.framework.common.exception.BaseExceptionInterface; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum ResponseCodeEnum implements BaseExceptionInterface { + + // ----------- 通用异常状态码 ----------- + SYSTEM_ERROR("SEARCH-10000", "出错啦,后台小哥正在努力修复中..."), + PARAM_NOT_VALID("SEARCH-10001", "参数错误"), + + // ----------- 业务异常状态码 ----------- + ; + + // 异常码 + private final String errorCode; + // 错误信息 + private final String errorMsg; + +} \ No newline at end of file diff --git a/han-note-search/src/main/java/com/hanserwei/hannote/search/exception/GlobalExceptionHandler.java b/han-note-search/src/main/java/com/hanserwei/hannote/search/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..2f2b83e --- /dev/null +++ b/han-note-search/src/main/java/com/hanserwei/hannote/search/exception/GlobalExceptionHandler.java @@ -0,0 +1,103 @@ +package com.hanserwei.hannote.search.exception; + +import com.hanserwei.framework.common.exception.ApiException; +import com.hanserwei.framework.common.response.Response; +import com.hanserwei.hannote.search.enums.ResponseCodeEnum; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.util.Optional; + +@SuppressWarnings("LoggingSimilarMessage") +@ControllerAdvice +@Slf4j +public class GlobalExceptionHandler { + + /** + * 捕获自定义业务异常 + * + * @return Response.fail(e) + */ + @ExceptionHandler({ApiException.class}) + @ResponseBody + public Response handleApiException(HttpServletRequest request, ApiException e) { + log.warn("{} request fail, errorCode: {}, errorMessage: {}", request.getRequestURI(), e.getErrorCode(), e.getErrorMsg()); + return Response.fail(e); + } + + /** + * 捕获参数校验异常 + * + * @return Response.fail(errorCode, errorMessage) + */ + @ExceptionHandler({MethodArgumentNotValidException.class}) + @ResponseBody + public Response handleMethodArgumentNotValidException(HttpServletRequest request, MethodArgumentNotValidException e) { + // 参数错误异常码 + String errorCode = ResponseCodeEnum.PARAM_NOT_VALID.getErrorCode(); + + // 获取 BindingResult + BindingResult bindingResult = e.getBindingResult(); + + StringBuilder sb = new StringBuilder(); + + // 获取校验不通过的字段,并组合错误信息,格式为: email 邮箱格式不正确, 当前值: '123124qq.com'; + Optional.of(bindingResult.getFieldErrors()).ifPresent(errors -> { + errors.forEach(error -> + sb.append(error.getField()) + .append(" ") + .append(error.getDefaultMessage()) + .append(", 当前值: '") + .append(error.getRejectedValue()) + .append("'; ") + + ); + }); + + // 错误信息 + String errorMessage = sb.toString(); + + log.warn("{} request error, errorCode: {}, errorMessage: {}", request.getRequestURI(), errorCode, errorMessage); + + return Response.fail(errorCode, errorMessage); + } + + /** + * 捕获 guava 参数校验异常 + * + * @return Response.fail(ResponseCodeEnum.PARAM_NOT_VALID) + */ + @ExceptionHandler({IllegalArgumentException.class}) + @ResponseBody + public Response handleIllegalArgumentException(HttpServletRequest request, IllegalArgumentException e) { + // 参数错误异常码 + String errorCode = ResponseCodeEnum.PARAM_NOT_VALID.getErrorCode(); + + // 错误信息 + String errorMessage = e.getMessage(); + + log.warn("{} request error, errorCode: {}, errorMessage: {}", request.getRequestURI(), errorCode, errorMessage); + + return Response.fail(errorCode, errorMessage); + } + + /** + * 其他类型异常 + * + * @param request 请求 + * @param e 异常 + * @return Response.fail(ResponseCodeEnum.SYSTEM_ERROR) + */ + @ExceptionHandler({Exception.class}) + @ResponseBody + public Response handleOtherException(HttpServletRequest request, Exception e) { + log.error("{} request error, ", request.getRequestURI(), e); + return Response.fail(ResponseCodeEnum.SYSTEM_ERROR); + } +} + diff --git a/han-note-search/src/main/java/com/hanserwei/hannote/search/index/UserIndex.java b/han-note-search/src/main/java/com/hanserwei/hannote/search/index/UserIndex.java new file mode 100644 index 0000000..e106dd1 --- /dev/null +++ b/han-note-search/src/main/java/com/hanserwei/hannote/search/index/UserIndex.java @@ -0,0 +1,40 @@ +package com.hanserwei.hannote.search.index; + +public class UserIndex { + + /** + * 索引名称 + */ + public static final String NAME = "user"; + + /** + * 用户ID + */ + public static final String FIELD_USER_ID = "id"; + + /** + * 昵称 + */ + public static final String FIELD_USER_NICKNAME = "nickname"; + + /** + * 头像 + */ + public static final String FIELD_USER_AVATAR = "avatar"; + + /** + * 小憨书ID + */ + public static final String FIELD_USER_HAN_NOTE_ID = "han_note_id"; + + /** + * 发布笔记总数 + */ + public static final String FIELD_USER_NOTE_TOTAL = "note_total"; + + /** + * 粉丝总数 + */ + public static final String FIELD_USER_FANS_TOTAL = "fans_total"; + +} \ No newline at end of file diff --git a/han-note-search/src/main/java/com/hanserwei/hannote/search/model/vo/SearchUserReqVO.java b/han-note-search/src/main/java/com/hanserwei/hannote/search/model/vo/SearchUserReqVO.java new file mode 100644 index 0000000..e6ec4cd --- /dev/null +++ b/han-note-search/src/main/java/com/hanserwei/hannote/search/model/vo/SearchUserReqVO.java @@ -0,0 +1,22 @@ +package com.hanserwei.hannote.search.model.vo; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class SearchUserReqVO { + + @NotBlank(message = "搜索关键词不能为空") + private String keyword; + + @Min(value = 1, message = "页码不能小于 1") + private Integer pageNo = 1; // 默认值为第一页 + +} \ No newline at end of file diff --git a/han-note-search/src/main/java/com/hanserwei/hannote/search/model/vo/SearchUserRspVO.java b/han-note-search/src/main/java/com/hanserwei/hannote/search/model/vo/SearchUserRspVO.java new file mode 100644 index 0000000..85965e5 --- /dev/null +++ b/han-note-search/src/main/java/com/hanserwei/hannote/search/model/vo/SearchUserRspVO.java @@ -0,0 +1,44 @@ +package com.hanserwei.hannote.search.model.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class SearchUserRspVO { + + /** + * 用户ID + */ + private Long userId; + + /** + * 昵称 + */ + private String nickname; + + /** + * 头像 + */ + private String avatar; + + /** + * 小憨书ID + */ + private String hanNoteId; + + /** + * 笔记发布总数 + */ + private Integer noteTotal; + + /** + * 粉丝总数 + */ + private Integer fansTotal; + +} \ No newline at end of file diff --git a/han-note-search/src/main/java/com/hanserwei/hannote/search/service/UserService.java b/han-note-search/src/main/java/com/hanserwei/hannote/search/service/UserService.java new file mode 100644 index 0000000..fc8daca --- /dev/null +++ b/han-note-search/src/main/java/com/hanserwei/hannote/search/service/UserService.java @@ -0,0 +1,16 @@ +package com.hanserwei.hannote.search.service; + +import com.hanserwei.framework.common.response.PageResponse; +import com.hanserwei.hannote.search.model.vo.SearchUserReqVO; +import com.hanserwei.hannote.search.model.vo.SearchUserRspVO; + +public interface UserService { + + /** + * 搜索用户 + * + * @param searchUserReqVO 搜索用户请求 + * @return 搜索用户响应 + */ + PageResponse searchUser(SearchUserReqVO searchUserReqVO); +} diff --git a/han-note-search/src/main/java/com/hanserwei/hannote/search/service/impl/UserServiceImpl.java b/han-note-search/src/main/java/com/hanserwei/hannote/search/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..4ae366f --- /dev/null +++ b/han-note-search/src/main/java/com/hanserwei/hannote/search/service/impl/UserServiceImpl.java @@ -0,0 +1,14 @@ +package com.hanserwei.hannote.search.service.impl; + +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; + +public class UserServiceImpl implements UserService { + + @Override + public PageResponse searchUser(SearchUserReqVO searchUserReqVO) { + return null; + } +} diff --git a/han-note-search/src/main/resources/application.yml b/han-note-search/src/main/resources/application.yml new file mode 100644 index 0000000..377993c --- /dev/null +++ b/han-note-search/src/main/resources/application.yml @@ -0,0 +1,9 @@ +server: + port: 8092 # 项目启动的端口 + +spring: + profiles: + active: dev # 默认激活 dev 本地开发环境 + servlet: + multipart: + max-file-size: 20MB # 单个文件最大大小 \ No newline at end of file diff --git a/han-note-search/src/main/resources/bootstrap.yml b/han-note-search/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..2dec797 --- /dev/null +++ b/han-note-search/src/main/resources/bootstrap.yml @@ -0,0 +1,19 @@ +spring: + application: + name: han-note-search # 应用名称 + profiles: + active: dev # 默认激活 dev 本地开发环境 + cloud: + nacos: + discovery: + enabled: true # 启用服务发现 + group: DEFAULT_GROUP # 所属组 + namespace: han-note # 命名空间 + server-addr: 127.0.0.1:8848 # 指定 Nacos 配置中心的服务器地址 + config: + server-addr: http://127.0.0.1:8848 # 指定 Nacos 配置中心的服务器地址 + prefix: ${spring.application.name} # 配置 Data Id 前缀,这里使用应用名称作为前缀 + group: DEFAULT_GROUP # 所属组 + namespace: han-note # 命名空间 + file-extension: yaml # 配置文件格式 + refresh-enabled: true # 是否开启动态刷新 \ No newline at end of file diff --git a/han-note-search/src/main/resources/logback-spring.xml b/han-note-search/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..dde2a37 --- /dev/null +++ b/han-note-search/src/main/resources/logback-spring.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + ${LOG_FILE}-%i.log + + 30 + + 10MB + + 0 + + false + + + ${LOG_PATTERN} + UTF-8 + + + + + + + 0 + + 256 + + + + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index d01fbb7..06f0c8f 100755 --- a/pom.xml +++ b/pom.xml @@ -23,6 +23,7 @@ han-note-user-relation han-note-count han-note-data-align + han-note-search @@ -67,6 +68,7 @@ 5.3.2 0.2.21 3.2.0 + 9.2.0 @@ -300,7 +302,12 @@ xxl-job-core ${xxl-job.version} - + + + co.elastic.clients + elasticsearch-java + ${elasticsearch-java.version} +