gaoluyang
3 小时以前 6de15528a3ba8f9c87d9aaffe895e69fdcdc9026
fix(system): 修复用户导入「覆盖」分支不可达,导入测试对齐新模板列

- 导入时先查账号是否存在,再按新建/更新两种模式校验唯一性,
避免勾选「是否覆盖」后仍被自己的账号校验拦下(更新分支形同失效)
- 单测:部门/岗位改为填名称(校验名称→编号翻译)、密码留空走系统初始密码,
并断言岗位关联写入、覆盖不改密码
已修改2个文件
130 ■■■■■ 文件已修改
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImplTest.java 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java
@@ -542,13 +542,13 @@
                UserSaveReqVO saveReqVO = BeanUtils.toBean(importUser, UserSaveReqVO.class)
                        .setUsername(username).setDeptId(deptId).setPostIds(postIds).setPassword(password);
                validateUserImportRow(saveReqVO, password);
                // 2.1.3 校验用户账号、手机号、邮箱唯一,以及部门、岗位可用
                validateUserForCreateOrUpdate(null, username, saveReqVO.getMobile(), saveReqVO.getEmail(),
                        deptId, postIds);
                // 2.2.1 判断如果不存在,在进行插入
                // 2.2.1 判断如果不存在,进行插入
                AdminUserDO existUser = userMapper.selectByUsername(username);
                if (existUser == null) {
                    // 2.2.2 校验用户账号、手机号、邮箱唯一,以及部门、岗位可用
                    validateUserForCreateOrUpdate(null, username, saveReqVO.getMobile(), saveReqVO.getEmail(),
                            deptId, postIds);
                    AdminUserDO user = BeanUtils.toBean(saveReqVO, AdminUserDO.class)
                            .setPassword(encodePassword(password))
                            .setStatus(ObjUtil.defaultIfNull(importUser.getStatus(),
@@ -558,11 +558,14 @@
                    respVO.getCreateUsernames().add(username);
                    return;
                }
                // 2.2.2 如果存在,判断是否允许更新
                // 2.2.3 如果存在,判断是否允许更新
                if (!isUpdateSupport) {
                    throw new ServiceException(USER_USERNAME_EXISTS.getCode(),
                            "用户账号已存在。如需覆盖这个用户,请勾选「是否覆盖」后重新导入;否则请修改 Excel 中的用户账号");
                }
                // 2.2.4 覆盖时按更新校验(带上用户编号,唯一性校验会跳过这个用户自己)
                validateUserForCreateOrUpdate(existUser.getId(), username, saveReqVO.getMobile(),
                        saveReqVO.getEmail(), deptId, postIds);
                AdminUserDO updateUser = BeanUtils.toBean(saveReqVO, AdminUserDO.class)
                        .setId(existUser.getId()).setPassword(null) // 覆盖时不改密码,避免把在用账号的密码改掉
                        .setStatus(importUser.getStatus()); //「账号状态」留空时保持原状态
yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImplTest.java
@@ -9,6 +9,7 @@
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.infra.api.config.ConfigApi;
import cn.iocoder.yudao.module.infra.api.file.FileApi;
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserImportExcelVO;
@@ -37,6 +38,7 @@
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -417,20 +419,20 @@
    @Test
    public void testImportUserList_01() {
        // 准备参数
        UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> {
            o.setEmail(randomEmail());
            o.setMobile(randomMobile());
        });
        UserImportExcelVO importUser = randomImportUser(o -> o.setUsername(randomString()));
        // mock 部门、岗位,让「部门」「岗位」两列能解析到
        mockDeptAndPostList("研发部", "普通员工");
        // mock 方法,模拟失败
        doThrow(new ServiceException(DEPT_NOT_FOUND)).when(deptService).validateDeptList(any());
        // 调用
        UserImportRespVO respVO = userService.importUserList(newArrayList(importUser), true);
        // 断言
        // 断言:失败明细的 key 定位到具体行
        assertEquals(0, respVO.getCreateUsernames().size());
        assertEquals(0, respVO.getUpdateUsernames().size());
        assertEquals(1, respVO.getFailureUsernames().size());
        assertEquals(DEPT_NOT_FOUND.getMsg(), respVO.getFailureUsernames().get(importUser.getUsername()));
        assertEquals(DEPT_NOT_FOUND.getMsg(),
                respVO.getFailureUsernames().get(buildRowKey(1, importUser.getUsername())));
    }
    /**
@@ -439,18 +441,9 @@
    @Test
    public void testImportUserList_02() {
        // 准备参数
        UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> {
            o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
            o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围
            o.setEmail(randomEmail());
            o.setMobile(randomMobile());
        });
        // mock deptService 的方法
        DeptDO dept = randomPojo(DeptDO.class, o -> {
            o.setId(importUser.getDeptId());
            o.setStatus(CommonStatusEnum.ENABLE.getStatus());
        });
        when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
        UserImportExcelVO importUser = randomImportUser(o -> o.setUsername(randomString()));
        // mock 部门、岗位
        Long deptId = mockDeptAndPostList("研发部", "普通员工");
        // mock passwordEncoder 的方法
        when(passwordEncoder.encode(eq("yudaoyuanma"))).thenReturn("java");
@@ -459,8 +452,10 @@
        // 断言
        assertEquals(1, respVO.getCreateUsernames().size());
        AdminUserDO user = userMapper.selectByUsername(respVO.getCreateUsernames().get(0));
        assertPojoEquals(importUser, user);
        assertPojoEquals(importUser, user, "dept", "post", "password"); // dept、post 在库里存的是编号
        assertEquals("java", user.getPassword());
        assertEquals(deptId, user.getDeptId()); //「部门」列填的名称翻译成了部门编号
        assertEquals(1, userPostMapper.selectListByUserId(user.getId()).size()); //「岗位」列同步写入了用户-岗位关联
        assertEquals(0, respVO.getUpdateUsernames().size());
        assertEquals(0, respVO.getFailureUsernames().size());
    }
@@ -474,27 +469,20 @@
        AdminUserDO dbUser = randomAdminUserDO();
        userMapper.insert(dbUser);
        // 准备参数
        UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> {
            o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
            o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围
            o.setUsername(dbUser.getUsername());
            o.setEmail(randomEmail());
            o.setMobile(randomMobile());
        });
        // mock deptService 的方法
        DeptDO dept = randomPojo(DeptDO.class, o -> {
            o.setId(importUser.getDeptId());
            o.setStatus(CommonStatusEnum.ENABLE.getStatus());
        });
        when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
        UserImportExcelVO importUser = randomImportUser(o -> o.setUsername(dbUser.getUsername()));
        // mock 部门、岗位
        mockDeptAndPostList("研发部", "普通员工");
        // 调用
        UserImportRespVO respVO = userService.importUserList(newArrayList(importUser), false);
        // 断言
        // 断言:提示里要告诉用户怎么改(勾选覆盖或换账号)
        assertEquals(0, respVO.getCreateUsernames().size());
        assertEquals(0, respVO.getUpdateUsernames().size());
        assertEquals(1, respVO.getFailureUsernames().size());
        assertEquals(USER_USERNAME_EXISTS.getMsg(), respVO.getFailureUsernames().get(importUser.getUsername()));
        String failureMessage = respVO.getFailureUsernames().get(buildRowKey(1, dbUser.getUsername()));
        assertNotNull(failureMessage);
        assertTrue(failureMessage.startsWith("用户账号已存在"));
        assertTrue(failureMessage.contains("是否覆盖"));
    }
    /**
@@ -506,19 +494,9 @@
        AdminUserDO dbUser = randomAdminUserDO();
        userMapper.insert(dbUser);
        // 准备参数
        UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> {
            o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
            o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围
            o.setUsername(dbUser.getUsername());
            o.setEmail(randomEmail());
            o.setMobile(randomMobile());
        });
        // mock deptService 的方法
        DeptDO dept = randomPojo(DeptDO.class, o -> {
            o.setId(importUser.getDeptId());
            o.setStatus(CommonStatusEnum.ENABLE.getStatus());
        });
        when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
        UserImportExcelVO importUser = randomImportUser(o -> o.setUsername(dbUser.getUsername()));
        // mock 部门、岗位
        Long deptId = mockDeptAndPostList("研发部", "普通员工");
        // 调用
        UserImportRespVO respVO = userService.importUserList(newArrayList(importUser), true);
@@ -526,10 +504,53 @@
        assertEquals(0, respVO.getCreateUsernames().size());
        assertEquals(1, respVO.getUpdateUsernames().size());
        AdminUserDO user = userMapper.selectByUsername(respVO.getUpdateUsernames().get(0));
        assertPojoEquals(importUser, user);
        assertPojoEquals(importUser, user, "dept", "post", "password"); // dept、post 在库里存的是编号
        assertEquals(deptId, user.getDeptId());
        assertEquals(dbUser.getPassword(), user.getPassword()); // 覆盖时不能把在用账号的密码改掉
        assertEquals(1, userPostMapper.selectListByUserId(user.getId()).size());
        assertEquals(0, respVO.getFailureUsernames().size());
    }
    /**
     * 生成一个「部门、岗位、密码」都填好的导入行:
     * 部门、岗位填的是名称(用于校验名称翻译成编号),密码留空(用于校验走系统配置的初始密码)
     */
    private UserImportExcelVO randomImportUser(Consumer<UserImportExcelVO> consumer) {
        return randomPojo(UserImportExcelVO.class, o -> {
            o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
            o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围
            o.setEmail(randomEmail());
            o.setMobile(randomMobile());
            o.setDept("研发部");
            o.setPost("普通员工");
            o.setPassword(null);
            consumer.accept(o);
        });
    }
    /** mock 部门、岗位的列表查询,让「部门」「岗位」两列填写的名称能解析到;返回部门的编号 */
    private Long mockDeptAndPostList(String deptName, String... postNames) {
        DeptDO dept = randomPojo(DeptDO.class, o -> {
            o.setName(deptName);
            o.setStatus(CommonStatusEnum.ENABLE.getStatus());
        });
        when(deptService.getDeptList(any(DeptListReqVO.class))).thenReturn(newArrayList(dept));
        List<PostDO> posts = new ArrayList<>();
        for (String postName : postNames) {
            posts.add(randomPojo(PostDO.class, o -> {
                o.setName(postName);
                o.setStatus(CommonStatusEnum.ENABLE.getStatus());
            }));
        }
        when(postService.getPostList(any(), any())).thenReturn(posts);
        return dept.getId();
    }
    /** 失败明细的 key:与 AdminUserServiceImpl#importUserList 里拼的行号文案保持一致 */
    private String buildRowKey(int rowIndex, String username) {
        return "第 " + rowIndex + " 行「" + username + "」";
    }
    @Test
    public void testValidateUserExists_notExists() {
        assertServiceException(() -> userService.validateUserExists(randomLongId()), USER_NOT_EXISTS);