-
+
+
+
+ 导出 HTML
+ 导出 Word
+ 导出 Markdown
+
+
+
+
+
+
+
diff --git a/src/main/java/cn/iocoder/dashboard/framework/logger/apilog/core/service/ApiAccessLogFrameworkService.java b/src/main/java/cn/iocoder/dashboard/framework/logger/apilog/core/service/ApiAccessLogFrameworkService.java
index eda202ac3..ecafe5559 100644
--- a/src/main/java/cn/iocoder/dashboard/framework/logger/apilog/core/service/ApiAccessLogFrameworkService.java
+++ b/src/main/java/cn/iocoder/dashboard/framework/logger/apilog/core/service/ApiAccessLogFrameworkService.java
@@ -3,6 +3,7 @@ package cn.iocoder.dashboard.framework.logger.apilog.core.service;
import cn.iocoder.dashboard.framework.logger.apilog.core.service.dto.ApiAccessLogCreateDTO;
import javax.validation.Valid;
+import java.util.concurrent.Future;
/**
* API 访问日志 Framework Service 接口
@@ -15,7 +16,8 @@ public interface ApiAccessLogFrameworkService {
* 创建 API 访问日志
*
* @param createDTO 创建信息
+ * @return 是否创建成功
*/
- void createApiAccessLogAsync(@Valid ApiAccessLogCreateDTO createDTO);
+ Future
createApiAccessLogAsync(@Valid ApiAccessLogCreateDTO createDTO);
}
diff --git a/src/main/java/cn/iocoder/dashboard/framework/logger/apilog/core/service/ApiErrorLogFrameworkService.java b/src/main/java/cn/iocoder/dashboard/framework/logger/apilog/core/service/ApiErrorLogFrameworkService.java
index 032ef40e1..763db3a12 100644
--- a/src/main/java/cn/iocoder/dashboard/framework/logger/apilog/core/service/ApiErrorLogFrameworkService.java
+++ b/src/main/java/cn/iocoder/dashboard/framework/logger/apilog/core/service/ApiErrorLogFrameworkService.java
@@ -3,6 +3,7 @@ package cn.iocoder.dashboard.framework.logger.apilog.core.service;
import cn.iocoder.dashboard.framework.logger.apilog.core.service.dto.ApiErrorLogCreateDTO;
import javax.validation.Valid;
+import java.util.concurrent.Future;
/**
* API 错误日志 Framework Service 接口
@@ -15,7 +16,8 @@ public interface ApiErrorLogFrameworkService {
* 创建 API 错误日志
*
* @param createDTO 创建信息
+ * @return 是否创建成功
*/
- void createApiErrorLogAsync(@Valid ApiErrorLogCreateDTO createDTO);
+ Future createApiErrorLogAsync(@Valid ApiErrorLogCreateDTO createDTO);
}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/infra/controller/doc/InfDbDocController.java b/src/main/java/cn/iocoder/dashboard/modules/infra/controller/doc/InfDbDocController.java
index 0735c4d54..6d21d772c 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/infra/controller/doc/InfDbDocController.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/infra/controller/doc/InfDbDocController.java
@@ -1,6 +1,8 @@
package cn.iocoder.dashboard.modules.infra.controller.doc;
-import cn.hutool.extra.servlet.ServletUtil;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.util.IdUtil;
+import cn.iocoder.dashboard.util.servlet.ServletUtils;
import cn.smallbun.screw.core.Configuration;
import cn.smallbun.screw.core.engine.EngineConfig;
import cn.smallbun.screw.core.engine.EngineFileType;
@@ -10,18 +12,18 @@ import cn.smallbun.screw.core.process.ProcessConfig;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiOperation;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
-import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
-import javax.sql.DataSource;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
+import java.io.IOException;
import java.util.Collections;
@Api(tags = "数据库文档")
@@ -34,36 +36,79 @@ public class InfDbDocController {
private static final String FILE_OUTPUT_DIR = System.getProperty("java.io.tmpdir") + File.separator
+ "db-doc";
- private static final EngineFileType FILE_OUTPUT_TYPE = EngineFileType.HTML; // 可以设置 Word 或者 Markdown 格式
private static final String DOC_FILE_NAME = "数据库文档";
private static final String DOC_VERSION = "1.0.0";
private static final String DOC_DESCRIPTION = "文档描述";
- @Resource
- private DataSource dataSource;
-
@GetMapping("/export-html")
- public synchronized void exportHtml(HttpServletResponse response) throws FileNotFoundException {
+ @ApiOperation("导出 html 格式的数据文档")
+ @ApiImplicitParam(name = "deleteFile", value = "是否删除在服务器本地生成的数据库文档", example = "true", dataTypeClass = Boolean.class)
+ public void exportHtml(@RequestParam(defaultValue = "true") Boolean deleteFile,
+ HttpServletResponse response) throws IOException {
+ doExportFile(EngineFileType.HTML, deleteFile, response);
+ }
+
+ @GetMapping("/export-word")
+ @ApiOperation("导出 word 格式的数据文档")
+ @ApiImplicitParam(name = "deleteFile", value = "是否删除在服务器本地生成的数据库文档", example = "true", dataTypeClass = Boolean.class)
+ public void exportWord(@RequestParam(defaultValue = "true") Boolean deleteFile,
+ HttpServletResponse response) throws IOException {
+ doExportFile(EngineFileType.WORD, deleteFile, response);
+ }
+
+ @GetMapping("/export-markdown")
+ @ApiOperation("导出 markdown 格式的数据文档")
+ @ApiImplicitParam(name = "deleteFile", value = "是否删除在服务器本地生成的数据库文档", example = "true", dataTypeClass = Boolean.class)
+ public void exportMarkdown(@RequestParam(defaultValue = "true") Boolean deleteFile,
+ HttpServletResponse response) throws IOException {
+ doExportFile(EngineFileType.MD, deleteFile, response);
+ }
+
+ private void doExportFile(EngineFileType fileOutputType, Boolean deleteFile,
+ HttpServletResponse response) throws IOException {
+ String docFileName = DOC_FILE_NAME + "_" + IdUtil.fastSimpleUUID();
+ String filePath = doExportFile(fileOutputType, docFileName);
+ String downloadFileName = DOC_FILE_NAME + fileOutputType.getFileSuffix(); //下载后的文件名
+ try {
+ // 读取,返回
+ ServletUtils.writeAttachment(response, downloadFileName, FileUtil.readBytes(filePath));
+ } finally {
+ handleDeleteFile(deleteFile, filePath);
+ }
+ }
+
+ /**
+ * 输出文件,返回文件路径
+ *
+ * @param fileOutputType 文件类型
+ * @param fileName 文件名, 无需 ".docx" 等文件后缀
+ * @return 生成的文件所在路径
+ */
+ private String doExportFile(EngineFileType fileOutputType, String fileName) {
try (HikariDataSource dataSource = buildDataSource()) {
// 创建 screw 的配置
Configuration config = Configuration.builder()
.version(DOC_VERSION) // 版本
.description(DOC_DESCRIPTION) // 描述
.dataSource(dataSource) // 数据源
- .engineConfig(buildEngineConfig()) // 引擎配置
+ .engineConfig(buildEngineConfig(fileOutputType, fileName)) // 引擎配置
.produceConfig(buildProcessConfig()) // 处理配置
.build();
// 执行 screw,生成数据库文档
new DocumentationExecute(config).execute();
- // 读取,返回
- ServletUtil.write(response,
- new FileInputStream(FILE_OUTPUT_DIR + File.separator + DOC_FILE_NAME + FILE_OUTPUT_TYPE.getFileSuffix()),
- MediaType.TEXT_HTML_VALUE);
+ return FILE_OUTPUT_DIR + File.separator + fileName + fileOutputType.getFileSuffix();
}
}
+ private void handleDeleteFile(Boolean deleteFile, String filePath) {
+ if (!deleteFile) {
+ return;
+ }
+ FileUtil.del(filePath);
+ }
+
/**
* 创建数据源
*/
@@ -71,7 +116,6 @@ public class InfDbDocController {
private HikariDataSource buildDataSource() {
// 创建 HikariConfig 配置类
HikariConfig hikariConfig = new HikariConfig();
-// hikariConfig.setDriverClassName("com.mysql.cj.jdbc.Driver");
hikariConfig.setJdbcUrl(dataSourceProperties.getUrl());
hikariConfig.setUsername(dataSourceProperties.getUsername());
hikariConfig.setPassword(dataSourceProperties.getPassword());
@@ -83,13 +127,13 @@ public class InfDbDocController {
/**
* 创建 screw 的引擎配置
*/
- private static EngineConfig buildEngineConfig() {
+ private static EngineConfig buildEngineConfig(EngineFileType fileOutputType, String docFileName) {
return EngineConfig.builder()
.fileOutputDir(FILE_OUTPUT_DIR) // 生成文件路径
.openOutputDir(false) // 打开目录
- .fileType(FILE_OUTPUT_TYPE) // 文件类型
+ .fileType(fileOutputType) // 文件类型
.produceType(EngineTemplateType.freemarker) // 文件类型
- .fileName(DOC_FILE_NAME) // 自定义文件名称
+ .fileName(docFileName) // 自定义文件名称
.build();
}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/infra/controller/logger/vo/apierrorlog/InfApiErrorLogExportReqVO.java b/src/main/java/cn/iocoder/dashboard/modules/infra/controller/logger/vo/apierrorlog/InfApiErrorLogExportReqVO.java
index a5bc820c2..991987d0e 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/infra/controller/logger/vo/apierrorlog/InfApiErrorLogExportReqVO.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/infra/controller/logger/vo/apierrorlog/InfApiErrorLogExportReqVO.java
@@ -14,7 +14,7 @@ import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOU
public class InfApiErrorLogExportReqVO {
@ApiModelProperty(value = "用户编号", example = "666")
- private Integer userId;
+ private Long userId;
@ApiModelProperty(value = "用户类型", example = "1")
private Integer userType;
diff --git a/src/main/java/cn/iocoder/dashboard/modules/infra/controller/logger/vo/apierrorlog/InfApiErrorLogPageReqVO.java b/src/main/java/cn/iocoder/dashboard/modules/infra/controller/logger/vo/apierrorlog/InfApiErrorLogPageReqVO.java
index 26f32411b..c966ab068 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/infra/controller/logger/vo/apierrorlog/InfApiErrorLogPageReqVO.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/infra/controller/logger/vo/apierrorlog/InfApiErrorLogPageReqVO.java
@@ -19,7 +19,7 @@ import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOU
public class InfApiErrorLogPageReqVO extends PageParam {
@ApiModelProperty(value = "用户编号", example = "666")
- private Integer userId;
+ private Long userId;
@ApiModelProperty(value = "用户类型", example = "1")
private Integer userType;
diff --git a/src/main/java/cn/iocoder/dashboard/modules/infra/dal/dataobject/logger/InfApiAccessLogDO.java b/src/main/java/cn/iocoder/dashboard/modules/infra/dal/dataobject/logger/InfApiAccessLogDO.java
index 2a4d2cf19..ff32cea16 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/infra/dal/dataobject/logger/InfApiAccessLogDO.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/infra/dal/dataobject/logger/InfApiAccessLogDO.java
@@ -37,7 +37,7 @@ public class InfApiAccessLogDO extends BaseDO {
/**
* 用户编号
*/
- private Integer userId;
+ private Long userId;
/**
* 用户类型
*
diff --git a/src/main/java/cn/iocoder/dashboard/modules/infra/dal/dataobject/logger/InfApiErrorLogDO.java b/src/main/java/cn/iocoder/dashboard/modules/infra/dal/dataobject/logger/InfApiErrorLogDO.java
index 855c70315..dbe326cb7 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/infra/dal/dataobject/logger/InfApiErrorLogDO.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/infra/dal/dataobject/logger/InfApiErrorLogDO.java
@@ -30,7 +30,7 @@ public class InfApiErrorLogDO extends BaseDO {
/**
* 用户编号
*/
- private Integer userId;
+ private Long userId;
/**
* 链路追踪编号
*
@@ -148,6 +148,6 @@ public class InfApiErrorLogDO extends BaseDO {
*
* 关联 {@link SysUserDO#getId()}
*/
- private Integer processUserId;
+ private Long processUserId;
}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/infra/service/job/impl/InfJobServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/infra/service/job/impl/InfJobServiceImpl.java
index eab60051c..3cdbbf1c9 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/infra/service/job/impl/InfJobServiceImpl.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/infra/service/job/impl/InfJobServiceImpl.java
@@ -43,7 +43,7 @@ public class InfJobServiceImpl implements InfJobService {
private SchedulerManager schedulerManager;
@Override
- @Transactional
+ @Transactional(rollbackFor = Exception.class)
public Long createJob(InfJobCreateReqVO createReqVO) throws SchedulerException {
validateCronExpression(createReqVO.getCronExpression());
// 校验唯一性
@@ -68,7 +68,7 @@ public class InfJobServiceImpl implements InfJobService {
}
@Override
- @Transactional
+ @Transactional(rollbackFor = Exception.class)
public void updateJob(InfJobUpdateReqVO updateReqVO) throws SchedulerException {
validateCronExpression(updateReqVO.getCronExpression());
// 校验存在
@@ -88,7 +88,7 @@ public class InfJobServiceImpl implements InfJobService {
}
@Override
- @Transactional
+ @Transactional(rollbackFor = Exception.class)
public void updateJobStatus(Long id, Integer status) throws SchedulerException {
// 校验 status
if (!containsAny(status, InfJobStatusEnum.NORMAL.getStatus(), InfJobStatusEnum.STOP.getStatus())) {
@@ -122,7 +122,7 @@ public class InfJobServiceImpl implements InfJobService {
}
@Override
- @Transactional
+ @Transactional(rollbackFor = Exception.class)
public void deleteJob(Long id) throws SchedulerException {
// 校验存在
InfJobDO job = this.validateJobExists(id);
diff --git a/src/main/java/cn/iocoder/dashboard/modules/infra/service/logger/impl/InfApiAccessLogServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/infra/service/logger/impl/InfApiAccessLogServiceImpl.java
index f0d20aec4..7ed83d9e1 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/infra/service/logger/impl/InfApiAccessLogServiceImpl.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/infra/service/logger/impl/InfApiAccessLogServiceImpl.java
@@ -9,12 +9,13 @@ import cn.iocoder.dashboard.modules.infra.dal.dataobject.logger.InfApiAccessLogD
import cn.iocoder.dashboard.modules.infra.dal.mysql.logger.InfApiAccessLogMapper;
import cn.iocoder.dashboard.modules.infra.service.logger.InfApiAccessLogService;
import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
-import javax.validation.Valid;
import java.util.List;
+import java.util.concurrent.Future;
/**
* API 访问日志 Service 实现类
@@ -30,10 +31,11 @@ public class InfApiAccessLogServiceImpl implements InfApiAccessLogService {
@Override
@Async
- public void createApiAccessLogAsync(ApiAccessLogCreateDTO createDTO) {
+ public Future createApiAccessLogAsync(ApiAccessLogCreateDTO createDTO) {
// 插入
InfApiAccessLogDO apiAccessLog = InfApiAccessLogConvert.INSTANCE.convert(createDTO);
- apiAccessLogMapper.insert(apiAccessLog);
+ int insert = apiAccessLogMapper.insert(apiAccessLog);
+ return new AsyncResult<>(insert == 1);
}
@Override
diff --git a/src/main/java/cn/iocoder/dashboard/modules/infra/service/logger/impl/InfApiErrorLogServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/infra/service/logger/impl/InfApiErrorLogServiceImpl.java
index c6a8418e4..647c0621b 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/infra/service/logger/impl/InfApiErrorLogServiceImpl.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/infra/service/logger/impl/InfApiErrorLogServiceImpl.java
@@ -10,12 +10,14 @@ import cn.iocoder.dashboard.modules.infra.dal.mysql.logger.InfApiErrorLogMapper;
import cn.iocoder.dashboard.modules.infra.enums.logger.InfApiErrorLogProcessStatusEnum;
import cn.iocoder.dashboard.modules.infra.service.logger.InfApiErrorLogService;
import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
+import java.util.concurrent.Future;
import static cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.API_ERROR_LOG_NOT_FOUND;
@@ -35,10 +37,11 @@ public class InfApiErrorLogServiceImpl implements InfApiErrorLogService {
@Override
@Async
- public void createApiErrorLogAsync(ApiErrorLogCreateDTO createDTO) {
+ public Future createApiErrorLogAsync(ApiErrorLogCreateDTO createDTO) {
InfApiErrorLogDO apiErrorLog = InfApiErrorLogConvert.INSTANCE.convert(createDTO);
apiErrorLog.setProcessStatus(InfApiErrorLogProcessStatusEnum.INIT.getStatus());
- apiErrorLogMapper.insert(apiErrorLog);
+ int insert = apiErrorLogMapper.insert(apiErrorLog);
+ return new AsyncResult<>(insert == 1);
}
@Override
@@ -62,7 +65,7 @@ public class InfApiErrorLogServiceImpl implements InfApiErrorLogService {
}
// 标记处理
apiErrorLogMapper.updateById(InfApiErrorLogDO.builder().id(id).processStatus(processStatus)
- .processUserId(processStatus).processTime(new Date()).build());
+ .processUserId(processUserId).processTime(new Date()).build());
}
}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/common/SysCaptchaController.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/common/SysCaptchaController.java
index 13e84dc4e..487389331 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/controller/common/SysCaptchaController.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/controller/common/SysCaptchaController.java
@@ -21,8 +21,8 @@ public class SysCaptchaController {
@Resource
private SysCaptchaService captchaService;
- @ApiOperation("生成图片验证码")
@GetMapping("/get-image")
+ @ApiOperation("生成图片验证码")
public CommonResult getCaptchaImage() {
return success(captchaService.getCaptchaImage());
}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/user/SysUserController.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/user/SysUserController.java
index 273be98f0..4374ff4d9 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/controller/user/SysUserController.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/controller/user/SysUserController.java
@@ -40,8 +40,8 @@ public class SysUserController {
@Resource
private SysDeptService deptService;
- @ApiOperation("获得用户分页列表")
@GetMapping("/page")
+ @ApiOperation("获得用户分页列表")
@PreAuthorize("@ss.hasPermission('system:user:list')")
public CommonResult> pageUsers(@Validated SysUserPageReqVO reqVO) {
// 获得用户分页列表
@@ -66,9 +66,9 @@ public class SysUserController {
/**
* 根据用户编号获取详细信息
*/
+ @GetMapping("/get")
@ApiOperation("获得用户详情")
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
- @GetMapping("/get")
// @PreAuthorize("@ss.hasPermi('system:user:query')")
public CommonResult getInfo(@RequestParam("id") Long id) {
return success(SysUserConvert.INSTANCE.convert(userService.getUser(id)));
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/user/SysUserProfileController.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/user/SysUserProfileController.java
index bd84ba60c..a43b0a4e7 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/controller/user/SysUserProfileController.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/controller/user/SysUserProfileController.java
@@ -1,92 +1,80 @@
package cn.iocoder.dashboard.modules.system.controller.user;
+import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil;
+import cn.iocoder.dashboard.common.pojo.CommonResult;
+import cn.iocoder.dashboard.framework.security.core.util.SecurityFrameworkUtils;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserProfileRespVO;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserProfileUpdateReqVO;
+import cn.iocoder.dashboard.modules.system.convert.auth.SysAuthConvert;
+import cn.iocoder.dashboard.modules.system.convert.user.SysUserConvert;
+import cn.iocoder.dashboard.modules.system.dal.dataobject.permission.SysRoleDO;
+import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO;
+import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService;
+import cn.iocoder.dashboard.modules.system.service.permission.SysRoleService;
+import cn.iocoder.dashboard.modules.system.service.user.SysUserService;
+import cn.iocoder.dashboard.util.collection.CollectionUtils;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.GetMapping;
+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.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.util.List;
+
+import static cn.iocoder.dashboard.common.pojo.CommonResult.success;
+import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.FILE_IS_EMPTY;
+
+/**
+ * @author niudehua
+ */
+@RestController
+@RequestMapping("/system/user/profile")
+@Api(tags = "用户个人中心")
+@Slf4j
public class SysUserProfileController {
-// /**
-// * 个人信息
-// */
-// @GetMapping
-// public AjaxResult profile()
-// {
-// LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
-// SysUser user = loginUser.getUser();
-// AjaxResult ajax = AjaxResult.success(user);
-// ajax.put("roleGroup", userService.selectUserRoleGroup(loginUser.getUsername()));
-// ajax.put("postGroup", userService.selectUserPostGroup(loginUser.getUsername()));
-// return ajax;
-// }
-//
-// /**
-// * 修改用户
-// */
-// @Log(title = "个人信息", businessType = BusinessType.UPDATE)
-// @PutMapping
-// public AjaxResult updateProfile(@RequestBody SysUser user)
-// {
-// if (userService.updateUserProfile(user) > 0)
-// {
-// LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
-// // 更新缓存用户信息
-// loginUser.getUser().setNickName(user.getNickName());
-// loginUser.getUser().setPhonenumber(user.getPhonenumber());
-// loginUser.getUser().setEmail(user.getEmail());
-// loginUser.getUser().setSex(user.getSex());
-// tokenService.setLoginUser(loginUser);
-// return AjaxResult.success();
-// }
-// return AjaxResult.error("修改个人信息异常,请联系管理员");
-// }
-//
-// /**
-// * 重置密码
-// */
-// @Log(title = "个人信息", businessType = BusinessType.UPDATE)
-// @PutMapping("/updatePwd")
-// public AjaxResult updatePwd(String oldPassword, String newPassword)
-// {
-// LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
-// String userName = loginUser.getUsername();
-// String password = loginUser.getPassword();
-// if (!SecurityUtils.matchesPassword(oldPassword, password))
-// {
-// return AjaxResult.error("修改密码失败,旧密码错误");
-// }
-// if (SecurityUtils.matchesPassword(newPassword, password))
-// {
-// return AjaxResult.error("新密码不能与旧密码相同");
-// }
-// if (userService.resetUserPwd(userName, SecurityUtils.encryptPassword(newPassword)) > 0)
-// {
-// // 更新缓存用户密码
-// loginUser.getUser().setPassword(SecurityUtils.encryptPassword(newPassword));
-// tokenService.setLoginUser(loginUser);
-// return AjaxResult.success();
-// }
-// return AjaxResult.error("修改密码异常,请联系管理员");
-// }
-//
-// /**
-// * 头像上传
-// */
-// @Log(title = "用户头像", businessType = BusinessType.UPDATE)
-// @PostMapping("/avatar")
-// public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws IOException
-// {
-// if (!file.isEmpty())
-// {
-// LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
-// String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file);
-// if (userService.updateUserAvatar(loginUser.getUsername(), avatar))
-// {
-// AjaxResult ajax = AjaxResult.success();
-// ajax.put("imgUrl", avatar);
-// // 更新缓存用户头像
-// loginUser.getUser().setAvatar(avatar);
-// tokenService.setLoginUser(loginUser);
-// return ajax;
-// }
-// }
-// return AjaxResult.error("上传图片异常,请联系管理员");
-// }
+ @Resource
+ private SysUserService userService;
+ @Resource
+ private SysPermissionService permissionService;
+ @Resource
+ private SysRoleService roleService;
+ @GetMapping("/get")
+ @ApiOperation("获得登录用户信息")
+ public CommonResult profile() {
+ // 获取用户信息
+ Long userId = SecurityFrameworkUtils.getLoginUserId();
+ SysUserDO user = userService.getUser(userId);
+ SysUserProfileRespVO userProfileRespVO = SysUserConvert.INSTANCE.convert03(user);
+ List userRoles = roleService.listRolesFromCache(permissionService.listUserRoleIs(userId));
+ userProfileRespVO.setRoles(CollectionUtils.convertSet(userRoles, SysUserConvert.INSTANCE::convert));
+ return success(userProfileRespVO);
+ }
+
+ @PostMapping("/update")
+ @ApiOperation("修改用户个人信息")
+ public CommonResult updateProfile(@RequestBody SysUserProfileUpdateReqVO reqVO, HttpServletRequest request) {
+ userService.updateUserProfile(reqVO);
+ SecurityFrameworkUtils.setLoginUser(SysAuthConvert.INSTANCE.convert(reqVO), request);
+ return success(true);
+ }
+
+ @PostMapping("/upload-avatar")
+ @ApiOperation("上传用户个人头像")
+ public CommonResult uploadAvatar(@RequestParam("avatarFile") MultipartFile file) throws IOException {
+ if (file.isEmpty()) {
+ throw ServiceExceptionUtil.exception(FILE_IS_EMPTY);
+ }
+ userService.updateAvatar(SecurityFrameworkUtils.getLoginUserId(), file.getInputStream());
+ return success(true);
+ }
}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/user/vo/user/SysUserProfileRespVO.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/user/vo/user/SysUserProfileRespVO.java
new file mode 100644
index 000000000..39737f00b
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/controller/user/vo/user/SysUserProfileRespVO.java
@@ -0,0 +1,37 @@
+package cn.iocoder.dashboard.modules.system.controller.user.vo.user;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import java.util.Set;
+
+
+@ApiModel("用户个人中心信息 Response VO")
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+public class SysUserProfileRespVO extends SysUserRespVO {
+
+ /**
+ * 所属角色
+ */
+ @ApiModelProperty(value = "所属角色", required = true, example = "123456")
+ private Set roles;
+
+ @ApiModel("角色")
+ @Data
+ public static class Role {
+
+ @ApiModelProperty(value = "角色编号", required = true, example = "1")
+ private Long id;
+
+ @ApiModelProperty(value = "角色名称", required = true, example = "普通角色")
+ private String name;
+
+ }
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/user/vo/user/SysUserProfileUpdateReqVO.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/user/vo/user/SysUserProfileUpdateReqVO.java
new file mode 100644
index 000000000..cea2ca77c
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/controller/user/vo/user/SysUserProfileUpdateReqVO.java
@@ -0,0 +1,44 @@
+package cn.iocoder.dashboard.modules.system.controller.user.vo.user;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.Email;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+@ApiModel("用户个人信息更新 Request VO")
+@Data
+public class SysUserProfileUpdateReqVO {
+
+ @ApiModelProperty(value = "用户编号", required = true, example = "1024")
+ @NotNull(message = "用户编号不能为空")
+ private Long id;
+
+ @ApiModelProperty(value = "用户昵称", required = true, example = "芋艿")
+ @Size(max = 30, message = "用户昵称长度不能超过30个字符")
+ private String nickname;
+
+ @ApiModelProperty(value = "用户邮箱", example = "yudao@iocoder.cn")
+ @Email(message = "邮箱格式不正确")
+ @Size(max = 50, message = "邮箱长度不能超过50个字符")
+ private String email;
+
+ @ApiModelProperty(value = "手机号码", example = "15601691300")
+ @Size(max = 11, message = "手机号码长度不能超过11个字符")
+ private String mobile;
+
+ @ApiModelProperty(value = "用户性别", example = "1", notes = "参见 SysSexEnum 枚举类")
+ private Integer sex;
+
+ @ApiModelProperty(value = "用户头像", example = "http://www.iocoder.cn/xxx.png")
+ private String avatar;
+
+ @ApiModelProperty(value = "旧密码", required = true, example = "123456")
+ private String oldPassword;
+
+ @ApiModelProperty(value = "新密码", required = true, example = "654321")
+ private String newPassword;
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/convert/auth/SysAuthConvert.java b/src/main/java/cn/iocoder/dashboard/modules/system/convert/auth/SysAuthConvert.java
index 1c34fe407..e474ce7b1 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/convert/auth/SysAuthConvert.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/convert/auth/SysAuthConvert.java
@@ -3,6 +3,7 @@ package cn.iocoder.dashboard.modules.system.convert.auth;
import cn.iocoder.dashboard.framework.security.core.LoginUser;
import cn.iocoder.dashboard.modules.system.controller.auth.vo.auth.SysAuthMenuRespVO;
import cn.iocoder.dashboard.modules.system.controller.auth.vo.auth.SysAuthPermissionInfoRespVO;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserProfileUpdateReqVO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.permission.SysMenuDO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.permission.SysRoleDO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO;
@@ -13,26 +14,33 @@ import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import org.slf4j.LoggerFactory;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
@Mapper
public interface SysAuthConvert {
SysAuthConvert INSTANCE = Mappers.getMapper(SysAuthConvert.class);
- @Mapping(source = "updateTime", target = "updateTime", ignore = true) // 字段相同,但是含义不同,忽略
+ @Mapping(source = "updateTime", target = "updateTime", ignore = true)
+ // 字段相同,但是含义不同,忽略
LoginUser convert(SysUserDO bean);
default SysAuthPermissionInfoRespVO convert(SysUserDO user, List roleList, List menuList) {
return SysAuthPermissionInfoRespVO.builder()
- .user(SysAuthPermissionInfoRespVO.UserVO.builder().nickname(user.getNickname()).avatar(user.getAvatar()).build())
- .roles(CollectionUtils.convertSet(roleList, SysRoleDO::getCode))
- .permissions(CollectionUtils.convertSet(menuList, SysMenuDO::getPermission))
- .build();
+ .user(SysAuthPermissionInfoRespVO.UserVO.builder().nickname(user.getNickname()).avatar(user.getAvatar()).build())
+ .roles(CollectionUtils.convertSet(roleList, SysRoleDO::getCode))
+ .permissions(CollectionUtils.convertSet(menuList, SysMenuDO::getPermission))
+ .build();
}
SysAuthMenuRespVO convertTreeNode(SysMenuDO menu);
+ LoginUser convert(SysUserProfileUpdateReqVO reqVO);
+
/**
* 将菜单列表,构建成菜单树
*
@@ -47,12 +55,12 @@ public interface SysAuthConvert {
Map treeNodeMap = new LinkedHashMap<>();
menuList.forEach(menu -> treeNodeMap.put(menu.getId(), SysAuthConvert.INSTANCE.convertTreeNode(menu)));
// 处理父子关系
- treeNodeMap.values().stream().filter(node -> !node.getParentId().equals(MenuIdEnum.ROOT.getId())).forEach((childNode) -> {
+ treeNodeMap.values().stream().filter(node -> !node.getParentId().equals(MenuIdEnum.ROOT.getId())).forEach(childNode -> {
// 获得父节点
SysAuthMenuRespVO parentNode = treeNodeMap.get(childNode.getParentId());
if (parentNode == null) {
LoggerFactory.getLogger(getClass()).error("[buildRouterTree][resource({}) 找不到父资源({})]",
- childNode.getId(), childNode.getParentId());
+ childNode.getId(), childNode.getParentId());
return;
}
// 将自己添加到父节点中
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/convert/user/SysUserConvert.java b/src/main/java/cn/iocoder/dashboard/modules/system/convert/user/SysUserConvert.java
index b8ff73671..af72c9c2b 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/convert/user/SysUserConvert.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/convert/user/SysUserConvert.java
@@ -1,7 +1,14 @@
package cn.iocoder.dashboard.modules.system.convert.user;
-import cn.iocoder.dashboard.modules.system.controller.user.vo.user.*;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserCreateReqVO;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserExcelVO;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserImportExcelVO;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserPageItemRespVO;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserProfileRespVO;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserProfileUpdateReqVO;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserUpdateReqVO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.dept.SysDeptDO;
+import cn.iocoder.dashboard.modules.system.dal.dataobject.permission.SysRoleDO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@@ -23,4 +30,12 @@ public interface SysUserConvert {
SysUserDO convert(SysUserImportExcelVO bean);
+ SysUserProfileRespVO convert03(SysUserDO bean);
+
+ SysUserProfileRespVO.Role convert(SysRoleDO bean);
+
+ SysUserDO convert(SysUserProfileUpdateReqVO bean);
+
+
+
}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/enums/SysErrorCodeConstants.java b/src/main/java/cn/iocoder/dashboard/modules/system/enums/SysErrorCodeConstants.java
index 21d3e8910..0197b6c05 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/enums/SysErrorCodeConstants.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/enums/SysErrorCodeConstants.java
@@ -40,6 +40,7 @@ public interface SysErrorCodeConstants {
ErrorCode USER_EMAIL_EXISTS = new ErrorCode(1002004002, "邮箱已经存在");
ErrorCode USER_NOT_EXISTS = new ErrorCode(1002004003, "用户不存在");
ErrorCode USER_IMPORT_LIST_IS_EMPTY = new ErrorCode(1002004004, "导入用户数据不能为空!");
+ ErrorCode USER_PASSWORD_FAILED = new ErrorCode(1002004005, "用户密码校验失败");
// ========== 部门模块 1002005000 ==========
ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1002004001, "已经存在该名字的部门");
@@ -74,5 +75,7 @@ public interface SysErrorCodeConstants {
// ========== 文件 1002009000 ==========
ErrorCode FILE_PATH_EXISTS = new ErrorCode(1002009001, "文件路径已经存在");
+ ErrorCode FILE_UPLOAD_FAILED = new ErrorCode(1002009002, "文件上传失败");
+ ErrorCode FILE_IS_EMPTY= new ErrorCode(1002009003, "文件为空");
}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysMenuServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysMenuServiceImpl.java
index 070677d8e..b4773c248 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysMenuServiceImpl.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysMenuServiceImpl.java
@@ -206,7 +206,7 @@ public class SysMenuServiceImpl implements SysMenuService {
*
* @param menuId 菜单编号
*/
- @Transactional
+ @Transactional(rollbackFor = Exception.class)
public void deleteMenu(Long menuId) {
// 校验更新的菜单是否存在
if (menuMapper.selectById(menuId) == null) {
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysPermissionServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysPermissionServiceImpl.java
index 9f48af9f5..ceb1d6d83 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysPermissionServiceImpl.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysPermissionServiceImpl.java
@@ -176,7 +176,7 @@ public class SysPermissionServiceImpl implements SysPermissionService {
}
@Override
- @Transactional
+ @Transactional(rollbackFor = Exception.class)
public void assignRoleMenu(Long roleId, Set menuIds) {
// 获得角色拥有菜单编号
Set dbMenuIds = CollectionUtils.convertSet(roleMenuMapper.selectListByRoleId(roleId),
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysRoleServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysRoleServiceImpl.java
index d760b734b..93f67ea71 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysRoleServiceImpl.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysRoleServiceImpl.java
@@ -174,7 +174,7 @@ public class SysRoleServiceImpl implements SysRoleService {
}
@Override
- @Transactional
+ @Transactional(rollbackFor = Exception.class)
public void deleteRole(Long id) {
// 校验是否可以更新
this.checkUpdateRole(id);
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/user/SysUserService.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/user/SysUserService.java
index 3de54f34b..c0ac9e611 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/service/user/SysUserService.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/user/SysUserService.java
@@ -2,10 +2,17 @@ package cn.iocoder.dashboard.modules.system.service.user;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.dashboard.common.pojo.PageResult;
-import cn.iocoder.dashboard.modules.system.controller.user.vo.user.*;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserCreateReqVO;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserExportReqVO;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserImportExcelVO;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserImportRespVO;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserPageReqVO;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserProfileUpdateReqVO;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserUpdateReqVO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO;
import cn.iocoder.dashboard.util.collection.CollectionUtils;
+import java.io.InputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
@@ -102,6 +109,14 @@ public interface SysUserService {
*/
void updateUser(SysUserUpdateReqVO reqVO);
+ /**
+ * 修改用户个人信息
+ *
+ * @param reqVO 用户个人信息
+ * @return 修改结果
+ */
+ void updateUserProfile(SysUserProfileUpdateReqVO reqVO);
+
/**
* 删除用户
*
@@ -112,7 +127,7 @@ public interface SysUserService {
/**
* 修改密码
*
- * @param id 用户编号
+ * @param id 用户编号
* @param password 密码
*/
void updateUserPassword(Long id, String password);
@@ -120,7 +135,7 @@ public interface SysUserService {
/**
* 修改密码
*
- * @param id 用户编号
+ * @param id 用户编号
* @param status 状态
*/
void updateUserStatus(Long id, Integer status);
@@ -128,12 +143,20 @@ public interface SysUserService {
/**
* 批量导入用户
*
- * @param importUsers 导入用户列表
+ * @param importUsers 导入用户列表
* @param isUpdateSupport 是否支持更新
* @return 导入结果
*/
SysUserImportRespVO importUsers(List importUsers, boolean isUpdateSupport);
+ /**
+ * 更新用户头像
+ *
+ * @param id 用户 id
+ * @param avatarFile 头像文件
+ */
+ void updateAvatar(Long id, InputStream avatarFile);
+
//
// /**
// * 修改用户基本信息
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/user/SysUserServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/user/SysUserServiceImpl.java
index d97d2c00f..acffcb7d1 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/service/user/SysUserServiceImpl.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/user/SysUserServiceImpl.java
@@ -1,17 +1,26 @@
package cn.iocoder.dashboard.modules.system.service.user;
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.dashboard.common.enums.CommonStatusEnum;
import cn.iocoder.dashboard.common.exception.ServiceException;
import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.dashboard.common.pojo.PageResult;
-import cn.iocoder.dashboard.modules.system.controller.user.vo.user.*;
+import cn.iocoder.dashboard.modules.infra.service.file.InfFileService;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserCreateReqVO;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserExportReqVO;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserImportExcelVO;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserImportRespVO;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserPageReqVO;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserProfileUpdateReqVO;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserUpdateReqVO;
import cn.iocoder.dashboard.modules.system.convert.user.SysUserConvert;
-import cn.iocoder.dashboard.modules.system.dal.mysql.user.SysUserMapper;
import cn.iocoder.dashboard.modules.system.dal.dataobject.dept.SysDeptDO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.dept.SysPostDO;
import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO;
+import cn.iocoder.dashboard.modules.system.dal.mysql.user.SysUserMapper;
import cn.iocoder.dashboard.modules.system.service.dept.SysDeptService;
import cn.iocoder.dashboard.modules.system.service.dept.SysPostService;
import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService;
@@ -22,7 +31,14 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
-import java.util.*;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
@@ -49,18 +65,8 @@ public class SysUserServiceImpl implements SysUserService {
@Resource
private PasswordEncoder passwordEncoder;
-// /**
-// * 根据条件分页查询用户列表
-// *
-// * @param user 用户信息
-// * @return 用户信息集合信息
-// */
-// @Override
-// @DataScope(deptAlias = "d", userAlias = "u")
-// public List selectUserList(SysUser user)
-// {
-// return userMapper.selectUserList(user);
-// }
+ @Resource
+ private InfFileService fileService;
@Override
public SysUserDO getUserByUserName(String username) {
@@ -108,7 +114,7 @@ public class SysUserServiceImpl implements SysUserService {
return Collections.emptySet();
}
Set deptIds = CollectionUtils.convertSet(deptService.listDeptsByParentIdFromCache(
- deptId, true), SysDeptDO::getId);
+ deptId, true), SysDeptDO::getId);
deptIds.add(deptId); // 包括自身
return deptIds;
}
@@ -117,7 +123,7 @@ public class SysUserServiceImpl implements SysUserService {
public Long createUser(SysUserCreateReqVO reqVO) {
// 校验正确性
this.checkCreateOrUpdate(null, reqVO.getUsername(), reqVO.getMobile(), reqVO.getEmail(),
- reqVO.getDeptId(), reqVO.getPostIds());
+ reqVO.getDeptId(), reqVO.getPostIds());
// 插入用户
SysUserDO user = SysUserConvert.INSTANCE.convert(reqVO);
user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启
@@ -130,12 +136,31 @@ public class SysUserServiceImpl implements SysUserService {
public void updateUser(SysUserUpdateReqVO reqVO) {
// 校验正确性
this.checkCreateOrUpdate(reqVO.getId(), reqVO.getUsername(), reqVO.getMobile(), reqVO.getEmail(),
- reqVO.getDeptId(), reqVO.getPostIds());
+ reqVO.getDeptId(), reqVO.getPostIds());
// 更新用户
SysUserDO updateObj = SysUserConvert.INSTANCE.convert(reqVO);
userMapper.updateById(updateObj);
}
+ @Override
+ public void updateUserProfile(SysUserProfileUpdateReqVO reqVO) {
+ // 校验正确性
+ this.checkUserExists(reqVO.getId());
+ this.checkEmailUnique(reqVO.getId(), reqVO.getEmail());
+ this.checkMobileUnique(reqVO.getId(), reqVO.getMobile());
+ // 校验填写密码
+ String encode = null;
+ if (this.checkOldPassword(reqVO.getId(), reqVO.getOldPassword(), reqVO.getNewPassword())) {
+ // 更新密码
+ encode = passwordEncoder.encode(reqVO.getNewPassword());
+ }
+ SysUserDO updateObj = SysUserConvert.INSTANCE.convert(reqVO);
+ if (StrUtil.isNotBlank(encode)) {
+ updateObj.setPassword(encode);
+ }
+ userMapper.updateById(updateObj);
+ }
+
@Override
public void deleteUser(Long id) {
// 校验用户存在
@@ -278,19 +303,42 @@ public class SysUserServiceImpl implements SysUserService {
});
}
+ /**
+ * 校验旧密码、新密码
+ *
+ * @param id 用户 id
+ * @param oldPassword 旧密码
+ * @param newPassword 新密码
+ * @return 校验结果
+ */
+ private boolean checkOldPassword(Long id, String oldPassword, String newPassword) {
+ if (id == null || StrUtil.isBlank(oldPassword) || StrUtil.isBlank(newPassword)) {
+ return false;
+ }
+ SysUserDO user = userMapper.selectById(id);
+ if (user == null) {
+ throw ServiceExceptionUtil.exception(USER_NOT_EXISTS);
+ }
+
+ if (!passwordEncoder.matches(oldPassword, user.getPassword())) {
+ throw ServiceExceptionUtil.exception(USER_PASSWORD_FAILED);
+ }
+ return true;
+ }
+
@Override
- @Transactional // 添加事务,异常则回滚所有导入
+ @Transactional(rollbackFor = Exception.class) // 添加事务,异常则回滚所有导入
public SysUserImportRespVO importUsers(List importUsers, boolean isUpdateSupport) {
if (CollUtil.isEmpty(importUsers)) {
throw ServiceExceptionUtil.exception(USER_IMPORT_LIST_IS_EMPTY);
}
SysUserImportRespVO respVO = SysUserImportRespVO.builder().createUsernames(new ArrayList<>())
- .updateUsernames(new ArrayList<>()).failureUsernames(new LinkedHashMap<>()).build();
+ .updateUsernames(new ArrayList<>()).failureUsernames(new LinkedHashMap<>()).build();
importUsers.forEach(importUser -> {
// 校验,判断是否有不符合的原因
try {
checkCreateOrUpdate(null, null, importUser.getMobile(), importUser.getEmail(),
- importUser.getDeptId(), null);
+ importUser.getDeptId(), null);
} catch (ServiceException ex) {
respVO.getFailureUsernames().put(importUser.getUsername(), ex.getMessage());
return;
@@ -316,4 +364,16 @@ public class SysUserServiceImpl implements SysUserService {
return respVO;
}
+ @Override
+ public void updateAvatar(Long id, InputStream avatarFile) {
+ this.checkUserExists(id);
+ // 存储文件
+ String avatar = fileService.createFile(IdUtil.fastUUID(), IoUtil.readBytes(avatarFile));
+ // 更新路径
+ SysUserDO sysUserDO = new SysUserDO();
+ sysUserDO.setId(id);
+ sysUserDO.setAvatar(avatar);
+ userMapper.updateById(sysUserDO);
+ }
+
}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/tool/service/codegen/impl/ToolCodegenServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/tool/service/codegen/impl/ToolCodegenServiceImpl.java
index 6b2b5f9f7..5b746f6fd 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/tool/service/codegen/impl/ToolCodegenServiceImpl.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/tool/service/codegen/impl/ToolCodegenServiceImpl.java
@@ -109,7 +109,7 @@ public class ToolCodegenServiceImpl implements ToolCodegenService {
}
@Override
- @Transactional
+ @Transactional(rollbackFor = Exception.class)
public List createCodegenListFromDB(List tableNames) {
List ids = new ArrayList<>(tableNames.size());
// 遍历添加。虽然效率会低一点,但是没必要做成完全批量,因为不会这么大量
@@ -118,7 +118,7 @@ public class ToolCodegenServiceImpl implements ToolCodegenService {
}
@Override
- @Transactional
+ @Transactional(rollbackFor = Exception.class)
public void updateCodegen(ToolCodegenUpdateReqVO updateReqVO) {
// 校验是否已经存在
if (codegenTableMapper.selectById(updateReqVO.getTable().getId()) == null) {
@@ -134,7 +134,7 @@ public class ToolCodegenServiceImpl implements ToolCodegenService {
}
@Override
- @Transactional
+ @Transactional(rollbackFor = Exception.class)
public void syncCodegenFromDB(Long tableId) {
// 校验是否已经存在
ToolCodegenTableDO table = codegenTableMapper.selectById(tableId);
@@ -149,7 +149,7 @@ public class ToolCodegenServiceImpl implements ToolCodegenService {
}
@Override
- @Transactional
+ @Transactional(rollbackFor = Exception.class)
public void syncCodegenFromSQL(Long tableId, String sql) {
// 校验是否已经存在
ToolCodegenTableDO table = codegenTableMapper.selectById(tableId);
@@ -201,7 +201,7 @@ public class ToolCodegenServiceImpl implements ToolCodegenService {
}
@Override
- @Transactional
+ @Transactional(rollbackFor = Exception.class)
public void deleteCodegen(Long tableId) {
// 校验是否已经存在
if (codegenTableMapper.selectById(tableId) == null) {
diff --git a/src/main/resources/application-dev.yaml b/src/main/resources/application-dev.yaml
index 7c3974196..b0a96fb04 100644
--- a/src/main/resources/application-dev.yaml
+++ b/src/main/resources/application-dev.yaml
@@ -145,14 +145,14 @@ yudao:
swagger:
title: 管理后台
description: 提供管理员管理的所有功能
- version: ${yudao.info.base-package}
+ version: ${yudao.info.version}
base-package: ${yudao.info.base-package}.modules
captcha:
timeout: 5m
width: 160
height: 60
file:
- base-path: http://127.0.0.1:${server.port}/${yudao.web.api-prefix}/infra/file/get/
+ base-path: http://127.0.0.1:${server.port}${yudao.web.api-prefix}/system/file/get/
codegen:
base-package: ${yudao.info.base-package}
db-schemas: ${spring.datasource.name}
diff --git a/src/main/resources/application-local.yaml b/src/main/resources/application-local.yaml
index 96dff278c..fdb260758 100644
--- a/src/main/resources/application-local.yaml
+++ b/src/main/resources/application-local.yaml
@@ -145,14 +145,14 @@ yudao:
swagger:
title: 管理后台
description: 提供管理员管理的所有功能
- version: ${yudao.info.base-package}
+ version: ${yudao.info.version}
base-package: ${yudao.info.base-package}.modules
captcha:
timeout: 5m
width: 160
height: 60
file:
- base-path: http://127.0.0.1:${server.port}/${yudao.web.api-prefix}/infra/file/get/
+ base-path: http://127.0.0.1:${server.port}${yudao.web.api-prefix}/system/file/get/
codegen:
base-package: ${yudao.info.base-package}
db-schemas: ${spring.datasource.name}
diff --git a/src/test/java/cn/iocoder/dashboard/BaseDbAndRedisUnitTest.java b/src/test/java/cn/iocoder/dashboard/BaseDbAndRedisUnitTest.java
index aadf47bda..f1e2dddd4 100644
--- a/src/test/java/cn/iocoder/dashboard/BaseDbAndRedisUnitTest.java
+++ b/src/test/java/cn/iocoder/dashboard/BaseDbAndRedisUnitTest.java
@@ -16,9 +16,9 @@ import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.jdbc.Sql;
/**
- * 依赖内存 DB 的单元测试
+ * 依赖内存 DB + Redis 的单元测试
*
- * 注意,Service 层同样适用。对于 Service 层的单元测试,我们针对自己模块的 Mapper 走的是 H2 内存数据库,针对别的模块的 Service 走的是 Mock 方法
+ * 相比 {@link BaseDbUnitTest} 来说,额外增加了内存 Redis
*
* @author 芋道源码
*/
diff --git a/src/test/java/cn/iocoder/dashboard/BaseRedisUnitTest.java b/src/test/java/cn/iocoder/dashboard/BaseRedisUnitTest.java
new file mode 100644
index 000000000..a806e4670
--- /dev/null
+++ b/src/test/java/cn/iocoder/dashboard/BaseRedisUnitTest.java
@@ -0,0 +1,32 @@
+package cn.iocoder.dashboard;
+
+import cn.iocoder.dashboard.config.RedisTestConfiguration;
+import cn.iocoder.dashboard.framework.redis.config.RedisConfig;
+import org.redisson.spring.starter.RedissonAutoConfiguration;
+import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.context.ActiveProfiles;
+
+/**
+ * 依赖内存 Redis 的单元测试
+ *
+ * 相比 {@link BaseDbUnitTest} 来说,从内存 DB 改成了内存 Redis
+ *
+ * @author 芋道源码
+ */
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = BaseRedisUnitTest.Application.class)
+@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件
+public class BaseRedisUnitTest {
+
+ @Import({
+ // Redis 配置类
+ RedisTestConfiguration.class, // Redis 测试配置类,用于启动 RedisServer
+ RedisAutoConfiguration.class, // Spring Redis 自动配置类
+ RedisConfig.class, // 自己的 Redis 配置类
+ RedissonAutoConfiguration.class, // Redisson 自动高配置类
+ })
+ public static class Application {
+ }
+
+}
diff --git a/src/test/java/cn/iocoder/dashboard/BaseSpringBootUnitTest.java b/src/test/java/cn/iocoder/dashboard/BaseSpringBootUnitTest.java
deleted file mode 100644
index fe615dcaf..000000000
--- a/src/test/java/cn/iocoder/dashboard/BaseSpringBootUnitTest.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package cn.iocoder.dashboard;
-
-import org.junit.jupiter.api.AfterEach;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.data.redis.core.RedisCallback;
-import org.springframework.data.redis.core.StringRedisTemplate;
-import org.springframework.test.context.ActiveProfiles;
-import org.springframework.test.context.jdbc.Sql;
-
-import javax.annotation.Resource;
-
-@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
-@ActiveProfiles("unit-test") // 设置使用 application-unit-test 配置文件
-@Sql(scripts = "/sql/clean.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) // 每个单元测试结束后,清理 DB
-@Deprecated
-public class BaseSpringBootUnitTest {
-
- @Resource
- private StringRedisTemplate stringRedisTemplate;
-
- /**
- * 每个单元测试结束后,清理 Redis
- */
- @AfterEach
- public void cleanRedis() {
- stringRedisTemplate.execute((RedisCallback