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..f5b293538 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,11 @@ package cn.iocoder.dashboard.modules.infra.controller.doc; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.lang.UUID; +import cn.hutool.core.util.IdUtil; import cn.hutool.extra.servlet.ServletUtil; +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 +15,20 @@ 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.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.http.MediaType; +import org.springframework.util.StreamUtils; 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.*; import java.util.Collections; @Api(tags = "数据库文档") @@ -34,36 +41,81 @@ 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 { + // 读取,返回 + //这里不用hutool工具类,它的中文文件名编码有问题,导致在浏览器下载时有问题 + 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); + } + /** * 创建数据源 */ @@ -83,13 +135,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/service/job/impl/InfJobServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/infra/service/job/impl/InfJobServiceImpl.java index d82207de1..156c423c6 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 @@ -41,7 +41,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()); // 校验唯一性 @@ -66,7 +66,7 @@ public class InfJobServiceImpl implements InfJobService { } @Override - @Transactional + @Transactional(rollbackFor = Exception.class) public void updateJob(InfJobUpdateReqVO updateReqVO) throws SchedulerException { validateCronExpression(updateReqVO.getCronExpression()); // 校验存在 @@ -86,7 +86,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())) { @@ -120,7 +120,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/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}