Browse Source

1. 完成用户导入的功能

YunaiV 4 năm trước cách đây
mục cha
commit
4ab1cf1cd4

+ 3 - 2
ruoyi-ui/src/api/system/user.js

@@ -122,7 +122,8 @@ export function uploadAvatar(data) {
 // 下载用户导入模板
 export function importTemplate() {
   return request({
-    url: '/system/user/importTemplate',
-    method: 'get'
+    url: '/system/user/get-import-template',
+    method: 'get',
+    responseType: 'blob'
   })
 }

+ 17 - 3
ruoyi-ui/src/views/system/user/index.vue

@@ -391,7 +391,7 @@ export default {
         // 设置上传的请求头部
         headers: { Authorization: "Bearer " + getToken() },
         // 上传的地址
-        url: process.env.VUE_APP_BASE_API + "/system/user/importData"
+        url: process.env.VUE_APP_BASE_API + '/api/' + "/system/user/import"
       },
       // 查询参数
       queryParams: {
@@ -669,7 +669,7 @@ export default {
     /** 下载模板操作 */
     importTemplate() {
       importTemplate().then(response => {
-        this.download(response.msg);
+        this.downloadExcel(response, '用户导入模板.xls');
       });
     },
     // 文件上传中处理
@@ -681,7 +681,21 @@ export default {
       this.upload.open = false;
       this.upload.isUploading = false;
       this.$refs.upload.clearFiles();
-      this.$alert(response.msg, "导入结果", { dangerouslyUseHTMLString: true });
+      // 拼接提示语
+      let data = response.data;
+      let text = '创建成功数量:' + data.createUsernames.length;
+      for (const username of data.createUsernames) {
+        text += '<br />&nbsp;&nbsp;&nbsp;&nbsp;' + username;
+      }
+      text += '<br />更新成功数量:' + data.updateUsernames.length;
+      for (const username of data.updateUsernames) {
+        text += '<br />&nbsp;&nbsp;&nbsp;&nbsp;' + username;
+      }
+      text += '<br />更新失败数量:' + Object.keys(data.failureUsernames).length;
+      for (const username in data.failureUsernames) {
+        text += '<br />&nbsp;&nbsp;&nbsp;&nbsp;' + username + ':' + data.failureUsernames[username];
+      }
+      this.$alert(text, "导入结果", { dangerouslyUseHTMLString: true });
       this.getList();
     },
     // 提交上传文件

+ 1 - 0
src/main/java/cn/iocoder/dashboard/common/exception/ServiceException.java

@@ -47,6 +47,7 @@ public final class ServiceException extends RuntimeException {
         return this;
     }
 
+    @Override
     public String getMessage() {
         return message;
     }

+ 3 - 3
src/main/java/cn/iocoder/dashboard/common/exception/util/ServiceExceptionUtil.java

@@ -103,11 +103,11 @@ public class ServiceExceptionUtil {
                 if (i == 0) {
                     return messagePattern;
                 } else {
-                    sbuf.append(messagePattern.substring(i, messagePattern.length()));
+                    sbuf.append(messagePattern.substring(i));
                     return sbuf.toString();
                 }
             } else {
-                sbuf.append(messagePattern.substring(i, j));
+                sbuf.append(messagePattern, i, j);
                 sbuf.append(params[l]);
                 i = j + 2;
             }
@@ -115,7 +115,7 @@ public class ServiceExceptionUtil {
         if (messagePattern.indexOf("{}", i) != -1) {
             LOGGER.error("[doFormat][参数过少:错误码({})|错误内容({})|参数({})", code, messagePattern, params);
         }
-        sbuf.append(messagePattern.substring(i, messagePattern.length()));
+        sbuf.append(messagePattern.substring(i));
         return sbuf.toString();
     }
 

+ 7 - 0
src/main/java/cn/iocoder/dashboard/framework/excel/core/util/ExcelUtils.java

@@ -2,6 +2,7 @@ package cn.iocoder.dashboard.framework.excel.core.util;
 
 import com.alibaba.excel.EasyExcel;
 import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
+import org.springframework.web.multipart.MultipartFile;
 
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
@@ -38,4 +39,10 @@ public class ExcelUtils {
         response.setContentType("application/vnd.ms-excel;charset=UTF-8");
     }
 
+    public static <T> List<T> raed(MultipartFile file, Class<T> head) throws IOException {
+       return EasyExcel.read(file.getInputStream(), head, null)
+                .autoCloseStream(false)  // 不要自动关闭,交给 Servlet 自己处理
+                .doReadAllSync();
+    }
+
 }

+ 33 - 32
src/main/java/cn/iocoder/dashboard/modules/system/controller/user/SysUserController.java

@@ -1,6 +1,7 @@
 package cn.iocoder.dashboard.modules.system.controller.user;
 
 import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
 import cn.iocoder.dashboard.common.pojo.CommonResult;
 import cn.iocoder.dashboard.common.pojo.PageResult;
 import cn.iocoder.dashboard.framework.excel.core.util.ExcelUtils;
@@ -8,23 +9,23 @@ import cn.iocoder.dashboard.modules.system.controller.user.vo.user.*;
 import cn.iocoder.dashboard.modules.system.convert.user.SysUserConvert;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.dept.SysDeptDO;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO;
+import cn.iocoder.dashboard.modules.system.enums.common.SysSexEnum;
 import cn.iocoder.dashboard.modules.system.service.dept.SysDeptService;
 import cn.iocoder.dashboard.modules.system.service.user.SysUserService;
 import cn.iocoder.dashboard.util.collection.CollectionUtils;
 import cn.iocoder.dashboard.util.collection.MapUtils;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
 import io.swagger.annotations.ApiOperation;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
 
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 import static cn.iocoder.dashboard.common.pojo.CommonResult.success;
 
@@ -145,35 +146,35 @@ public class SysUserController {
                 SysUserExcelVO.class, excelUsers);
     }
 
-//    @Log(title = "用户管理", businessType = BusinessType.EXPORT)
-//    @PreAuthorize("@ss.hasPermi('system:user:export')")
-//    @GetMapping("/export")
-//    public AjaxResult export(SysUser user)
-//    {
-//        List<SysUser> list = userService.selectUserList(user);
-//        ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
-//        return util.exportExcel(list, "用户数据");
-//    }
-//
+    @ApiOperation("获得导入用户模板")
+    @GetMapping("/get-import-template")
+    public void importTemplate(HttpServletResponse response) throws IOException {
+        // 手动创建导出 demo
+        List<SysUserImportExcelVO> list = Arrays.asList(
+                SysUserImportExcelVO.builder().username("yudao").deptId(1L).email("yudao@iocoder.cn").mobile("15601691300")
+                        .nickname("芋道").status(CommonStatusEnum.ENABLE.getStatus()).sex(SysSexEnum.MALE.getSEX()).build(),
+                SysUserImportExcelVO.builder().username("yuanma").deptId(2L).email("yuanma@iocoder.cn").mobile("15601701300")
+                        .nickname("源码").status(CommonStatusEnum.DISABLE.getStatus()).sex(SysSexEnum.FEMALE.getSEX()).build()
+        );
+
+        // 输出
+        ExcelUtils.write(response, "用户导入模板.xls", "用户列表",
+                SysUserImportExcelVO.class, list);
+
+    }
+
+    @ApiOperation("导入用户")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "path", value = "Excel 文件", required = true, dataTypeClass = MultipartFile.class),
+            @ApiImplicitParam(name = "update-support", value = "是否支持更新,默认为 false", example = "true", dataTypeClass = Long.class)
+    })
+    @PostMapping("/import")
 //    @Log(title = "用户管理", businessType = BusinessType.IMPORT)
 //    @PreAuthorize("@ss.hasPermi('system:user:import')")
-//    @PostMapping("/importData")
-//    public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception
-//    {
-//        ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
-//        List<SysUser> userList = util.importExcel(file.getInputStream());
-//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
-//        String operName = loginUser.getUsername();
-//        String message = userService.importUser(userList, updateSupport, operName);
-//        return AjaxResult.success(message);
-//    }
-//
-//    @GetMapping("/importTemplate")
-//    public AjaxResult importTemplate()
-//    {
-//        ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
-//        return util.importTemplateExcel("用户数据");
-//    }
-//
+    public CommonResult<SysUserImportRespVO> importExcel(@RequestParam("file") MultipartFile file,
+                                                         @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception {
+        List<SysUserImportExcelVO> list = ExcelUtils.raed(file, SysUserImportExcelVO.class);
+        return success(userService.importUsers(list, updateSupport));
+    }
 
 }

+ 7 - 1
src/main/java/cn/iocoder/dashboard/modules/system/controller/user/vo/user/SysUserImportExcelVO.java

@@ -4,12 +4,18 @@ import cn.iocoder.dashboard.framework.excel.core.annotations.DictFormat;
 import cn.iocoder.dashboard.framework.excel.core.convert.DictConvert;
 import cn.iocoder.dashboard.modules.system.enums.dict.DictTypeEnum;
 import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
 import lombok.Data;
+import lombok.NoArgsConstructor;
 
 /**
  * 用户 Excel 导入 VO
  */
 @Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
 public class SysUserImportExcelVO {
 
     @ExcelProperty("登录名称")
@@ -29,7 +35,7 @@ public class SysUserImportExcelVO {
 
     @ExcelProperty(value = "用户性别", converter = DictConvert.class)
     @DictFormat(DictTypeEnum.SYS_USER_SEX)
-    private String sex;
+    private Integer sex;
 
     @ExcelProperty(value = "账号状态", converter = DictConvert.class)
     @DictFormat(DictTypeEnum.SYS_COMMON_STATUS)

+ 25 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/user/vo/user/SysUserImportRespVO.java

@@ -0,0 +1,25 @@
+package cn.iocoder.dashboard.modules.system.controller.user.vo.user;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Builder;
+import lombok.Data;
+
+import java.util.List;
+import java.util.Map;
+
+@ApiModel("用户导入 Response VO")
+@Data
+@Builder
+public class SysUserImportRespVO {
+
+    @ApiModelProperty(value = "创建成功的用户名数组", required = true)
+    private List<String> createUsernames;
+
+    @ApiModelProperty(value = "更新成功的用户名数组", required = true)
+    private List<String> updateUsernames;
+
+    @ApiModelProperty(value = "导入失败的用户集合", required = true, notes = "key 为用户名,value 为失败原因")
+    private Map<String, String> failureUsernames;
+
+}

+ 1 - 4
src/main/java/cn/iocoder/dashboard/modules/system/controller/user/vo/user/SysUserRespVO.java

@@ -2,10 +2,7 @@ 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 lombok.*;
 
 import java.util.Date;
 

+ 3 - 4
src/main/java/cn/iocoder/dashboard/modules/system/convert/user/SysUserConvert.java

@@ -1,10 +1,7 @@
 package cn.iocoder.dashboard.modules.system.convert.user;
 
 import cn.iocoder.dashboard.common.pojo.PageResult;
-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.SysUserPageItemRespVO;
-import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserUpdateReqVO;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.*;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.dept.SysDeptDO;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO;
 import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -30,4 +27,6 @@ public interface SysUserConvert {
 
     SysUserExcelVO convert02(SysUserDO bean);
 
+    SysUserDO convert(SysUserImportExcelVO bean);
+
 }

+ 1 - 0
src/main/java/cn/iocoder/dashboard/modules/system/enums/SysErrorCodeConstants.java

@@ -37,6 +37,7 @@ public interface SysErrorCodeConstants {
     ErrorCode USER_MOBILE_EXISTS = new ErrorCode(1002004001, "手机号已经存在");
     ErrorCode USER_EMAIL_EXISTS = new ErrorCode(1002004002, "邮箱已经存在");
     ErrorCode USER_NOT_EXISTS = new ErrorCode(1002004003, "用户不存在");
+    ErrorCode USER_IMPORT_LIST_IS_EMPTY = new ErrorCode(1002004004, "导入用户数据不能为空!");
 
     // ========== 部门模块 1002005000 ==========
     ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1002004001, "已经存在该名字的部门");

+ 10 - 14
src/main/java/cn/iocoder/dashboard/modules/system/service/user/SysUserService.java

@@ -1,10 +1,7 @@
 package cn.iocoder.dashboard.modules.system.service.user;
 
 import cn.iocoder.dashboard.common.pojo.PageResult;
-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.SysUserPageReqVO;
-import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserUpdateReqVO;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.*;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO;
 
 import java.util.List;
@@ -86,6 +83,15 @@ public interface SysUserService {
      */
     void updateUserStatus(Long id, Integer status);
 
+    /**
+     * 批量导入用户
+     *
+     * @param importUsers 导入用户列表
+     * @param isUpdateSupport 是否支持更新
+     * @return 导入结果
+     */
+    SysUserImportRespVO importUsers(List<SysUserImportExcelVO> importUsers, boolean isUpdateSupport);
+
 //
 //    /**
 //     * 修改用户基本信息
@@ -121,14 +127,4 @@ public interface SysUserService {
 //     */
 //    public int resetUserPwd(String userName, String password);
 
-//
-//    /**
-//     * 导入用户数据
-//     *
-//     * @param userList 用户数据列表
-//     * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据
-//     * @param operName 操作用户
-//     * @return 结果
-//     */
-//    public String importUser(List<SysUser> userList, Boolean isUpdateSupport, String operName);
 }

+ 41 - 74
src/main/java/cn/iocoder/dashboard/modules/system/service/user/SysUserServiceImpl.java

@@ -3,12 +3,10 @@ package cn.iocoder.dashboard.modules.system.service.user;
 import cn.hutool.core.collection.CollUtil;
 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.SysUserCreateReqVO;
-import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserExportReqVO;
-import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserPageReqVO;
-import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserUpdateReqVO;
+import cn.iocoder.dashboard.modules.system.controller.user.vo.user.*;
 import cn.iocoder.dashboard.modules.system.convert.user.SysUserConvert;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dao.user.SysUserMapper;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.dept.SysDeptDO;
@@ -21,12 +19,10 @@ import cn.iocoder.dashboard.util.collection.CollectionUtils;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 
 import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
 
@@ -268,71 +264,42 @@ public class SysUserServiceImpl implements SysUserService {
         });
     }
 
-//    /**
-//     * 导入用户数据
-//     *
-//     * @param userList 用户数据列表
-//     * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据
-//     * @param operName 操作用户
-//     * @return 结果
-//     */
-//    @Override
-//    public String importUser(List<SysUser> userList, Boolean isUpdateSupport, String operName)
-//    {
-//        if (StringUtils.isNull(userList) || userList.size() == 0)
-//        {
-//            throw new CustomException("导入用户数据不能为空!");
-//        }
-//        int successNum = 0;
-//        int failureNum = 0;
-//        StringBuilder successMsg = new StringBuilder();
-//        StringBuilder failureMsg = new StringBuilder();
-//        String password = configService.selectConfigByKey("sys.user.initPassword");
-//        for (SysUser user : userList)
-//        {
-//            try
-//            {
-//                // 验证是否存在这个用户
-//                SysUser u = userMapper.selectUserByUserName(user.getUserName());
-//                if (StringUtils.isNull(u))
-//                {
-//                    user.setPassword(SecurityUtils.encryptPassword(password));
-//                    user.setCreateBy(operName);
-//                    this.insertUser(user);
-//                    successNum++;
-//                    successMsg.append("<br/>" + successNum + "、账号 " + user.getUserName() + " 导入成功");
-//                }
-//                else if (isUpdateSupport)
-//                {
-//                    user.setUpdateBy(operName);
-//                    this.updateUser(user);
-//                    successNum++;
-//                    successMsg.append("<br/>" + successNum + "、账号 " + user.getUserName() + " 更新成功");
-//                }
-//                else
-//                {
-//                    failureNum++;
-//                    failureMsg.append("<br/>" + failureNum + "、账号 " + user.getUserName() + " 已存在");
-//                }
-//            }
-//            catch (Exception e)
-//            {
-//                failureNum++;
-//                String msg = "<br/>" + failureNum + "、账号 " + user.getUserName() + " 导入失败:";
-//                failureMsg.append(msg + e.getMessage());
-//                log.error(msg, e);
-//            }
-//        }
-//        if (failureNum > 0)
-//        {
-//            failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
-//            throw new CustomException(failureMsg.toString());
-//        }
-//        else
-//        {
-//            successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
-//        }
-//        return successMsg.toString();
-//    }
+    @Override
+    @Transactional // 添加事务,异常则回滚所有导入
+    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();
+        importUsers.forEach(importUser -> {
+            // 校验,判断是否有不符合的原因
+            try {
+                checkCreateOrUpdate(null, null, importUser.getMobile(), importUser.getEmail(),
+                        importUser.getDeptId(), null);
+            } catch (ServiceException ex) {
+                respVO.getFailureUsernames().put(importUser.getUsername(), ex.getMessage());
+                return;
+            }
+            // 判断如果不存在,在进行插入
+            SysUserDO existUser = userMapper.selectByUsername(importUser.getUsername());
+            if (existUser == null) {
+                // TODO 芋艿:初始密码
+                userMapper.insert(SysUserConvert.INSTANCE.convert(importUser));
+                respVO.getCreateUsernames().add(importUser.getUsername());
+                return;
+            }
+            // 如果存在,判断是否允许更新
+            if (!isUpdateSupport) {
+                respVO.getFailureUsernames().put(importUser.getUsername(), USER_USERNAME_EXISTS.getMessage());
+                return;
+            }
+            SysUserDO updateUser = SysUserConvert.INSTANCE.convert(importUser);
+            updateUser.setId(existUser.getId());
+            userMapper.updateById(updateUser);
+            respVO.getUpdateUsernames().add(importUser.getUsername());
+        });
+        return respVO;
+    }
 
 }