!49 [update] 新增修改昵称,修改头像,查询昵称与头像接口

Merge pull request !49 from 宋天/user-register
This commit is contained in:
芋道源码 2021-10-27 01:38:31 +00:00 committed by Gitee
commit a19f92ff2c
30 changed files with 526 additions and 73 deletions

View File

@ -1,13 +1,13 @@
package cn.iocoder.yudao.adminserver.modules.infra.controller.file;
import cn.hutool.core.io.IoUtil;
import cn.iocoder.yudao.adminserver.modules.infra.service.file.InfFileService;
import cn.iocoder.yudao.adminserver.modules.infra.controller.file.vo.InfFilePageReqVO;
import cn.iocoder.yudao.coreservice.modules.infra.controller.file.vo.InfFileRespVO;
import cn.iocoder.yudao.coreservice.modules.infra.dal.dataobject.file.InfFileDO;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.adminserver.modules.infra.controller.file.vo.InfFilePageReqVO;
import cn.iocoder.yudao.adminserver.modules.infra.controller.file.vo.InfFileRespVO;
import cn.iocoder.yudao.adminserver.modules.infra.convert.file.InfFileConvert;
import cn.iocoder.yudao.adminserver.modules.infra.service.file.InfFileService;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;

View File

@ -1,8 +1,8 @@
package cn.iocoder.yudao.adminserver.modules.infra.convert.file;
import cn.iocoder.yudao.coreservice.modules.infra.controller.file.vo.InfFileRespVO;
import cn.iocoder.yudao.coreservice.modules.infra.dal.dataobject.file.InfFileDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.adminserver.modules.infra.controller.file.vo.InfFileRespVO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

View File

@ -1,19 +1,19 @@
package cn.iocoder.yudao.adminserver.modules.infra.dal.mysql.file;
import cn.iocoder.yudao.adminserver.modules.infra.controller.file.vo.InfFilePageReqVO;
import cn.iocoder.yudao.coreservice.modules.infra.dal.dataobject.file.InfFileDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
import cn.iocoder.yudao.adminserver.modules.infra.controller.file.vo.InfFilePageReqVO;
import org.apache.ibatis.annotations.Mapper;
/**
* admin 文件操作 Mapper
*
* @author 芋道源码
*/
@Mapper
public interface InfFileMapper extends BaseMapperX<InfFileDO> {
default Integer selectCountById(String id) {
return selectCount("id", id);
}
default PageResult<InfFileDO> selectPage(InfFilePageReqVO reqVO) {
return selectPage(reqVO, new QueryWrapperX<InfFileDO>()
.likeIfPresent("id", reqVO.getId())
@ -21,5 +21,4 @@ public interface InfFileMapper extends BaseMapperX<InfFileDO> {
.betweenIfPresent("create_time", reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
.orderByDesc("create_time"));
}
}

View File

@ -27,7 +27,4 @@ public interface InfErrorCodeConstants {
ErrorCode API_ERROR_LOG_NOT_FOUND = new ErrorCode(1001002000, "API 错误日志不存在");
ErrorCode API_ERROR_LOG_PROCESSED = new ErrorCode(1001002001, "API 错误日志已处理");
// ========== 文件 1001003000 ==========
ErrorCode FILE_NOT_EXISTS = new ErrorCode(1001003000, "文件不存在");
}

View File

@ -1,8 +1,8 @@
package cn.iocoder.yudao.adminserver.modules.infra.service.file;
import cn.iocoder.yudao.adminserver.modules.infra.controller.file.vo.InfFilePageReqVO;
import cn.iocoder.yudao.coreservice.modules.infra.dal.dataobject.file.InfFileDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.adminserver.modules.infra.controller.file.vo.InfFilePageReqVO;
/**
* 文件 Service 接口

View File

@ -1,20 +1,14 @@
package cn.iocoder.yudao.adminserver.modules.infra.service.file.impl;
import cn.hutool.core.io.FileTypeUtil;
import cn.iocoder.yudao.coreservice.modules.infra.dal.dataobject.file.InfFileDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.adminserver.modules.infra.framework.file.config.FileProperties;
import cn.iocoder.yudao.adminserver.modules.infra.controller.file.vo.InfFilePageReqVO;
import cn.iocoder.yudao.adminserver.modules.infra.dal.mysql.file.InfFileMapper;
import cn.iocoder.yudao.adminserver.modules.infra.service.file.InfFileService;
import cn.iocoder.yudao.adminserver.modules.infra.controller.file.vo.InfFilePageReqVO;
import cn.iocoder.yudao.coreservice.modules.infra.dal.dataobject.file.InfFileDO;
import cn.iocoder.yudao.coreservice.modules.infra.service.file.InfFileCoreService;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.adminserver.modules.infra.enums.InfErrorCodeConstants.FILE_NOT_EXISTS;
import static cn.iocoder.yudao.adminserver.modules.system.enums.SysErrorCodeConstants.FILE_PATH_EXISTS;
/**
* 文件 Service 实现类
@ -28,40 +22,21 @@ public class InfFileServiceImpl implements InfFileService {
private InfFileMapper fileMapper;
@Resource
private FileProperties fileProperties;
private InfFileCoreService fileCoreService;
@Override
public String createFile(String path, byte[] content) {
if (fileMapper.selectCountById(path) > 0) {
throw exception(FILE_PATH_EXISTS);
}
// 保存到数据库
InfFileDO file = new InfFileDO();
file.setId(path);
file.setType(FileTypeUtil.getType(new ByteArrayInputStream(content)));
file.setContent(content);
fileMapper.insert(file);
// 拼接路径返回
return fileProperties.getBasePath() + path;
return fileCoreService.createFile(path,content);
}
@Override
public void deleteFile(String id) {
// 校验存在
this.validateFileExists(id);
// 更新
fileMapper.deleteById(id);
}
private void validateFileExists(String id) {
if (fileMapper.selectById(id) == null) {
throw exception(FILE_NOT_EXISTS);
}
fileCoreService.deleteFile(id);
}
@Override
public InfFileDO getFile(String path) {
return fileMapper.selectById(path);
return fileCoreService.getFile(path);
}
@Override

View File

@ -4,7 +4,6 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.adminserver.modules.infra.service.file.InfFileService;
import cn.iocoder.yudao.adminserver.modules.system.controller.user.vo.profile.SysUserProfileUpdatePasswordReqVO;
import cn.iocoder.yudao.adminserver.modules.system.controller.user.vo.profile.SysUserProfileUpdateReqVO;
import cn.iocoder.yudao.adminserver.modules.system.controller.user.vo.user.*;
@ -16,6 +15,7 @@ import cn.iocoder.yudao.adminserver.modules.system.service.dept.SysDeptService;
import cn.iocoder.yudao.adminserver.modules.system.service.dept.SysPostService;
import cn.iocoder.yudao.adminserver.modules.system.service.permission.SysPermissionService;
import cn.iocoder.yudao.adminserver.modules.system.service.user.SysUserService;
import cn.iocoder.yudao.coreservice.modules.infra.service.file.InfFileCoreService;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
@ -59,7 +59,7 @@ public class SysUserServiceImpl implements SysUserService {
@Resource
private PasswordEncoder passwordEncoder;
@Resource
private InfFileService fileService;
private InfFileCoreService fileService;
@Override
public Long createUser(SysUserCreateReqVO reqVO) {

View File

@ -2,12 +2,12 @@ package cn.iocoder.yudao.adminserver.modules.infra.service.file;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.iocoder.yudao.adminserver.BaseDbUnitTest;
import cn.iocoder.yudao.coreservice.modules.infra.dal.dataobject.file.InfFileDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.adminserver.modules.infra.framework.file.config.FileProperties;
import cn.iocoder.yudao.adminserver.modules.infra.controller.file.vo.InfFilePageReqVO;
import cn.iocoder.yudao.adminserver.modules.infra.dal.mysql.file.InfFileMapper;
import cn.iocoder.yudao.adminserver.modules.infra.service.file.impl.InfFileServiceImpl;
import cn.iocoder.yudao.coreservice.modules.infra.dal.dataobject.file.InfFileDO;
import cn.iocoder.yudao.coreservice.modules.infra.dal.mysql.file.InfFileCoreMapper;
import cn.iocoder.yudao.coreservice.modules.infra.service.file.impl.InfFileCoreServiceImpl;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.coreservice.modules.infra.framework.file.config.FileProperties;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
@ -15,8 +15,8 @@ import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import static cn.iocoder.yudao.coreservice.modules.system.enums.SysErrorCodeConstants.FILE_NOT_EXISTS;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.adminserver.modules.infra.enums.InfErrorCodeConstants.FILE_NOT_EXISTS;
import static cn.iocoder.yudao.adminserver.modules.system.enums.SysErrorCodeConstants.FILE_PATH_EXISTS;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
@ -24,17 +24,17 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildTime;
import static org.junit.jupiter.api.Assertions.*;
@Import({InfFileServiceImpl.class, FileProperties.class})
@Import({InfFileCoreServiceImpl.class, FileProperties.class})
public class InfFileServiceTest extends BaseDbUnitTest {
@Resource
private InfFileServiceImpl fileService;
private InfFileService fileService;
@MockBean
private FileProperties fileProperties;
@Resource
private InfFileMapper fileMapper;
private InfFileCoreMapper fileMapper;
@Test
public void testCreateFile_success() {

View File

@ -3,7 +3,6 @@ package cn.iocoder.yudao.adminserver.modules.system.service.user;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil;
import cn.iocoder.yudao.adminserver.BaseDbUnitTest;
import cn.iocoder.yudao.adminserver.modules.infra.service.file.InfFileService;
import cn.iocoder.yudao.adminserver.modules.system.controller.user.vo.profile.SysUserProfileUpdatePasswordReqVO;
import cn.iocoder.yudao.adminserver.modules.system.controller.user.vo.profile.SysUserProfileUpdateReqVO;
import cn.iocoder.yudao.adminserver.modules.system.controller.user.vo.user.*;
@ -14,6 +13,7 @@ import cn.iocoder.yudao.adminserver.modules.system.service.dept.SysDeptService;
import cn.iocoder.yudao.adminserver.modules.system.service.dept.SysPostService;
import cn.iocoder.yudao.adminserver.modules.system.service.permission.SysPermissionService;
import cn.iocoder.yudao.adminserver.modules.system.service.user.impl.SysUserServiceImpl;
import cn.iocoder.yudao.coreservice.modules.infra.service.file.InfFileCoreService;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
import cn.iocoder.yudao.coreservice.modules.system.enums.common.SysSexEnum;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
@ -69,7 +69,7 @@ public class SysUserServiceImplTest extends BaseDbUnitTest {
@MockBean
private PasswordEncoder passwordEncoder;
@MockBean
private InfFileService fileService;
private InfFileCoreService fileService;
@Test
public void testCreatUser_success() {

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.adminserver.modules.infra.controller.file.vo;
package cn.iocoder.yudao.coreservice.modules.infra.controller.file.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

View File

@ -6,5 +6,7 @@ import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface InfFileCoreMapper extends BaseMapperX<InfFileDO> {
default Integer selectCountById(String id) {
return selectCount("id", id);
}
}

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.adminserver.modules.infra.framework.file.config;
package cn.iocoder.yudao.coreservice.modules.infra.framework.file.config;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.adminserver.modules.infra.framework.file.config;
package cn.iocoder.yudao.coreservice.modules.infra.framework.file.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

View File

@ -13,4 +13,4 @@
* 综合考虑暂时使用方案 3 的方式比较适合这样一个 all in one 的项目
* 随着文件的量级大了之后还是推荐采用云服务
*/
package cn.iocoder.yudao.adminserver.modules.infra.framework.file;
package cn.iocoder.yudao.coreservice.modules.infra.framework.file;

View File

@ -0,0 +1,36 @@
package cn.iocoder.yudao.coreservice.modules.infra.service.file;
import cn.iocoder.yudao.coreservice.modules.infra.dal.dataobject.file.InfFileDO;
/**
* core service 文件接口
*
* @author 宋天
*/
public interface InfFileCoreService {
/**
* 保存文件并返回文件的访问路径
*
* @param path 文件路径
* @param content 文件内容
* @return 文件路径
*/
String createFile(String path, byte[] content);
/**
* 删除文件
*
* @param id 编号
*/
void deleteFile(String id);
/**
* 获得文件
*
* @param path 文件路径
* @return 文件
*/
InfFileDO getFile(String path);
}

View File

@ -0,0 +1,64 @@
package cn.iocoder.yudao.coreservice.modules.infra.service.file.impl;
import cn.hutool.core.io.FileTypeUtil;
import cn.iocoder.yudao.coreservice.modules.infra.dal.dataobject.file.InfFileDO;
import cn.iocoder.yudao.coreservice.modules.infra.dal.mysql.file.InfFileCoreMapper;
import cn.iocoder.yudao.coreservice.modules.infra.framework.file.config.FileProperties;
import cn.iocoder.yudao.coreservice.modules.infra.service.file.InfFileCoreService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import static cn.iocoder.yudao.coreservice.modules.system.enums.SysErrorCodeConstants.*;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
/**
* core service 文件实现类
*
* @author 宋天
*/
@Service
public class InfFileCoreServiceImpl implements InfFileCoreService {
@Resource
private InfFileCoreMapper fileMapper;
@Resource
private FileProperties fileProperties;
@Override
public String createFile(String path, byte[] content) {
if (fileMapper.selectCountById(path) > 0) {
throw exception(FILE_PATH_EXISTS);
}
// 保存到数据库
InfFileDO file = new InfFileDO();
file.setId(path);
file.setType(FileTypeUtil.getType(new ByteArrayInputStream(content)));
file.setContent(content);
fileMapper.insert(file);
// 拼接路径返回
return fileProperties.getBasePath() + path;
}
@Override
public void deleteFile(String id) {
// 校验存在
this.validateFileExists(id);
// 更新
fileMapper.deleteById(id);
}
private void validateFileExists(String id) {
if (fileMapper.selectById(id) == null) {
throw exception(FILE_NOT_EXISTS);
}
}
@Override
public InfFileDO getFile(String path) {
return fileMapper.selectById(path);
}
}

View File

@ -14,4 +14,7 @@ public interface SysErrorCodeConstants {
ErrorCode SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS = new ErrorCode(1006000001, "模板参数({})缺失");
ErrorCode SMS_SEND_TEMPLATE_NOT_EXISTS = new ErrorCode(1006000000, "短信模板不存在");
// ========= 文件相关 1006001000=================
ErrorCode FILE_PATH_EXISTS = new ErrorCode(1006001000, "文件路径已存在");
ErrorCode FILE_NOT_EXISTS = new ErrorCode(1006001002, "文件不存在");
}

View File

@ -1,3 +1,11 @@
### 请求 /system/user/profile/get 接口 => 没有权限
GET {{userServerUrl}}/system/user/profile/get
Authorization: Bearer test245
### 请求 /system/user/profile/revise-nickname 接口 成功
PUT {{userServerUrl}}/system/user/profile/update-nickname?nickName=yunai222
Authorization: Bearer test245
### 请求 /system/user/profile/get-user-info 接口 成功
GET {{userServerUrl}}/system/user/profile/get-user-info?id=245
Authorization: Bearer test245

View File

@ -1,14 +1,24 @@
package cn.iocoder.yudao.userserver.modules.member.controller.user;
import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfoRespVO;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.IOException;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.userserver.modules.member.enums.MbrErrorCodeConstants.FILE_IS_EMPTY;
@Api(tags = "用户个人中心")
@RestController
@ -17,11 +27,33 @@ import org.springframework.web.bind.annotation.RestController;
@Slf4j
public class SysUserProfileController {
@GetMapping("/get")
@ApiOperation("获得登录用户信息")
@Resource
private MbrUserService userService;
@PutMapping("/update-nickname")
@ApiOperation("修改用户昵称")
@PreAuthenticated
public CommonResult<Boolean> profile() {
return null;
public CommonResult<Boolean> updateNickname(@RequestParam("nickName") String nickName) {
userService.updateNickname(getLoginUserId(), nickName);
return success(true);
}
@PutMapping("/update-avatar")
@ApiOperation("修改用户头像")
@PreAuthenticated
public CommonResult<String> updateAvatar(@RequestParam("avatarFile") MultipartFile file) throws IOException {
if (file.isEmpty()) {
throw ServiceExceptionUtil.exception(FILE_IS_EMPTY);
}
String avatar = userService.updateAvatar(getLoginUserId(), file.getInputStream());
return success(avatar);
}
@GetMapping("/get-user-info")
@ApiOperation("获取用户头像与昵称")
@PreAuthenticated
public CommonResult<MbrUserInfoRespVO> getUserInfo() {
return success(userService.getUserInfo(getLoginUserId()));
}
}

View File

@ -0,0 +1,20 @@
package cn.iocoder.yudao.userserver.modules.member.controller.user.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@ApiModel("用户个人信息 Response VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MbrUserInfoRespVO {
@ApiModelProperty(value = "用户昵称", required = true, example = "芋艿")
private String nickName;
@ApiModelProperty(value = "用户头像", required = true, example = "/infra/file/get/35a12e57-4297-4faa-bf7d-7ed2f211c952")
private String avatar;
}

View File

@ -9,4 +9,9 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
*/
public interface MbrErrorCodeConstants {
// ==========用户相关 1004001000============
ErrorCode USER_NOT_EXISTS = new ErrorCode(1004001000, "用户不存在");
// ==========文件相关 1004002000 ===========
ErrorCode FILE_IS_EMPTY = new ErrorCode(1004002000, "文件为空");
}

View File

@ -1,8 +1,11 @@
package cn.iocoder.yudao.userserver.modules.member.service.user;
import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfoRespVO;
import cn.iocoder.yudao.framework.common.validation.Mobile;
import java.io.InputStream;
/**
* 前台用户 Service 接口
*
@ -44,4 +47,26 @@ public interface MbrUserService {
*/
MbrUserDO getUser(Long id);
/**
* 修改用户昵称
* @param userId 用户id
* @param nickName 用户新昵称
*/
void updateNickname(Long userId, String nickName);
/**
* 修改用户头像
* @param userId 用户id
* @param inputStream 头像文件
* @return 头像url
*/
String updateAvatar(Long userId, InputStream inputStream);
/**
* 根据用户id获取用户头像与昵称
* @param userId 用户id
* @return 用户响应实体类
*/
MbrUserInfoRespVO getUserInfo(Long userId);
}

View File

@ -1,18 +1,26 @@
package cn.iocoder.yudao.userserver.modules.member.service.user.impl;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import cn.iocoder.yudao.coreservice.modules.infra.service.file.InfFileCoreService;
import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfoRespVO;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper;
import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.io.InputStream;
import java.util.Date;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.userserver.modules.member.enums.MbrErrorCodeConstants.USER_NOT_EXISTS;
/**
* User Service 实现类
*
@ -26,6 +34,9 @@ public class MbrUserServiceImpl implements MbrUserService {
@Resource
private MbrUserMapper userMapper;
@Resource
private InfFileCoreService fileCoreService;
@Resource
private PasswordEncoder passwordEncoder;
@ -68,4 +79,52 @@ public class MbrUserServiceImpl implements MbrUserService {
return userMapper.selectById(id);
}
@Override
public void updateNickname(Long userId, String nickName) {
MbrUserDO user = this.checkUserExists(userId);
// 仅当新昵称不等于旧昵称时进行修改
if (nickName.equals(user.getNickname())){
return;
}
MbrUserDO userDO = new MbrUserDO();
userDO.setId(user.getId());
userDO.setNickname(nickName);
userMapper.updateById(userDO);
}
@Override
public String updateAvatar(Long userId, InputStream avatarFile) {
this.checkUserExists(userId);
// 创建文件
String avatar = fileCoreService.createFile(IdUtil.fastUUID(), IoUtil.readBytes(avatarFile));
// 更新头像路径
MbrUserDO userDO = MbrUserDO.builder()
.id(userId)
.avatar(avatar)
.build();
userMapper.updateById(userDO);
return avatar;
}
@Override
public MbrUserInfoRespVO getUserInfo(Long userId) {
MbrUserDO user = this.checkUserExists(userId);
MbrUserInfoRespVO userResp = new MbrUserInfoRespVO();
userResp.setNickName(user.getNickname());
userResp.setAvatar(user.getAvatar());
return userResp;
}
@VisibleForTesting
public MbrUserDO checkUserExists(Long id) {
if (id == null) {
return null;
}
MbrUserDO user = userMapper.selectById(id);
if (user == null) {
throw exception(USER_NOT_EXISTS);
}else{
return user;
}
}
}

View File

@ -0,0 +1,39 @@
package cn.iocoder.yudao.userserver;
import cn.iocoder.yudao.framework.datasource.config.YudaoDataSourceAutoConfiguration;
import cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.jdbc.Sql;
/**
* 依赖内存 DB 的单元测试
*
* 注意Service 层同样适用对于 Service 层的单元测试我们针对自己模块的 Mapper 走的是 H2 内存数据库针对别的模块的 Service 走的是 Mock 方法
*
* @author 芋道源码
*/
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseDbUnitTest.Application.class)
@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件
@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) // 每个单元测试结束后清理 DB
public class BaseDbUnitTest {
@Import({
// DB 配置类
YudaoDataSourceAutoConfiguration.class, // 自己的 DB 配置类
DataSourceAutoConfiguration.class, // Spring DB 自动配置类
DataSourceTransactionManagerAutoConfiguration.class, // Spring 事务自动配置类
DruidDataSourceAutoConfigure.class, // Druid 自动配置类
// MyBatis 配置类
YudaoMybatisAutoConfiguration.class, // 自己的 MyBatis 配置类
MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类
})
public static class Application {
}
}

View File

@ -0,0 +1,107 @@
package cn.iocoder.yudao.userserver.modules.member.service;
import cn.iocoder.yudao.coreservice.modules.infra.service.file.InfFileCoreService;
import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO;
import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import cn.iocoder.yudao.userserver.BaseDbUnitTest;
import cn.iocoder.yudao.userserver.modules.member.controller.user.vo.MbrUserInfoRespVO;
import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper;
import cn.iocoder.yudao.userserver.modules.member.service.user.impl.MbrUserServiceImpl;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.Assert;
import javax.annotation.Resource;
import java.io.*;
import java.util.function.Consumer;
import static cn.hutool.core.util.RandomUtil.randomBytes;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
import static org.mockito.Mockito.*;
/**
* {@link MbrUserServiceImpl} 的单元测试类
*
* @author 宋天
*/
@Import(MbrUserServiceImpl.class)
public class MbrUserServiceImplTest extends BaseDbUnitTest {
@Resource
private MbrUserServiceImpl mbrUserService;
@Resource
private MbrUserMapper userMapper;
@MockBean
private InfFileCoreService fileCoreService;
@MockBean
private PasswordEncoder passwordEncoder;
@Test
public void testUpdateNickName_success(){
// mock 数据
MbrUserDO userDO = randomMbrUserDO();
userMapper.insert(userDO);
// 随机昵称
String newNickName = randomString();
// 调用接口修改昵称
mbrUserService.updateNickname(userDO.getId(),newNickName);
// 查询新修改后的昵称
String nickname = mbrUserService.getUser(userDO.getId()).getNickname();
// 断言
assertEquals(newNickName,nickname);
}
@Test
public void testGetUserInfo_success(){
// mock 数据
MbrUserDO userDO = randomMbrUserDO();
userMapper.insert(userDO);
// 查询用户昵称与头像
MbrUserInfoRespVO userInfo = mbrUserService.getUserInfo(userDO.getId());
System.out.println(userInfo);
}
@Test
public void testUpdateAvatar_success(){
// mock 数据
MbrUserDO dbUser = randomMbrUserDO();
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
byte[] avatarFileBytes = randomBytes(10);
ByteArrayInputStream avatarFile = new ByteArrayInputStream(avatarFileBytes);
// mock 方法
String avatar = randomString();
when(fileCoreService.createFile(anyString(), eq(avatarFileBytes))).thenReturn(avatar);
// 调用
String str = mbrUserService.updateAvatar(userId, avatarFile);
// 断言
assertEquals(avatar, str);
}
// ========== 随机对象 ==========
@SafeVarargs
private static MbrUserDO randomMbrUserDO(Consumer<MbrUserDO>... consumers) {
Consumer<MbrUserDO> consumer = (o) -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
};
return randomPojo(MbrUserDO.class, ArrayUtils.append(consumer, consumers));
}
}

View File

@ -0,0 +1,44 @@
spring:
main:
lazy-initialization: true # 开启懒加载,加快速度
banner-mode: off # 单元测试,禁用 Banner
--- #################### 数据库相关配置 ####################
spring:
# 数据源配置项
datasource:
name: ruoyi-vue-pro
url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false; # MODE 使用 MySQL 模式DATABASE_TO_UPPER 配置表和字段使用小写
driver-class-name: org.h2.Driver
username: sa
password:
schema: classpath:sql/create_tables.sql # MySQL 转 H2 的语句,使用 https://www.jooq.org/translate/ 工具
druid:
async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度
initial-size: 1 # 单元测试,配置为 1提升启动速度
# Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优
redis:
host: 127.0.0.1 # 地址
port: 16379 # 端口(单元测试,使用 16379 端口)
database: 0 # 数据库索引
mybatis:
lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试
--- #################### 定时任务相关配置 ####################
--- #################### 配置中心相关配置 ####################
--- #################### 服务保障相关配置 ####################
# Lock4j 配置项(单元测试,禁用 Lock4j
# Resilience4j 配置项
--- #################### 监控相关配置 ####################
--- #################### 芋道相关配置 ####################
# 芋道配置项,设置当前项目所有自定义的配置

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -0,0 +1,4 @@
<configuration>
<!-- 引用 Spring Boot 的 logback 基础配置 -->
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
</configuration>

View File

@ -0,0 +1,2 @@
-- mbr 开头的 DB
DELETE FROM "mbr_user";

View File

@ -0,0 +1,32 @@
-- mbr 开头的 DB
CREATE TABLE IF NOT EXISTS "mbr_user" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '编号',
"nickname" varchar(30) NOT NULL DEFAULT '' COMMENT '用户昵称',
"avatar" varchar(255) NOT NULL DEFAULT '' COMMENT '头像',
"status" tinyint NOT NULL COMMENT '状态',
"mobile" varchar(11) NOT NULL COMMENT '手机号',
"password" varchar(100) NOT NULL DEFAULT '' COMMENT '密码',
"register_ip" varchar(32) NOT NULL COMMENT '注册 IP',
"login_ip" varchar(50) NULL DEFAULT '' COMMENT '最后登录IP',
"login_date" datetime NULL DEFAULT NULL COMMENT '最后登录时间',
"creator" varchar(64) NULL DEFAULT '' COMMENT '创建者',
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
"updater" varchar(64) NULL DEFAULT '' COMMENT '更新者',
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
"deleted" bit(1) NOT NULL DEFAULT '0' COMMENT '是否删除',
PRIMARY KEY ("id")
) COMMENT '会员表';
-- inf 开头的 DB
CREATE TABLE IF NOT EXISTS "inf_file" (
"id" varchar(188) NOT NULL,
"type" varchar(63) DEFAULT NULL,
"content" blob NOT NULL,
"creator" varchar(64) DEFAULT '',
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) DEFAULT '',
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '文件表';