13 KiB
title, url, publishedTime
| title | url | publishedTime |
|---|---|---|
| 笔记内容查询接口开发 - 犬小哈专栏 | https://www.quanxiaoha.com/column/10324.html | null |
本小节中,我们为 KV 键值服务添加一个 —— 笔记内容查询接口,上游服务传入笔记 ID,即可获取内容文本。
接口定义
接口地址
POST /kv/note/content/find
入参
{
"noteId": "15382b55-b351-4d11-ac1a-860d7bc005fb" // 笔记 ID
}
出参
{
"success": true,
"message": null,
"errorCode": null,
"data": {
"noteId": "15382b55-b351-4d11-ac1a-860d7bc005fb", // 笔记 ID
"content": "笔记内容测试" // 笔记内容
}
}
新建 DTO 实体类
编辑 xiaohashu-kv-api 模块,在里面分别新建接口对应的出入参 DTO 实体类,如下图所示:
在 /dto/req 包下创建 FindNoteContentReqDTO 入参类,代码如下:
package com.quanxiaoha.xiaohashu.kv.dto.req;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author: 犬小哈
* @date: 2024/4/7 15:17
* @version: v1.0.0
* @description: 查询笔记内容
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class FindNoteContentReqDTO {
@NotBlank(message = "笔记 ID 不能为空")
private String noteId;
}
在 /dto/rsp 包下,创建接口出参实体类 FindNoteContentRspDTO, 代码如下:
package com.quanxiaoha.xiaohashu.kv.dto.rsp;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.UUID;
/**
* @author: 犬小哈
* @date: 2024/4/7 15:17
* @version: v1.0.0
* @description: 笔记内容
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class FindNoteContentRspDTO {
/**
* 笔记 ID
*/
private UUID noteId;
/**
* 笔记内容
*/
private String content;
}
异常状态码枚举
新建 /enums 枚举包,并添加一个异常状态码枚举类,代码如下:
package com.quanxiaoha.xiaohashu.kv.biz.enums;
import com.quanxiaoha.framework.common.exception.BaseExceptionInterface;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author: 犬小哈
* @url: www.quanxiaoha.com
* @date: 2023-08-15 10:33
* @description: 响应异常码
**/
@Getter
@AllArgsConstructor
public enum ResponseCodeEnum implements BaseExceptionInterface {
// ----------- 通用异常状态码 -----------
SYSTEM_ERROR("KV-10000", "出错啦,后台小哥正在努力修复中..."),
PARAM_NOT_VALID("KV-10001", "参数错误"),
// ----------- 业务异常状态码 -----------
NOTE_CONTENT_NOT_FOUND("KV-20000", "该笔记内容不存在"),
;
// 异常码
private final String errorCode;
// 错误信息
private final String errorMessage;
}
除通用异常枚举值外,还声明了一个
NOTE_CONTENT_NOT_FOUND枚举值,用于等会业务层中,若判断笔记内容不存在时使用。
全局异常捕获器
从其他服务中,复制一个全局异常捕获器过来,复制过来后,ResponseCodeEnum 的包路径会爆红,修正一下即可,其他不用动,代码如下:
package com.quanxiaoha.xiaohashu.kv.biz.exception;
import com.quanxiaoha.framework.common.exception.BizException;
import com.quanxiaoha.framework.common.response.Response;
import com.quanxiaoha.xiaohashu.kv.biz.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;
/**
* @author: 犬小哈
* @url: www.quanxiaoha.com
* @date: 2023-08-15 10:14
* @description: 全局异常处理
**/
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 捕获自定义业务异常
* @return
*/
@ExceptionHandler({ BizException.class })
@ResponseBody
public Response<Object> handleBizException(HttpServletRequest request, BizException e) {
log.warn("{} request fail, errorCode: {}, errorMessage: {}", request.getRequestURI(), e.getErrorCode(), e.getErrorMessage());
return Response.fail(e);
}
/**
* 捕获参数校验异常
* @return
*/
@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.ofNullable(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
*/
@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
*/
@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);
}
}
编写 service 业务层
回到 xiaohashu-kv-biz 模块中,编辑 NoteContentService 业务接口,声明一个查询笔记内容方法,代码如下:
package com.quanxiaoha.xiaohashu.kv.biz.service;
import com.quanxiaoha.framework.common.response.Response;
import com.quanxiaoha.xiaohashu.kv.dto.req.AddNoteContentReqDTO;
import com.quanxiaoha.xiaohashu.kv.dto.req.FindNoteContentReqDTO;
import com.quanxiaoha.xiaohashu.kv.dto.rsp.FindNoteContentRspDTO;
/**
* @author: 犬小哈
* @date: 2024/4/7 15:41
* @version: v1.0.0
* @description: 笔记内容存储业务
**/
public interface NoteContentService {
// 省略...
/**
* 查询笔记内容
*
* @param findNoteContentReqDTO
* @return
*/
Response<FindNoteContentRspDTO> findNoteContent(FindNoteContentReqDTO findNoteContentReqDTO);
}
接着,在其实现类 NoteContentServiceImpl 中,实现上述声明的方法,代码如下:
package com.quanxiaoha.xiaohashu.kv.biz.service.impl;
import com.quanxiaoha.framework.common.exception.BizException;
import com.quanxiaoha.framework.common.response.Response;
import com.quanxiaoha.xiaohashu.kv.biz.domain.dataobject.NoteContentDO;
import com.quanxiaoha.xiaohashu.kv.biz.domain.repository.NoteContentRepository;
import com.quanxiaoha.xiaohashu.kv.biz.enums.ResponseCodeEnum;
import com.quanxiaoha.xiaohashu.kv.biz.service.NoteContentService;
import com.quanxiaoha.xiaohashu.kv.dto.req.AddNoteContentReqDTO;
import com.quanxiaoha.xiaohashu.kv.dto.req.FindNoteContentReqDTO;
import com.quanxiaoha.xiaohashu.kv.dto.rsp.FindNoteContentRspDTO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Optional;
import java.util.UUID;
/**
* @author: 犬小哈
* @date: 2024/4/7 15:41
* @version: v1.0.0
* @description: Key-Value 业务
**/
@Service
@Slf4j
public class NoteContentServiceImpl implements NoteContentService {
@Resource
private NoteContentRepository noteContentRepository;
// 省略...
/**
* 查询笔记内容
*
* @param findNoteContentReqDTO
* @return
*/
@Override
public Response<FindNoteContentRspDTO> findNoteContent(FindNoteContentReqDTO findNoteContentReqDTO) {
// 笔记 ID
String noteId = findNoteContentReqDTO.getNoteId();
// 根据笔记 ID 查询笔记内容
Optional<NoteContentDO> optional = noteContentRepository.findById(UUID.fromString(noteId));
// 若笔记内容不存在
if (!optional.isPresent()) {
throw new BizException(ResponseCodeEnum.NOTE_CONTENT_NOT_FOUND);
}
NoteContentDO noteContentDO = optional.get();
// 构建返参 DTO
FindNoteContentRspDTO findNoteContentRspDTO = FindNoteContentRspDTO.builder()
.noteId(noteContentDO.getId())
.content(noteContentDO.getContent())
.build();
return Response.success(findNoteContentRspDTO);
}
}
解释一波逻辑:
- 获取入参中的笔记 ID;
- 查询 Cassandra 存储数据库,获取该条笔记对应的内容;
- 判断
Optional, 若为空,抛出笔记不存在业务异常,交给全局异常捕获器统一处理;- 否则,则构建返参
DTO返回;
添加 controller 接口
编辑 NoteContentController 控制器,添加 /kv/note/content/find 接口,代码如下:
package com.quanxiaoha.xiaohashu.kv.biz.controller;
import com.quanxiaoha.framework.common.response.Response;
import com.quanxiaoha.xiaohashu.kv.biz.service.NoteContentService;
import com.quanxiaoha.xiaohashu.kv.dto.req.AddNoteContentReqDTO;
import com.quanxiaoha.xiaohashu.kv.dto.req.FindNoteContentReqDTO;
import com.quanxiaoha.xiaohashu.kv.dto.rsp.FindNoteContentRspDTO;
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;
/**
* @author: 犬小哈
* @date: 2024/4/4 13:22
* @version: v1.0.0
* @description: 笔记内容
**/
@RestController
@RequestMapping("/kv")
@Slf4j
public class NoteContentController {
@Resource
private NoteContentService noteContentService;
// 省略...
@PostMapping(value = "/note/content/find")
public Response<FindNoteContentRspDTO> findNoteContent(@Validated @RequestBody FindNoteContentReqDTO findNoteContentReqDTO) {
return noteContentService.findNoteContent(findNoteContentReqDTO);
}
}
自测一波
接口编写完毕后,重启 KV 键值服务。调试一波接口,如下图所示,看看能否正常查询出笔记内容文本:
Tip
: 入参中的笔记 ID ,记得从 Cassandra 中获取一个表中已存在的 UUID。
如上图所示,响参成功,接口调试通过。
添加 Feign 客户端接口
最后,编辑 xiaohashu-kv-api 模块中的 KeyValueFeignApi 接口,将笔记内容查询接口提供出去,以供其他服务调用。代码如下:
package com.quanxiaoha.xiaohashu.kv.api;
import com.quanxiaoha.framework.common.response.Response;
import com.quanxiaoha.xiaohashu.kv.constant.ApiConstants;
import com.quanxiaoha.xiaohashu.kv.dto.req.AddNoteContentReqDTO;
import com.quanxiaoha.xiaohashu.kv.dto.req.FindNoteContentReqDTO;
import com.quanxiaoha.xiaohashu.kv.dto.rsp.FindNoteContentRspDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
* @author: 犬小哈
* @date: 2024/4/13 22:56
* @version: v1.0.0
* @description: K-V 键值存储 Feign 接口
**/
@FeignClient(name = ApiConstants.SERVICE_NAME)
public interface KeyValueFeignApi {
String PREFIX = "/kv";
// 省略...
@PostMapping(value = PREFIX + "/note/content/find")
Response<FindNoteContentRspDTO> findNoteContent(@RequestBody FindNoteContentReqDTO findNoteContentReqDTO);
}