feat(relation): 实现用户关注功能
- 新增关注用户接口,支持通过用户ID关注其他用户 - 添加参数校验,确保被关注用户ID不为空 - 实现关注用户时的业务逻辑,包括: -不能关注自己 - 校验被关注用户是否存 - 集成Feign客户端,调用用户服务查询用户信息 - 定义关注相关的异常码和错误信息 - 更新网关配置,路由/relation/**请求到用户关系服务- 添加HTTP客户端测试用例,用于验证关注功能 - 引入用户API依赖,支持远程调用用户服务
This commit is contained in:
@@ -22,6 +22,12 @@ spring:
|
|||||||
- Path=/note/**
|
- Path=/note/**
|
||||||
filters:
|
filters:
|
||||||
- StripPrefix=1
|
- StripPrefix=1
|
||||||
|
- id: user-relation
|
||||||
|
uri: lb://han-note-user-relation
|
||||||
|
predicates:
|
||||||
|
- Path=/relation/**
|
||||||
|
filters:
|
||||||
|
- StripPrefix=1
|
||||||
data:
|
data:
|
||||||
redis:
|
redis:
|
||||||
database: 5 # Redis 数据库索引(默认为 0)
|
database: 5 # Redis 数据库索引(默认为 0)
|
||||||
|
|||||||
@@ -91,6 +91,12 @@
|
|||||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.hanserwei</groupId>
|
||||||
|
<artifactId>han-note-user-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ package com.hanserwei.hannote.user.relation.biz;
|
|||||||
import org.mybatis.spring.annotation.MapperScan;
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@MapperScan("com.hanserwei.hannote.user.relation.biz.domain.mapper")
|
@MapperScan("com.hanserwei.hannote.user.relation.biz.domain.mapper")
|
||||||
|
@EnableFeignClients(basePackages = "com.hanserwei.hannote")
|
||||||
public class HannoteUserRelationBizApplication {
|
public class HannoteUserRelationBizApplication {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(HannoteUserRelationBizApplication.class, args);
|
SpringApplication.run(HannoteUserRelationBizApplication.class, args);
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.hanserwei.hannote.user.relation.biz.controller;
|
||||||
|
|
||||||
|
import com.hanserwei.framework.biz.operationlog.aspect.ApiOperationLog;
|
||||||
|
import com.hanserwei.framework.common.response.Response;
|
||||||
|
import com.hanserwei.hannote.user.relation.biz.model.vo.FollowUserReqVO;
|
||||||
|
import com.hanserwei.hannote.user.relation.biz.service.RelationService;
|
||||||
|
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("/relation")
|
||||||
|
@Slf4j
|
||||||
|
public class RelationController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private RelationService relationService;
|
||||||
|
|
||||||
|
@PostMapping("/follow")
|
||||||
|
@ApiOperationLog(description = "关注用户")
|
||||||
|
public Response<?> follow(@Validated @RequestBody FollowUserReqVO followUserReqVO) {
|
||||||
|
return relationService.follow(followUserReqVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -13,6 +13,8 @@ public enum ResponseCodeEnum implements BaseExceptionInterface {
|
|||||||
PARAM_NOT_VALID("RELATION-10001", "参数错误"),
|
PARAM_NOT_VALID("RELATION-10001", "参数错误"),
|
||||||
|
|
||||||
// ----------- 业务异常状态码 -----------
|
// ----------- 业务异常状态码 -----------
|
||||||
|
CANT_FOLLOW_YOUR_SELF("RELATION-20001", "无法关注自己"),
|
||||||
|
FOLLOW_USER_NOT_EXISTED("RELATION-20002", "关注的用户不存在"),
|
||||||
;
|
;
|
||||||
|
|
||||||
// 异常码
|
// 异常码
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.hanserwei.hannote.user.relation.biz.model.vo;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Builder
|
||||||
|
public class FollowUserReqVO {
|
||||||
|
|
||||||
|
@NotNull(message = "被关注用户 ID 不能为空")
|
||||||
|
private Long followUserId;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.hanserwei.hannote.user.relation.biz.rpc;
|
||||||
|
|
||||||
|
import com.hanserwei.framework.common.response.Response;
|
||||||
|
import com.hanserwei.hannote.user.api.UserFeignApi;
|
||||||
|
import com.hanserwei.hannote.user.dto.req.FindUserByIdReqDTO;
|
||||||
|
import com.hanserwei.hannote.user.dto.resp.FindUserByIdRspDTO;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class UserRpcService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UserFeignApi userFeignApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据用户 ID 查询
|
||||||
|
*
|
||||||
|
* @param userId 用户 ID
|
||||||
|
* @return 用户信息
|
||||||
|
*/
|
||||||
|
public FindUserByIdRspDTO findById(Long userId) {
|
||||||
|
FindUserByIdReqDTO findUserByIdReqDTO = new FindUserByIdReqDTO();
|
||||||
|
findUserByIdReqDTO.setId(userId);
|
||||||
|
|
||||||
|
Response<FindUserByIdRspDTO> response = userFeignApi.findById(findUserByIdReqDTO);
|
||||||
|
|
||||||
|
if (!response.isSuccess() || Objects.isNull(response.getData())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.hanserwei.hannote.user.relation.biz.service;
|
||||||
|
|
||||||
|
import com.hanserwei.framework.common.response.Response;
|
||||||
|
import com.hanserwei.hannote.user.relation.biz.model.vo.FollowUserReqVO;
|
||||||
|
|
||||||
|
public interface RelationService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关注用户
|
||||||
|
*
|
||||||
|
* @param followUserReqVO 关注用户请求
|
||||||
|
* @return 响应
|
||||||
|
*/
|
||||||
|
Response<?> follow(FollowUserReqVO followUserReqVO);
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package com.hanserwei.hannote.user.relation.biz.service.impl;
|
||||||
|
|
||||||
|
import com.hanserwei.framework.biz.context.holder.LoginUserContextHolder;
|
||||||
|
import com.hanserwei.framework.common.exception.ApiException;
|
||||||
|
import com.hanserwei.framework.common.response.Response;
|
||||||
|
import com.hanserwei.hannote.user.dto.resp.FindUserByIdRspDTO;
|
||||||
|
import com.hanserwei.hannote.user.relation.biz.enums.ResponseCodeEnum;
|
||||||
|
import com.hanserwei.hannote.user.relation.biz.model.vo.FollowUserReqVO;
|
||||||
|
import com.hanserwei.hannote.user.relation.biz.rpc.UserRpcService;
|
||||||
|
import com.hanserwei.hannote.user.relation.biz.service.RelationService;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class RelationServiceImpl implements RelationService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UserRpcService userRpcService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response<?> follow(FollowUserReqVO followUserReqVO) {
|
||||||
|
// 获取被关注用户 ID
|
||||||
|
Long followUserId = followUserReqVO.getFollowUserId();
|
||||||
|
|
||||||
|
// 获取当前登录用户 ID
|
||||||
|
Long userId = LoginUserContextHolder.getUserId();
|
||||||
|
if (Objects.equals(userId, followUserId)) {
|
||||||
|
throw new ApiException(ResponseCodeEnum.CANT_FOLLOW_YOUR_SELF);
|
||||||
|
}
|
||||||
|
// 校验关注的用户是否存在
|
||||||
|
FindUserByIdRspDTO findUserByIdRspDTO = userRpcService.findById(followUserId);
|
||||||
|
if (Objects.isNull(findUserByIdRspDTO)){
|
||||||
|
throw new ApiException(ResponseCodeEnum.FOLLOW_USER_NOT_EXISTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: 校验关注数是否已经达到上限
|
||||||
|
|
||||||
|
// TODO: 写入 Redis ZSET 关注列表
|
||||||
|
|
||||||
|
// TODO: 发送 MQ
|
||||||
|
|
||||||
|
return Response.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -118,4 +118,22 @@ Authorization: Bearer {{token}}
|
|||||||
"title": "笔记修改测试",
|
"title": "笔记修改测试",
|
||||||
"content": "我把图文笔记的内容修改了",
|
"content": "我把图文笔记的内容修改了",
|
||||||
"topicId": 1
|
"topicId": 1
|
||||||
|
}
|
||||||
|
|
||||||
|
### 关注自己
|
||||||
|
POST http://localhost:8000/relation/relation/follow
|
||||||
|
Content-Type: application/json
|
||||||
|
Authorization: Bearer {{token}}
|
||||||
|
|
||||||
|
{
|
||||||
|
"followUserId": {{userId}}
|
||||||
|
}
|
||||||
|
|
||||||
|
### 关注不存在的用户
|
||||||
|
POST http://localhost:8000/relation/relation/follow
|
||||||
|
Content-Type: application/json
|
||||||
|
Authorization: Bearer {{token}}
|
||||||
|
|
||||||
|
{
|
||||||
|
"followUserId": -1
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"dev": {
|
"dev": {
|
||||||
"token": "4bXpiBbjXEDFE4ZpqjCOHu1rP81qepl2ROOygrxRGb61K536ckLuyAwfyQHSMcyRdUzf8CxntLEMfbU2ynbYx9nJKlx4vpWZrHqv2mI4iMhnShQ4mPBi7OPPgZi22O2f",
|
"token": "4bXpiBbjXEDFE4ZpqjCOHu1rP81qepl2ROOygrxRGb61K536ckLuyAwfyQHSMcyRdUzf8CxntLEMfbU2ynbYx9nJKlx4vpWZrHqv2mI4iMhnShQ4mPBi7OPPgZi22O2f",
|
||||||
"noteId": "1977249693272375330"
|
"noteId": "1977249693272375330",
|
||||||
|
"userId": "100"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user