Merge branch 'master' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/user-social

 Conflicts:
	yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/enums/SysErrorCodeConstants.java
This commit is contained in:
YunaiV 2021-10-28 08:30:24 +08:00
commit 30ad7c43b8
39 changed files with 646 additions and 185 deletions

View File

@ -20,7 +20,7 @@
<url>https://github.com/YunaiV/ruoyi-vue-pro</url> <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<properties> <properties>
<revision>1.1.0-snapshot</revision> <revision>1.2.0-snapshot</revision>
<!-- Maven 相关 --> <!-- Maven 相关 -->
<java.version>1.8</java.version> <java.version>1.8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source> <maven.compiler.source>${java.version}</maven.compiler.source>

View File

@ -1,13 +1,14 @@
package cn.iocoder.yudao.adminserver.modules.infra.controller.file; package cn.iocoder.yudao.adminserver.modules.infra.controller.file;
import cn.hutool.core.io.IoUtil; 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.coreservice.modules.infra.dal.dataobject.file.InfFileDO;
import cn.iocoder.yudao.coreservice.modules.infra.service.file.InfFileCoreService;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult; 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.convert.file.InfFileConvert;
import cn.iocoder.yudao.adminserver.modules.infra.service.file.InfFileService;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParam;
@ -36,6 +37,8 @@ public class InfFileController {
@Resource @Resource
private InfFileService fileService; private InfFileService fileService;
@Resource
private InfFileCoreService fileCoreService;
@PostMapping("/upload") @PostMapping("/upload")
@ApiOperation("上传文件") @ApiOperation("上传文件")
@ -45,7 +48,7 @@ public class InfFileController {
}) })
public CommonResult<String> uploadFile(@RequestParam("file") MultipartFile file, public CommonResult<String> uploadFile(@RequestParam("file") MultipartFile file,
@RequestParam("path") String path) throws IOException { @RequestParam("path") String path) throws IOException {
return success(fileService.createFile(path, IoUtil.readBytes(file.getInputStream()))); return success(fileCoreService.createFile(path, IoUtil.readBytes(file.getInputStream())));
} }
@DeleteMapping("/delete") @DeleteMapping("/delete")
@ -53,7 +56,7 @@ public class InfFileController {
@ApiImplicitParam(name = "id", value = "编号", required = true) @ApiImplicitParam(name = "id", value = "编号", required = true)
@PreAuthorize("@ss.hasPermission('infra:file:delete')") @PreAuthorize("@ss.hasPermission('infra:file:delete')")
public CommonResult<Boolean> deleteFile(@RequestParam("id") String id) { public CommonResult<Boolean> deleteFile(@RequestParam("id") String id) {
fileService.deleteFile(id); fileCoreService.deleteFile(id);
return success(true); return success(true);
} }
@ -61,7 +64,7 @@ public class InfFileController {
@ApiOperation("下载文件") @ApiOperation("下载文件")
@ApiImplicitParam(name = "path", value = "文件附件", required = true, dataTypeClass = MultipartFile.class) @ApiImplicitParam(name = "path", value = "文件附件", required = true, dataTypeClass = MultipartFile.class)
public void getFile(HttpServletResponse response, @PathVariable("path") String path) throws IOException { public void getFile(HttpServletResponse response, @PathVariable("path") String path) throws IOException {
InfFileDO file = fileService.getFile(path); InfFileDO file = fileCoreService.getFile(path);
if (file == null) { if (file == null) {
log.warn("[getFile][path({}) 文件不存在]", path); log.warn("[getFile][path({}) 文件不存在]", path);
response.setStatus(HttpStatus.NOT_FOUND.value()); response.setStatus(HttpStatus.NOT_FOUND.value());

View File

@ -1,8 +1,8 @@
package cn.iocoder.yudao.adminserver.modules.infra.convert.file; 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.coreservice.modules.infra.dal.dataobject.file.InfFileDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult; 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.Mapper;
import org.mapstruct.factory.Mappers; import org.mapstruct.factory.Mappers;

View File

@ -1,19 +1,19 @@
package cn.iocoder.yudao.adminserver.modules.infra.dal.mysql.file; 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.coreservice.modules.infra.dal.dataobject.file.InfFileDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; 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; import org.apache.ibatis.annotations.Mapper;
/**
* admin 文件操作 Mapper
*
* @author 芋道源码
*/
@Mapper @Mapper
public interface InfFileMapper extends BaseMapperX<InfFileDO> { public interface InfFileMapper extends BaseMapperX<InfFileDO> {
default Integer selectCountById(String id) {
return selectCount("id", id);
}
default PageResult<InfFileDO> selectPage(InfFilePageReqVO reqVO) { default PageResult<InfFileDO> selectPage(InfFilePageReqVO reqVO) {
return selectPage(reqVO, new QueryWrapperX<InfFileDO>() return selectPage(reqVO, new QueryWrapperX<InfFileDO>()
.likeIfPresent("id", reqVO.getId()) .likeIfPresent("id", reqVO.getId())
@ -21,5 +21,4 @@ public interface InfFileMapper extends BaseMapperX<InfFileDO> {
.betweenIfPresent("create_time", reqVO.getBeginCreateTime(), reqVO.getEndCreateTime()) .betweenIfPresent("create_time", reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
.orderByDesc("create_time")); .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_NOT_FOUND = new ErrorCode(1001002000, "API 错误日志不存在");
ErrorCode API_ERROR_LOG_PROCESSED = new ErrorCode(1001002001, "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; 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.coreservice.modules.infra.dal.dataobject.file.InfFileDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.adminserver.modules.infra.controller.file.vo.InfFilePageReqVO;
/** /**
* 文件 Service 接口 * 文件 Service 接口
@ -11,30 +11,6 @@ import cn.iocoder.yudao.adminserver.modules.infra.controller.file.vo.InfFilePage
*/ */
public interface InfFileService { public interface InfFileService {
/**
* 保存文件并返回文件的访问路径
*
* @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

@ -1,20 +1,14 @@
package cn.iocoder.yudao.adminserver.modules.infra.service.file.impl; 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.dal.mysql.file.InfFileMapper;
import cn.iocoder.yudao.adminserver.modules.infra.service.file.InfFileService; 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 org.springframework.stereotype.Service;
import javax.annotation.Resource; 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 实现类 * 文件 Service 实现类
@ -27,43 +21,6 @@ public class InfFileServiceImpl implements InfFileService {
@Resource @Resource
private InfFileMapper fileMapper; private InfFileMapper 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);
}
@Override @Override
public PageResult<InfFileDO> getFilePage(InfFilePageReqVO pageReqVO) { public PageResult<InfFileDO> getFilePage(InfFilePageReqVO pageReqVO) {
return fileMapper.selectPage(pageReqVO); return fileMapper.selectPage(pageReqVO);

View File

@ -1,7 +1,6 @@
package cn.iocoder.yudao.adminserver.modules.system.enums; package cn.iocoder.yudao.adminserver.modules.system.enums;
import cn.iocoder.yudao.framework.common.exception.ErrorCode; import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import javafx.beans.binding.MapExpression;
/** /**
* System 错误码枚举类 * System 错误码枚举类

View File

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

View File

@ -1,13 +1,12 @@
package cn.iocoder.yudao.adminserver.modules.infra.service.file; 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.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.controller.file.vo.InfFilePageReqVO;
import cn.iocoder.yudao.adminserver.modules.infra.dal.mysql.file.InfFileMapper; import cn.iocoder.yudao.coreservice.modules.infra.dal.dataobject.file.InfFileDO;
import cn.iocoder.yudao.adminserver.modules.infra.service.file.impl.InfFileServiceImpl; 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.impl.InfFileCoreServiceImpl;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.MockBean;
@ -15,79 +14,22 @@ import org.springframework.context.annotation.Import;
import javax.annotation.Resource; import javax.annotation.Resource;
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;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildTime;
import static org.junit.jupiter.api.Assertions.*; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static org.junit.jupiter.api.Assertions.assertEquals;
@Import({InfFileServiceImpl.class, FileProperties.class}) @Import({InfFileCoreServiceImpl.class, FileProperties.class})
public class InfFileServiceTest extends BaseDbUnitTest { public class InfFileServiceTest extends BaseDbUnitTest {
@Resource @Resource
private InfFileServiceImpl fileService; private InfFileService fileService;
@MockBean @MockBean
private FileProperties fileProperties; private FileProperties fileProperties;
@Resource @Resource
private InfFileMapper fileMapper; private InfFileCoreMapper fileMapper;
@Test
public void testCreateFile_success() {
// 准备参数
String path = randomString();
byte[] content = ResourceUtil.readBytes("file/erweima.jpg");
// 调用
String url = fileService.createFile(path, content);
// 断言
assertEquals(fileProperties.getBasePath() + path, url);
// 校验数据
InfFileDO file = fileMapper.selectById(path);
assertEquals(path, file.getId());
assertEquals("jpg", file.getType());
assertArrayEquals(content, file.getContent());
}
@Test
public void testCreateFile_exists() {
// mock 数据
InfFileDO dbFile = randomPojo(InfFileDO.class);
fileMapper.insert(dbFile);
// 准备参数
String path = dbFile.getId(); // 模拟已存在
byte[] content = ResourceUtil.readBytes("file/erweima.jpg");
// 调用并断言异常
assertServiceException(() -> fileService.createFile(path, content), FILE_PATH_EXISTS);
}
@Test
public void testDeleteFile_success() {
// mock 数据
InfFileDO dbFile = randomPojo(InfFileDO.class);
fileMapper.insert(dbFile);// @Sql: 先插入出一条存在的数据
// 准备参数
String id = dbFile.getId();
// 调用
fileService.deleteFile(id);
// 校验数据不存在了
assertNull(fileMapper.selectById(id));
}
@Test
public void testDeleteFile_notExists() {
// 准备参数
String id = randomString();
// 调用, 并断言异常
assertServiceException(() -> fileService.deleteFile(id), FILE_NOT_EXISTS);
}
@Test @Test
public void testGetFilePage() { public void testGetFilePage() {

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.collection.CollUtil;
import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.RandomUtil;
import cn.iocoder.yudao.adminserver.BaseDbUnitTest; 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.SysUserProfileUpdatePasswordReqVO;
import cn.iocoder.yudao.adminserver.modules.system.controller.user.vo.profile.SysUserProfileUpdateReqVO; import cn.iocoder.yudao.adminserver.modules.system.controller.user.vo.profile.SysUserProfileUpdateReqVO;
import cn.iocoder.yudao.adminserver.modules.system.controller.user.vo.user.*; 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.dept.SysPostService;
import cn.iocoder.yudao.adminserver.modules.system.service.permission.SysPermissionService; 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.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.dal.dataobject.user.SysUserDO;
import cn.iocoder.yudao.coreservice.modules.system.enums.common.SysSexEnum; import cn.iocoder.yudao.coreservice.modules.system.enums.common.SysSexEnum;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
@ -69,7 +69,7 @@ public class SysUserServiceImplTest extends BaseDbUnitTest {
@MockBean @MockBean
private PasswordEncoder passwordEncoder; private PasswordEncoder passwordEncoder;
@MockBean @MockBean
private InfFileService fileService; private InfFileCoreService fileService;
@Test @Test
public void testCreatUser_success() { public void testCreatUser_success() {

View File

@ -3,9 +3,9 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent> <parent>
<artifactId>yudao</artifactId>
<groupId>cn.iocoder.boot</groupId> <groupId>cn.iocoder.boot</groupId>
<version>1.1.0-snapshot</version> <artifactId>yudao</artifactId>
<version>${revision}</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

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.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;

View File

@ -6,5 +6,7 @@ import org.apache.ibatis.annotations.Mapper;
@Mapper @Mapper
public interface InfFileCoreMapper extends BaseMapperX<InfFileDO> { 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.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration; 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 lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;

View File

@ -13,4 +13,4 @@
* 综合考虑暂时使用方案 3 的方式比较适合这样一个 all in one 的项目 * 综合考虑暂时使用方案 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,9 +14,12 @@ public interface SysErrorCodeConstants {
ErrorCode SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS = new ErrorCode(1006000001, "模板参数({})缺失"); ErrorCode SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS = new ErrorCode(1006000001, "模板参数({})缺失");
ErrorCode SMS_SEND_TEMPLATE_NOT_EXISTS = new ErrorCode(1006000000, "短信模板不存在"); ErrorCode SMS_SEND_TEMPLATE_NOT_EXISTS = new ErrorCode(1006000000, "短信模板不存在");
// ========= 文件相关 1006001000=================
ErrorCode FILE_PATH_EXISTS = new ErrorCode(1006001000, "文件路径已存在");
ErrorCode FILE_NOT_EXISTS = new ErrorCode(1006001002, "文件不存在");
// ========== 社交模块 1006001000 ========== // ========== 社交模块 1006002000 ==========
ErrorCode SOCIAL_AUTH_FAILURE = new ErrorCode(1006001000, "社交授权失败,原因是:{}"); ErrorCode SOCIAL_AUTH_FAILURE = new ErrorCode(1006002000, "社交授权失败,原因是:{}");
ErrorCode SOCIAL_UNBIND_NOT_SELF = new ErrorCode(1006001001, "社交解绑失败,非当前用户绑定"); ErrorCode SOCIAL_UNBIND_NOT_SELF = new ErrorCode(1006002001, "社交解绑失败,非当前用户绑定");
} }

View File

@ -0,0 +1,87 @@
package cn.iocoder.yudao.coreservice.modules.infra.service.file;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.iocoder.yudao.coreservice.BaseDbUnitTest;
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.impl.InfFileCoreServiceImpl;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
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.coreservice.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;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
import static org.junit.jupiter.api.Assertions.*;
@Import({InfFileCoreServiceImpl.class, FileProperties.class})
public class InfFileCoreServiceTest extends BaseDbUnitTest {
@Resource
private InfFileCoreService fileCoreService;
@MockBean
private FileProperties fileProperties;
@Resource
private InfFileCoreMapper fileMapper;
@Test
public void testCreateFile_success() {
// 准备参数
String path = randomString();
byte[] content = ResourceUtil.readBytes("file/erweima.jpg");
// 调用
String url = fileCoreService.createFile(path, content);
// 断言
assertEquals(fileProperties.getBasePath() + path, url);
// 校验数据
InfFileDO file = fileMapper.selectById(path);
assertEquals(path, file.getId());
assertEquals("jpg", file.getType());
assertArrayEquals(content, file.getContent());
}
@Test
public void testCreateFile_exists() {
// mock 数据
InfFileDO dbFile = randomPojo(InfFileDO.class);
fileMapper.insert(dbFile);
// 准备参数
String path = dbFile.getId(); // 模拟已存在
byte[] content = ResourceUtil.readBytes("file/erweima.jpg");
// 调用并断言异常
assertServiceException(() -> fileCoreService.createFile(path, content), FILE_PATH_EXISTS);
}
@Test
public void testDeleteFile_success() {
// mock 数据
InfFileDO dbFile = randomPojo(InfFileDO.class);
fileMapper.insert(dbFile);// @Sql: 先插入出一条存在的数据
// 准备参数
String id = dbFile.getId();
// 调用
fileCoreService.deleteFile(id);
// 校验数据不存在了
assertNull(fileMapper.selectById(id));
}
@Test
public void testDeleteFile_notExists() {
// 准备参数
String id = randomString();
// 调用, 并断言异常
assertServiceException(() -> fileCoreService.deleteFile(id), FILE_NOT_EXISTS);
}
}

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -1,5 +1,6 @@
-- inf 开头的 DB -- inf 开头的 DB
DELETE FROM "inf_api_access_log"; DELETE FROM "inf_api_access_log";
DELETE FROM "inf_file";
DELETE FROM "inf_api_error_log"; DELETE FROM "inf_api_error_log";
-- sys 开头的 DB -- sys 开头的 DB

View File

@ -1,5 +1,17 @@
-- inf 开头的 DB -- 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 '文件表';
-- sys 开头的 DB -- sys 开头的 DB
CREATE TABLE IF NOT EXISTS `sys_user_session` ( CREATE TABLE IF NOT EXISTS `sys_user_session` (

View File

@ -14,7 +14,7 @@
<url>https://github.com/YunaiV/ruoyi-vue-pro</url> <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<properties> <properties>
<revision>1.1.0-snapshot</revision> <revision>1.2.0-snapshot</revision>
<!-- 统一依赖管理 --> <!-- 统一依赖管理 -->
<spring.boot.version>2.4.5</spring.boot.version> <spring.boot.version>2.4.5</spring.boot.version>
<!-- Web 相关 --> <!-- Web 相关 -->

View File

@ -1,3 +1,11 @@
### 请求 /system/user/profile/get 接口 => 没有权限 ### 请求 /system/user/profile/get 接口 => 没有权限
GET {{userServerUrl}}/system/user/profile/get GET {{userServerUrl}}/system/user/profile/get
Authorization: Bearer test245 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; 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.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; 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.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.bind.annotation.RestController;
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 = "用户个人中心") @Api(tags = "用户个人中心")
@RestController @RestController
@ -17,11 +27,34 @@ import org.springframework.web.bind.annotation.RestController;
@Slf4j @Slf4j
public class SysUserProfileController { public class SysUserProfileController {
@GetMapping("/get") @Resource
@ApiOperation("获得登录用户信息") private MbrUserService userService;
@PutMapping("/update-nickname")
@ApiOperation("修改用户昵称")
@PreAuthenticated @PreAuthenticated
public CommonResult<Boolean> profile() { public CommonResult<Boolean> updateNickname(@RequestParam("nickName") String nickName) {
return null; 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 { 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; package cn.iocoder.yudao.userserver.modules.member.service.user;
import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO; 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 cn.iocoder.yudao.framework.common.validation.Mobile;
import java.io.InputStream;
/** /**
* 前台用户 Service 接口 * 前台用户 Service 接口
* *
@ -44,4 +47,26 @@ public interface MbrUserService {
*/ */
MbrUserDO getUser(Long id); 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; package cn.iocoder.yudao.userserver.modules.member.service.user.impl;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil; 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.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.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper; import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper;
import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService; import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.validation.Valid; import javax.validation.Valid;
import java.io.InputStream;
import java.util.Date; 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 实现类 * User Service 实现类
* *
@ -26,6 +34,9 @@ public class MbrUserServiceImpl implements MbrUserService {
@Resource @Resource
private MbrUserMapper userMapper; private MbrUserMapper userMapper;
@Resource
private InfFileCoreService fileCoreService;
@Resource @Resource
private PasswordEncoder passwordEncoder; private PasswordEncoder passwordEncoder;
@ -68,4 +79,53 @@ public class MbrUserServiceImpl implements MbrUserService {
return userMapper.selectById(id); 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 '文件表';

View File

@ -3,16 +3,20 @@
* 邮件 * 邮件
* 钉钉、飞书等通知 * 钉钉、飞书等通知
## [v1.2.0] 待定 ## [v1.3.0] 待定
* 工作流 * 工作流
## [v1.1.1] 待定 ## [v1.2.0] 进行中
* 新增用户前台的昵称、头像的修改
TODO
* 支付 * 支付
* 用户前台的社交登陆 * 用户前台的社交登陆
## [v1.1.0] 进行中 ## [v1.1.0] 2021.10.25
* 新增管理后台的企业微信、钉钉等社交登录 * 新增管理后台的企业微信、钉钉等社交登录
* 新增用户前台(例如说,用户使用的小程序)的后端项目 `yudao-user-server` * 新增用户前台(例如说,用户使用的小程序)的后端项目 `yudao-user-server`