1. 新增用户与部门,一对多的关系;
2. 新增管理多部门用户,如果有为公司的多个部门可以进行选择登录(选择后,直到下次变更访问公司前,只能访问此次选择公的业务数据,使用 company_id 控制,后续补充此数据权限的实现);
3. sql 转化工具修复,现在可以正确的对 mysql 进行不同数据库实例的转化了;
4. 所有表格主键,修改为分布式 Id 实现;
5. 补全在初始版本中没有被纳入的其他预制功能模块
This commit is contained in:
Administrator
2025-07-01 07:30:25 +00:00
parent 84ddc8ca6e
commit 06b278563e
1254 changed files with 85893 additions and 1524 deletions

View File

@@ -81,9 +81,8 @@ public class OAuth2TokenServiceImplTest extends BaseDbAndRedisUnitTest {
assertPojoEquals(accessTokenDO, dbAccessTokenDO, "expiresTime", "createTime", "updateTime", "deleted");
assertEquals(userId, accessTokenDO.getUserId());
assertEquals(userType, accessTokenDO.getUserType());
assertEquals(3, accessTokenDO.getUserInfo().size());
assertEquals(2, accessTokenDO.getUserInfo().size());
assertEquals(user.getNickname(), accessTokenDO.getUserInfo().get("nickname"));
assertEquals(user.getDeptId().toString(), accessTokenDO.getUserInfo().get("deptId"));
assertEquals(clientId, accessTokenDO.getClientId());
assertEquals(scopes, accessTokenDO.getScopes());
assertFalse(DateUtils.isExpired(accessTokenDO.getExpiresTime()));

View File

@@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper;
import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum;
import cn.iocoder.yudao.module.system.service.dept.DeptService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import cn.iocoder.yudao.module.system.service.userdept.UserDeptService;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
@@ -43,6 +44,8 @@ public class PermissionServiceTest extends BaseDbUnitTest {
@MockBean
private MenuService menuService;
@MockBean
private UserDeptService userDeptService;
@MockBean
private DeptService deptService;
@MockBean
private AdminUserService userService;

View File

@@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.system.service.user;
import cn.hutool.core.util.RandomUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
@@ -11,8 +10,6 @@ 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.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;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserImportRespVO;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
@@ -20,13 +17,16 @@ import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.UserPostDO;
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.dataobject.userdept.UserDeptDO;
import cn.iocoder.yudao.module.system.dal.mysql.dept.UserPostMapper;
import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper;
import cn.iocoder.yudao.module.system.dal.mysql.userdept.UserDeptMapper;
import cn.iocoder.yudao.module.system.enums.common.SexEnum;
import cn.iocoder.yudao.module.system.service.dept.DeptService;
import cn.iocoder.yudao.module.system.service.dept.PostService;
import cn.iocoder.yudao.module.system.service.permission.PermissionService;
import cn.iocoder.yudao.module.system.service.tenant.TenantService;
import cn.iocoder.yudao.module.system.service.userdept.UserDeptServiceImpl;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -35,9 +35,7 @@ import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.security.crypto.password.PasswordEncoder;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.function.Consumer;
import static cn.hutool.core.util.RandomUtil.randomEle;
@@ -58,7 +56,7 @@ import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
@Import(AdminUserServiceImpl.class)
@Import({AdminUserServiceImpl.class,UserDeptServiceImpl.class})
public class AdminUserServiceImplTest extends BaseDbUnitTest {
@Resource
@@ -68,6 +66,10 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
private AdminUserMapper userMapper;
@Resource
private UserPostMapper userPostMapper;
@Resource
private UserDeptMapper userDeptMapper;
@Resource
private UserDeptServiceImpl userDeptService;
@MockBean
private DeptService deptService;
@@ -104,12 +106,6 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
handler.handle(tenant);
return true;
}));
// mock deptService 的方法
DeptDO dept = randomPojo(DeptDO.class, o -> {
o.setId(reqVO.getDeptId());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
// mock postService 的方法
List<PostDO> posts = CollectionUtils.convertList(reqVO.getPostIds(), postId ->
randomPojo(PostDO.class, o -> {
@@ -124,7 +120,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
Long userId = userService.createUser(reqVO);
// 断言
AdminUserDO user = userMapper.selectById(userId);
assertPojoEquals(reqVO, user, "password", "id");
assertPojoEquals(reqVO, user, "password", "id","deptIds");
assertEquals("yudaoyuanma", user.getPassword());
assertEquals(CommonStatusEnum.ENABLE.getStatus(), user.getStatus());
// 断言关联岗位
@@ -162,12 +158,6 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
o.setMobile(randomString());
o.setPostIds(asSet(2L, 3L));
});
// mock deptService 的方法
DeptDO dept = randomPojo(DeptDO.class, o -> {
o.setId(reqVO.getDeptId());
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
});
when(deptService.getDept(eq(dept.getId()))).thenReturn(dept);
// mock postService 的方法
List<PostDO> posts = CollectionUtils.convertList(reqVO.getPostIds(), postId ->
randomPojo(PostDO.class, o -> {
@@ -180,7 +170,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
userService.updateUser(reqVO);
// 断言
AdminUserDO user = userMapper.selectById(reqVO.getId());
assertPojoEquals(reqVO, user, "password");
assertPojoEquals(reqVO, user, "password","deptIds");
// 断言关联岗位
List<UserPostDO> userPosts = userPostMapper.selectListByUserId(user.getId());
assertEquals(2L, userPosts.get(0).getPostId());
@@ -309,7 +299,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
// 调用
AdminUserDO user = userService.getUserByUsername(username);
// 断言
assertPojoEquals(dbUser, user);
assertPojoEquals(dbUser, user,"deptIds");
}
@Test
@@ -323,7 +313,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
// 调用
AdminUserDO user = userService.getUserByMobile(mobile);
// 断言
assertPojoEquals(dbUser, user);
assertPojoEquals(dbUser, user,"deptIds");
}
@Test
@@ -336,17 +326,19 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
reqVO.setMobile("1560");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setCreateTime(buildBetweenTime(2020, 12, 1, 2020, 12, 24));
reqVO.setDeptId(1L); // 其中1L 是 2L 的父部门
// page查询暂时不支持带部门进行查询
// reqVO.setDeptId(1L); // 其中1L 是 2L 的父部门
// mock 方法
List<DeptDO> deptList = newArrayList(randomPojo(DeptDO.class, o -> o.setId(2L)));
when(deptService.getChildDeptList(eq(reqVO.getDeptId()))).thenReturn(deptList);
// 新增 1L 和用户关联关系
userDeptMapper.insert(new UserDeptDO().setUserId(dbUser.getId()).setDeptId(1L));
// 调用
PageResult<AdminUserDO> pageResult = userService.getUserPage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbUser, pageResult.getList().get(0));
assertPojoEquals(dbUser, pageResult.getList().get(0), "deptIds");
}
/**
@@ -359,7 +351,6 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
o.setMobile("15601691300");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setCreateTime(buildTime(2020, 12, 12));
o.setDeptId(2L);
});
userMapper.insert(dbUser);
// 测试 username 不匹配
@@ -370,8 +361,6 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
userMapper.insert(cloneIgnoreId(dbUser, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 createTime 不匹配
userMapper.insert(cloneIgnoreId(dbUser, o -> o.setCreateTime(buildTime(2020, 11, 11))));
// 测试 dept 不匹配
userMapper.insert(cloneIgnoreId(dbUser, o -> o.setDeptId(0L)));
return dbUser;
}
@@ -382,20 +371,25 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
userMapper.insert(dbUser);
// 准备参数
Long userId = dbUser.getId();
// 新增用户 部门 关系
userDeptMapper.insert(new UserDeptDO().setDeptId(1L).setUserId(userId));
// 调用
AdminUserDO user = userService.getUser(userId);
// 断言
assertPojoEquals(dbUser, user);
assertPojoEquals(dbUser, user, "deptIds");
}
@Test
public void testGetUserListByDeptIds() {
// mock 数据
AdminUserDO dbUser = randomAdminUserDO(o -> o.setDeptId(1L));
AdminUserDO dbUser = randomAdminUserDO();
userMapper.insert(dbUser);
List<UserDeptDO> userDeptList = new ArrayList<>(2);
dbUser.getDeptIds().forEach(o -> userDeptList.add(randomUserDeptDO().setDeptId(o)));
userDeptList.stream().findFirst().ifPresent(o -> o.setUserId(dbUser.getId()));
userDeptMapper.insertBatch(userDeptList);
// 测试 deptId 不匹配
userMapper.insert(cloneIgnoreId(dbUser, o -> o.setDeptId(2L)));
userMapper.insert(cloneIgnoreId(dbUser, o -> o.setDeptIds(new HashSet<>())));
// 准备参数
Collection<Long> deptIds = singleton(1L);
@@ -403,126 +397,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
List<AdminUserDO> list = userService.getUserListByDeptIds(deptIds);
// 断言
assertEquals(1, list.size());
assertEquals(dbUser, list.get(0));
}
/**
* 情况一,校验不通过,导致插入失败
*/
@Test
public void testImportUserList_01() {
// 准备参数
UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> {
o.setEmail(randomEmail());
o.setMobile(randomMobile());
});
// mock 方法,模拟失败
doThrow(new ServiceException(DEPT_NOT_FOUND)).when(deptService).validateDeptList(any());
// 调用
UserImportRespVO respVO = userService.importUserList(newArrayList(importUser), true);
// 断言
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()));
}
/**
* 情况二,不存在,进行插入
*/
@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);
// mock passwordEncoder 的方法
when(passwordEncoder.encode(eq("yudaoyuanma"))).thenReturn("java");
// 调用
UserImportRespVO respVO = userService.importUserList(newArrayList(importUser), true);
// 断言
assertEquals(1, respVO.getCreateUsernames().size());
AdminUserDO user = userMapper.selectByUsername(respVO.getCreateUsernames().get(0));
assertPojoEquals(importUser, user);
assertEquals("java", user.getPassword());
assertEquals(0, respVO.getUpdateUsernames().size());
assertEquals(0, respVO.getFailureUsernames().size());
}
/**
* 情况三,存在,但是不强制更新
*/
@Test
public void testImportUserList_03() {
// mock 数据
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);
// 调用
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()));
}
/**
* 情况四,存在,强制更新
*/
@Test
public void testImportUserList_04() {
// mock 数据
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);
// 调用
UserImportRespVO respVO = userService.importUserList(newArrayList(importUser), true);
// 断言
assertEquals(0, respVO.getCreateUsernames().size());
assertEquals(1, respVO.getUpdateUsernames().size());
AdminUserDO user = userMapper.selectByUsername(respVO.getUpdateUsernames().get(0));
assertPojoEquals(importUser, user);
assertEquals(0, respVO.getFailureUsernames().size());
assertPojoEquals(dbUser, list.get(0), "deptIds");
}
@Test
@@ -632,12 +507,12 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
// 准备参数
Collection<Long> postIds = asSet(10L, 20L);
// mock user1 数据
AdminUserDO user1 = randomAdminUserDO(o -> o.setPostIds(asSet(10L, 30L)));
AdminUserDO user1 = randomAdminUserDO(o -> o.setPostIds(asSet(10L, 30L)).setDeptIds(new HashSet<>()));
userMapper.insert(user1);
userPostMapper.insert(new UserPostDO().setUserId(user1.getId()).setPostId(10L));
userPostMapper.insert(new UserPostDO().setUserId(user1.getId()).setPostId(30L));
// mock user2 数据
AdminUserDO user2 = randomAdminUserDO(o -> o.setPostIds(singleton(100L)));
AdminUserDO user2 = randomAdminUserDO(o -> o.setPostIds(singleton(100L)).setDeptIds(new HashSet<>()));
userMapper.insert(user2);
userPostMapper.insert(new UserPostDO().setUserId(user2.getId()).setPostId(100L));
@@ -645,7 +520,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
List<AdminUserDO> result = userService.getUserListByPostIds(postIds);
// 断言
assertEquals(1, result.size());
assertEquals(user1, result.get(0));
assertPojoEquals(user1, result.get(0), "deptIds");
}
@Test
@@ -656,13 +531,15 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
// 测试 id 不匹配
userMapper.insert(randomAdminUserDO());
// 准备参数
Collection<Long> ids = singleton(user.getId());
Long id = user.getId();
Collection<Long> ids = singleton(id);
// 新增 1L 和用户关联关系
userDeptMapper.insert(new UserDeptDO().setUserId(id).setDeptId(1L));
// 调用
List<AdminUserDO> result = userService.getUserList(ids);
// 断言
assertEquals(1, result.size());
assertEquals(user, result.get(0));
assertPojoEquals(user, result.get(0), "deptIds");
}
@Test
@@ -674,12 +551,14 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
userMapper.insert(randomAdminUserDO());
// 准备参数
Collection<Long> ids = singleton(user.getId());
// 新增 1L 和用户关联关系
userDeptMapper.insert(new UserDeptDO().setUserId(user.getId()).setDeptId(1L));
// 调用
Map<Long, AdminUserDO> result = userService.getUserMap(ids);
// 断言
assertEquals(1, result.size());
assertEquals(user, result.get(user.getId()));
assertPojoEquals(user, result.get(user.getId()), "deptIds");
}
@Test
@@ -696,7 +575,7 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
List<AdminUserDO> result = userService.getUserListByNickname(nickname);
// 断言
assertEquals(1, result.size());
assertEquals(user, result.get(0));
assertPojoEquals(user, result.get(0),"deptIds");
}
@Test
@@ -705,15 +584,19 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
AdminUserDO user = randomAdminUserDO(o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()));
userMapper.insert(user);
// 测试 status 不匹配
userMapper.insert(randomAdminUserDO(o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())));
AdminUserDO user2 = randomAdminUserDO(o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()));
userMapper.insert(user2);
// 准备参数
Integer status = CommonStatusEnum.DISABLE.getStatus();
// 新增 1L 和用户关联关系 未关联 部门的用户无法被正确查询
userDeptMapper.insert(new UserDeptDO().setUserId(user2.getId()).setDeptId(1L));
userDeptMapper.insert(new UserDeptDO().setUserId(user.getId()).setDeptId(1L));
// 调用
List<AdminUserDO> result = userService.getUserListByStatus(status);
// 断言
assertEquals(1, result.size());
assertEquals(user, result.get(0));
AdminUserDO user1 = userService.getUser(result.get(0).getId());
assertPojoEquals(user, user1, "deptIds");
}
@Test
@@ -756,9 +639,16 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest {
private static AdminUserDO randomAdminUserDO(Consumer<AdminUserDO>... consumers) {
Consumer<AdminUserDO> consumer = (o) -> {
o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围
o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围
o.setSex(randomEle(SexEnum.values()).getSex());
o.setDeptIds(new HashSet<>(asSet(1L, 2L))); // 保证 deptIds 的范围
};
return randomPojo(AdminUserDO.class, ArrayUtils.append(consumer, consumers));
}
@SafeVarargs
private static UserDeptDO randomUserDeptDO(Consumer<UserDeptDO>... consumers) {
Consumer<UserDeptDO> consumer = (o) -> {};
return randomPojo(UserDeptDO.class, ArrayUtils.append(consumer, consumers));
}
}

View File

@@ -1,3 +1,15 @@
create table IF NOT EXISTS system_user_dept (
id bigint primary key, -- 自增主键
user_id bigint not null,
dept_id bigint not null,
tenant_id bigint default 0 not null,
remark varchar(2000),
creator varchar(256) default '',
create_time timestamp default current_timestamp not null,
updater varchar(256) default '',
deleted tinyint default 0 not null,
update_time timestamp default current_timestamp not null
);
CREATE TABLE IF NOT EXISTS "system_dept" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar(30) NOT NULL DEFAULT '',
@@ -14,6 +26,8 @@ CREATE TABLE IF NOT EXISTS "system_dept" (
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint not null default '0',
"is_tenant" bit NOT NULL DEFAULT FALSE,
"is_company" bit NOT NULL DEFAULT FALSE,
"is_group" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '部门表';