Browse Source

Merge branch 'master' into ut_infra_logger

wangkai 4 years ago
parent
commit
914626b850
17 changed files with 384 additions and 154 deletions
  1. 68 16
      src/main/java/cn/iocoder/dashboard/modules/infra/controller/doc/InfDbDocController.java
  2. 4 4
      src/main/java/cn/iocoder/dashboard/modules/infra/service/job/impl/InfJobServiceImpl.java
  3. 2 2
      src/main/java/cn/iocoder/dashboard/modules/system/controller/user/SysUserController.java
  4. 74 86
      src/main/java/cn/iocoder/dashboard/modules/system/controller/user/SysUserProfileController.java
  5. 37 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/user/vo/user/SysUserProfileRespVO.java
  6. 44 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/user/vo/user/SysUserProfileUpdateReqVO.java
  7. 16 8
      src/main/java/cn/iocoder/dashboard/modules/system/convert/auth/SysAuthConvert.java
  8. 16 1
      src/main/java/cn/iocoder/dashboard/modules/system/convert/user/SysUserConvert.java
  9. 3 0
      src/main/java/cn/iocoder/dashboard/modules/system/enums/SysErrorCodeConstants.java
  10. 1 1
      src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysMenuServiceImpl.java
  11. 1 1
      src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysPermissionServiceImpl.java
  12. 1 1
      src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysRoleServiceImpl.java
  13. 27 4
      src/main/java/cn/iocoder/dashboard/modules/system/service/user/SysUserService.java
  14. 81 21
      src/main/java/cn/iocoder/dashboard/modules/system/service/user/SysUserServiceImpl.java
  15. 5 5
      src/main/java/cn/iocoder/dashboard/modules/tool/service/codegen/impl/ToolCodegenServiceImpl.java
  16. 2 2
      src/main/resources/application-dev.yaml
  17. 2 2
      src/main/resources/application-local.yaml

+ 68 - 16
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,34 +41,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 {
+            // 读取,返回
+            //这里不用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();
     }
 

+ 4 - 4
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);

+ 2 - 2
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<PageResult<SysUserPageItemRespVO>> 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<SysUserRespVO> getInfo(@RequestParam("id") Long id) {
         return success(SysUserConvert.INSTANCE.convert(userService.getUser(id)));

+ 74 - 86
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<SysUserProfileRespVO> profile() {
+        // 获取用户信息
+        Long userId = SecurityFrameworkUtils.getLoginUserId();
+        SysUserDO user = userService.getUser(userId);
+        SysUserProfileRespVO userProfileRespVO = SysUserConvert.INSTANCE.convert03(user);
+        List<SysRoleDO> userRoles = roleService.listRolesFromCache(permissionService.listUserRoleIs(userId));
+        userProfileRespVO.setRoles(CollectionUtils.convertSet(userRoles, SysUserConvert.INSTANCE::convert));
+        return success(userProfileRespVO);
+    }
+
+    @PostMapping("/update")
+    @ApiOperation("修改用户个人信息")
+    public CommonResult<Boolean> 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<Boolean> 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);
+    }
 }

+ 37 - 0
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<Role> roles;
+
+    @ApiModel("角色")
+    @Data
+    public static class Role {
+
+        @ApiModelProperty(value = "角色编号", required = true, example = "1")
+        private Long id;
+
+        @ApiModelProperty(value = "角色名称", required = true, example = "普通角色")
+        private String name;
+
+    }
+}

+ 44 - 0
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;
+
+}

+ 16 - 8
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<SysRoleDO> roleList, List<SysMenuDO> 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<Long, SysAuthMenuRespVO> 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;
             }
             // 将自己添加到父节点中

+ 16 - 1
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);
+
+
+
 }

+ 3 - 0
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, "文件为空");
 
 }

+ 1 - 1
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) {

+ 1 - 1
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<Long> menuIds) {
         // 获得角色拥有菜单编号
         Set<Long> dbMenuIds = CollectionUtils.convertSet(roleMenuMapper.selectListByRoleId(roleId),

+ 1 - 1
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);

+ 27 - 4
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<SysUserImportExcelVO> importUsers, boolean isUpdateSupport);
 
+    /**
+     * 更新用户头像
+     *
+     * @param id         用户 id
+     * @param avatarFile 头像文件
+     */
+    void updateAvatar(Long id, InputStream avatarFile);
+
 //
 //    /**
 //     * 修改用户基本信息

+ 81 - 21
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<SysUser> 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<Long> 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<SysUserImportExcelVO> 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);
+    }
+
 }

+ 5 - 5
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<Long> createCodegenListFromDB(List<String> tableNames) {
         List<Long> 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) {

+ 2 - 2
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}

+ 2 - 2
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}