feat(search): 初始化搜索服务模块- 添加搜索服务基础配置文件 application.yml 和 bootstrap.yml
- 配置 Nacos 服务发现与配置中心集成 - 引入 Elasticsearch 客户端依赖并配置连接参数 - 创建全局异常处理器 GlobalExceptionHandler - 定义响应码枚举 ResponseCodeEnum - 添加用户搜索请求/响应 VO 类 - 定义用户索引常量 UserIndex - 创建 UserService 接口及实现类
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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<Object> 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<Object> 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<Object> 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<Object> handleOtherException(HttpServletRequest request, Exception e) {
|
||||
log.error("{} request error, ", request.getRequestURI(), e);
|
||||
return Response.fail(ResponseCodeEnum.SYSTEM_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
}
|
||||
@@ -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; // 默认值为第一页
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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<SearchUserRspVO> searchUser(SearchUserReqVO searchUserReqVO);
|
||||
}
|
||||
@@ -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<SearchUserRspVO> searchUser(SearchUserReqVO searchUserReqVO) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
9
han-note-search/src/main/resources/application.yml
Normal file
9
han-note-search/src/main/resources/application.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
server:
|
||||
port: 8092 # 项目启动的端口
|
||||
|
||||
spring:
|
||||
profiles:
|
||||
active: dev # 默认激活 dev 本地开发环境
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 20MB # 单个文件最大大小
|
||||
19
han-note-search/src/main/resources/bootstrap.yml
Normal file
19
han-note-search/src/main/resources/bootstrap.yml
Normal file
@@ -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 # 是否开启动态刷新
|
||||
58
han-note-search/src/main/resources/logback-spring.xml
Normal file
58
han-note-search/src/main/resources/logback-spring.xml
Normal file
@@ -0,0 +1,58 @@
|
||||
<configuration>
|
||||
<!-- 引用 Spring Boot 的 logback 基础配置 -->
|
||||
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
|
||||
|
||||
<!-- 应用名称 -->
|
||||
<property scope="context" name="appName" value="search"/>
|
||||
<!-- 自定义日志输出路径,以及日志名称前缀 -->
|
||||
<property name="LOG_FILE" value="./logs/${appName}.%d{yyyy-MM-dd}"/>
|
||||
<!-- 每行日志输出的格式 -->
|
||||
<property name="LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/>
|
||||
|
||||
<!-- 文件输出 -->
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<!-- 日志文件的命名格式 -->
|
||||
<fileNamePattern>${LOG_FILE}-%i.log</fileNamePattern>
|
||||
<!-- 保留 30 天的日志文件 -->
|
||||
<maxHistory>30</maxHistory>
|
||||
<!-- 单个日志文件最大大小 -->
|
||||
<maxFileSize>10MB</maxFileSize>
|
||||
<!-- 日志文件的总大小,0 表示不限制 -->
|
||||
<totalSizeCap>0</totalSizeCap>
|
||||
<!-- 重启服务时,是否清除历史日志,不推荐清理 -->
|
||||
<cleanHistoryOnStart>false</cleanHistoryOnStart>
|
||||
</rollingPolicy>
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<pattern>${LOG_PATTERN}</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 异步写入日志,提升性能 -->
|
||||
<appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
|
||||
<!-- 是否丢弃日志, 0 表示不丢弃。默认情况下,如果队列满 80%, 会丢弃 TRACE、DEBUG、INFO 级别的日志 -->
|
||||
<discardingThreshold>0</discardingThreshold>
|
||||
<!-- 队列大小。默认值为 256 -->
|
||||
<queueSize>256</queueSize>
|
||||
<appender-ref ref="FILE"/>
|
||||
</appender>
|
||||
|
||||
<!-- 本地 dev 开发环境 -->
|
||||
<springProfile name="dev">
|
||||
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE"/> <!-- 输出控制台日志 -->
|
||||
<appender-ref ref="ASYNC_FILE"/> <!-- 打印日志到文件中。PS: 本地环境下,如果不想打印日志到文件,可注释掉此行 -->
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
<!-- 其它环境 -->
|
||||
<springProfile name="prod">
|
||||
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
|
||||
<root level="INFO">
|
||||
<appender-ref ref="ASYNC_FILE"/> <!-- 生产环境下,仅打印日志到文件中 -->
|
||||
</root>
|
||||
</springProfile>
|
||||
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user