13 KiB
13 KiB
title, url, publishedTime
| title | url | publishedTime |
|---|---|---|
| 代码重构:手机号查询用户信息接口开发 - 犬小哈专栏 | https://www.quanxiaoha.com/column/10316.html | null |
上小节 中,我们已经将自动注册用户的代码,重构到了用户服务中,以接口的形式提供出来。本小节中,将为用户服务添加一个新的接口——手机号查询用户信息,如下图所示,并将账号密码登录中标注的部分,重构成通过 Feign 来查询用户信息。
接口定义
手机号查询用户信息接口定义如下:
接口地址
POST /user/findByPhone
入参
{
"phone": "18011119108", // 手机号
}
出参
{
"success": true,
"message": null,
"errorCode": null,
"data": {
"id": 1, // 用户 ID
"password": "xxx" // 密码
}
}
创建出入参 DTO
编辑 xiaohashu-user-api 模块,添加接口对应的出入参 DTO 实体类:
入参
package com.quanxiaoha.xiaohashu.user.dto.req;
import com.quanxiaoha.framework.common.validator.PhoneNumber;
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 FindUserByPhoneReqDTO {
/**
* 手机号
*/
@NotBlank(message = "手机号不能为空")
@PhoneNumber
private String phone;
}
出参
package com.quanxiaoha.xiaohashu.user.dto.resp;
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 FindUserByPhoneRspDTO {
private Long id;
private String password;
}
添加全局枚举
编辑 ResponseCodeEnum 异常码枚举类,添加一个该用户不存在的枚举值,等会业务层判空需要用到:
package com.quanxiaoha.xiaohashu.user.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 {
// 省略...
USER_NOT_FOUND("USER-20007", "该用户不存在"),
;
// 异常码
private final String errorCode;
// 错误信息
private final String errorMessage;
}
编写 service 业务层
接着,编辑 xiaohashu-user-biz 模块中的 UserService 接口,声明一个根据手机号查询用户信息的方法,代码如下:
package com.quanxiaoha.xiaohashu.user.biz.service;
import com.quanxiaoha.framework.common.response.Response;
import com.quanxiaoha.xiaohashu.user.biz.model.vo.UpdateUserInfoReqVO;
import com.quanxiaoha.xiaohashu.user.dto.req.FindUserByPhoneReqDTO;
import com.quanxiaoha.xiaohashu.user.dto.req.RegisterUserReqDTO;
import com.quanxiaoha.xiaohashu.user.dto.resp.FindUserByPhoneRspDTO;
/**
* @author: 犬小哈
* @date: 2024/4/7 15:41
* @version: v1.0.0
* @description: 用户业务
**/
public interface UserService {
// 省略...
/**
* 根据手机号查询用户信息
*
* @param findUserByPhoneReqDTO
* @return
*/
Response<FindUserByPhoneRspDTO> findByPhone(FindUserByPhoneReqDTO findUserByPhoneReqDTO);
}
接着,在接口的实现类中实现上述方法,代码如下:
package com.quanxiaoha.xiaohashu.user.biz.service.impl;
import com.google.common.base.Preconditions;
import com.quanxiaoha.framework.biz.context.holder.LoginUserContextHolder;
import com.quanxiaoha.framework.common.enums.DeletedEnum;
import com.quanxiaoha.framework.common.enums.StatusEnum;
import com.quanxiaoha.framework.common.exception.BizException;
import com.quanxiaoha.framework.common.response.Response;
import com.quanxiaoha.framework.common.util.JsonUtils;
import com.quanxiaoha.framework.common.util.ParamUtils;
import com.quanxiaoha.xiaohashu.oss.api.FileFeignApi;
import com.quanxiaoha.xiaohashu.user.biz.constant.RedisKeyConstants;
import com.quanxiaoha.xiaohashu.user.biz.constant.RoleConstants;
import com.quanxiaoha.xiaohashu.user.biz.domain.dataobject.RoleDO;
import com.quanxiaoha.xiaohashu.user.biz.domain.dataobject.UserDO;
import com.quanxiaoha.xiaohashu.user.biz.domain.dataobject.UserRoleDO;
import com.quanxiaoha.xiaohashu.user.biz.domain.mapper.RoleDOMapper;
import com.quanxiaoha.xiaohashu.user.biz.domain.mapper.UserDOMapper;
import com.quanxiaoha.xiaohashu.user.biz.domain.mapper.UserRoleDOMapper;
import com.quanxiaoha.xiaohashu.user.biz.enums.ResponseCodeEnum;
import com.quanxiaoha.xiaohashu.user.biz.enums.SexEnum;
import com.quanxiaoha.xiaohashu.user.biz.model.vo.UpdateUserInfoReqVO;
import com.quanxiaoha.xiaohashu.user.biz.rpc.OssRpcService;
import com.quanxiaoha.xiaohashu.user.biz.service.UserService;
import com.quanxiaoha.xiaohashu.user.dto.req.FindUserByPhoneReqDTO;
import com.quanxiaoha.xiaohashu.user.dto.req.RegisterUserReqDTO;
import com.quanxiaoha.xiaohashu.user.dto.resp.FindUserByPhoneRspDTO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* @author: 犬小哈
* @date: 2024/4/7 15:41
* @version: v1.0.0
* @description: 用户业务
**/
@Service
@Slf4j
public class UserServiceImpl implements UserService {
// 省略...
/**
* 根据手机号查询用户信息
*
* @param findUserByPhoneReqDTO
* @return
*/
@Override
public Response<FindUserByPhoneRspDTO> findByPhone(FindUserByPhoneReqDTO findUserByPhoneReqDTO) {
String phone = findUserByPhoneReqDTO.getPhone();
// 根据手机号查询用户信息
UserDO userDO = userDOMapper.selectByPhone(phone);
// 判空
if (Objects.isNull(userDO)) {
throw new BizException(ResponseCodeEnum.USER_NOT_FOUND);
}
// 构建返参
FindUserByPhoneRspDTO findUserByPhoneRspDTO = FindUserByPhoneRspDTO.builder()
.id(userDO.getId())
.password(userDO.getPassword())
.build();
return Response.success(findUserByPhoneRspDTO);
}
}
编写 controller 层
然后,编辑 UserController 控制器,添加 /user/findByPhone 手机号查询用户信息接口,代码如下:
package com.quanxiaoha.xiaohashu.user.biz.controller;
import com.quanxiaoha.framework.biz.operationlog.aspect.ApiOperationLog;
import com.quanxiaoha.framework.common.response.Response;
import com.quanxiaoha.xiaohashu.user.biz.model.vo.UpdateUserInfoReqVO;
import com.quanxiaoha.xiaohashu.user.biz.service.UserService;
import com.quanxiaoha.xiaohashu.user.dto.req.FindUserByPhoneReqDTO;
import com.quanxiaoha.xiaohashu.user.dto.req.RegisterUserReqDTO;
import com.quanxiaoha.xiaohashu.user.dto.resp.FindUserByPhoneRspDTO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
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("/user")
@Slf4j
public class UserController {
@Resource
private UserService userService;
// 省略...
// ===================================== 对其他服务提供的接口 =====================================
// 省略...
@PostMapping("/findByPhone")
@ApiOperationLog(description = "手机号查询用户信息")
public Response<FindUserByPhoneRspDTO> findByPhone(@Validated @RequestBody FindUserByPhoneReqDTO findUserByPhoneReqDTO) {
return userService.findByPhone(findUserByPhoneReqDTO);
}
}
封装 Feign 客户端接口
接口创建完成后,编辑 xiaohashu-user-api 模块,封装 Feign 客户端接口,以便其他服务直接使用:
package com.quanxiaoha.xiaohashu.user.api;
import com.quanxiaoha.framework.common.response.Response;
import com.quanxiaoha.xiaohashu.user.constant.ApiConstants;
import com.quanxiaoha.xiaohashu.user.dto.req.FindUserByPhoneReqDTO;
import com.quanxiaoha.xiaohashu.user.dto.req.RegisterUserReqDTO;
import com.quanxiaoha.xiaohashu.user.dto.resp.FindUserByPhoneRspDTO;
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: TODO
**/
@FeignClient(name = ApiConstants.SERVICE_NAME)
public interface UserFeignApi {
String PREFIX = "/user";
// 省略...
/**
* 根据手机号查询用户信息
*
* @param findUserByPhoneReqDTO
* @return
*/
@PostMapping(value = PREFIX + "/findByPhone")
Response<FindUserByPhoneRspDTO> findByPhone(@RequestBody FindUserByPhoneReqDTO findUserByPhoneReqDTO);
}
封装 rpc 调用
回到 xiaohashu-auth 认证服务中,编辑 UserRpcService ,将根据手机号查询用户信息封装成一个方法,代码如下:
package com.quanxiaoha.xiaohashu.auth.rpc;
import com.quanxiaoha.framework.common.response.Response;
import com.quanxiaoha.xiaohashu.user.api.UserFeignApi;
import com.quanxiaoha.xiaohashu.user.dto.req.FindUserByPhoneReqDTO;
import com.quanxiaoha.xiaohashu.user.dto.req.RegisterUserReqDTO;
import com.quanxiaoha.xiaohashu.user.dto.resp.FindUserByPhoneRspDTO;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Component;
/**
* @author: 犬小哈
* @date: 2024/4/13 23:29
* @version: v1.0.0
* @description: 用户服务
**/
@Component
public class UserRpcService {
@Resource
private UserFeignApi userFeignApi;
// 省略...
/**
* 根据手机号查询用户信息
*
* @param phone
* @return
*/
public FindUserByPhoneRspDTO findUserByPhone(String phone) {
FindUserByPhoneReqDTO findUserByPhoneReqDTO = new FindUserByPhoneReqDTO();
findUserByPhoneReqDTO.setPhone(phone);
Response<FindUserByPhoneRspDTO> response = userFeignApi.findByPhone(findUserByPhoneReqDTO);
if (!response.isSuccess()) {
return null;
}
return response.getData();
}
}
重构 service 层
封装完成后,编辑认证服务的 UserServiceImpl 类,将之前的代码重构成通过 RPC 调用,来查询用户信息,如下图标注所示:
代码如下:
// 省略...
case PASSWORD: // 密码登录
String password = userLoginReqVO.getPassword();
// RPC: 调用用户服务,通过手机号查询用户
FindUserByPhoneRspDTO findUserByPhoneRspDTO = userRpcService.findUserByPhone(phone);
// 判断该手机号是否注册
if (Objects.isNull(findUserByPhoneRspDTO)) {
throw new BizException(ResponseCodeEnum.USER_NOT_FOUND);
}
// 拿到密文密码
String encodePassword = findUserByPhoneRspDTO.getPassword();
// 匹配密码是否一致
boolean isPasswordCorrect = passwordEncoder.matches(password, encodePassword);
// 如果不正确,则抛出业务异常,提示用户名或者密码不正确
if (!isPasswordCorrect) {
throw new BizException(ResponseCodeEnum.PHONE_OR_PASSWORD_ERROR);
}
userId = findUserByPhoneRspDTO.getId();
break;
// 省略...
自测一波
至此,通过手机号查询用户信息功能就重构完成了,最后,别忘了重新自测一波账号/密码登录接口,保证代码修改过后,功能也是正常的。