feat(wm-system): #74 系统管理(角色/用户/部门/日志/字典)
- 增强 SysRole: 5级角色(level 1-5), 数据权限(dataScope), 权限配置(permissions JSON) - 增强 SysUser: 角色关联(roleId), 最后登录记录, 分页查询+多维度筛选 - 增强 SysDepartment: 树结构(ancestors), 子部门查询, 树形构建 - 增强 SysLog: 状态/错误信息字段, 分页+多条件筛选(类型/用户/模块/时间/状态) - 新增 DataDictionary + DataDictionaryItem: 字典CRUD+字典项CRUD, 按编码查询 - 5个专用Controller: RoleController/UserController/DepartmentController/LogController/DictionaryController - 28个RESTful API端点于 /api/system/* - 5个专用Service接口+实现: RoleService/UserService/DepartmentService/LogService/DictionaryService - DDL: V2__system_enhance.sql — 新增表+索引+初始数据 - 单元测试: 5个Service测试类, 覆盖核心业务逻辑 - DTO: 请求参数校验(jakarta.validation) - 保持SysService向后兼容
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
package com.water.system.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.water.common.core.exception.BusinessException;
|
||||
import com.water.system.entity.SysDepartment;
|
||||
import com.water.system.entity.dto.DeptRequest;
|
||||
import com.water.system.mapper.SysDepartmentMapper;
|
||||
import com.water.system.service.impl.DepartmentServiceImpl;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class DepartmentServiceImplTest {
|
||||
|
||||
@Mock
|
||||
private SysDepartmentMapper deptMapper;
|
||||
|
||||
@InjectMocks
|
||||
private DepartmentServiceImpl departmentService;
|
||||
|
||||
@Test
|
||||
void getDeptTree_shouldBuildTree() {
|
||||
SysDepartment root = new SysDepartment();
|
||||
root.setId(1L);
|
||||
root.setParentId(0L);
|
||||
root.setDeptName("总公司");
|
||||
|
||||
SysDepartment child1 = new SysDepartment();
|
||||
child1.setId(2L);
|
||||
child1.setParentId(1L);
|
||||
child1.setDeptName("技术部");
|
||||
|
||||
SysDepartment child2 = new SysDepartment();
|
||||
child2.setId(3L);
|
||||
child2.setParentId(1L);
|
||||
child2.setDeptName("市场部");
|
||||
|
||||
when(deptMapper.selectList(any(LambdaQueryWrapper.class)))
|
||||
.thenReturn(Arrays.asList(root, child1, child2));
|
||||
|
||||
List<SysDepartment> tree = departmentService.getDeptTree();
|
||||
|
||||
assertEquals(1, tree.size());
|
||||
assertEquals("总公司", tree.get(0).getDeptName());
|
||||
assertEquals(2, tree.get(0).getChildren().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void createDept_shouldSetAncestors() {
|
||||
SysDepartment parent = new SysDepartment();
|
||||
parent.setId(1L);
|
||||
parent.setAncestors("0");
|
||||
parent.setDeptName("总公司");
|
||||
|
||||
when(deptMapper.selectById(1L)).thenReturn(parent);
|
||||
when(deptMapper.insert(any(SysDepartment.class))).thenAnswer(inv -> {
|
||||
SysDepartment d = inv.getArgument(0);
|
||||
d.setId(10L);
|
||||
assertEquals("0,1", d.getAncestors());
|
||||
return 1;
|
||||
});
|
||||
|
||||
DeptRequest request = new DeptRequest();
|
||||
request.setDeptName("研发部");
|
||||
request.setParentId(1L);
|
||||
|
||||
Long id = departmentService.createDept(request);
|
||||
assertEquals(10L, id);
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteDept_shouldFailWithChildren() {
|
||||
when(deptMapper.selectCount(any(LambdaQueryWrapper.class))).thenReturn(2L);
|
||||
|
||||
assertThrows(BusinessException.class, () -> departmentService.deleteDept(1L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteDept_shouldSucceedWithoutChildren() {
|
||||
when(deptMapper.selectCount(any(LambdaQueryWrapper.class))).thenReturn(0L);
|
||||
|
||||
assertDoesNotThrow(() -> departmentService.deleteDept(5L));
|
||||
verify(deptMapper).deleteById(5L);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.water.system.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.water.common.core.exception.BusinessException;
|
||||
import com.water.system.entity.DataDictionary;
|
||||
import com.water.system.entity.DataDictionaryItem;
|
||||
import com.water.system.entity.dto.DictItemRequest;
|
||||
import com.water.system.entity.dto.DictRequest;
|
||||
import com.water.system.mapper.DataDictionaryMapper;
|
||||
import com.water.system.mapper.DataDictionaryItemMapper;
|
||||
import com.water.system.service.impl.DictionaryServiceImpl;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class DictionaryServiceImplTest {
|
||||
|
||||
@Mock
|
||||
private DataDictionaryMapper dictMapper;
|
||||
|
||||
@Mock
|
||||
private DataDictionaryItemMapper itemMapper;
|
||||
|
||||
@InjectMocks
|
||||
private DictionaryServiceImpl dictionaryService;
|
||||
|
||||
@Test
|
||||
void createDictionary_shouldSucceed() {
|
||||
DictRequest request = new DictRequest();
|
||||
request.setDictCode("water_quality_level");
|
||||
request.setDictName("水质等级");
|
||||
|
||||
when(dictMapper.selectByDictCode("water_quality_level")).thenReturn(null);
|
||||
when(dictMapper.insert(any(DataDictionary.class))).thenAnswer(inv -> {
|
||||
DataDictionary d = inv.getArgument(0);
|
||||
d.setId(10L);
|
||||
return 1;
|
||||
});
|
||||
|
||||
Long id = dictionaryService.createDictionary(request);
|
||||
assertEquals(10L, id);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createDictionary_shouldFailOnDuplicate() {
|
||||
DictRequest request = new DictRequest();
|
||||
request.setDictCode("sys_user_status");
|
||||
|
||||
DataDictionary existing = new DataDictionary();
|
||||
existing.setDictCode("sys_user_status");
|
||||
when(dictMapper.selectByDictCode("sys_user_status")).thenReturn(existing);
|
||||
|
||||
assertThrows(BusinessException.class, () -> dictionaryService.createDictionary(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
void listItemsByDictCode_shouldReturnItems() {
|
||||
DataDictionaryItem item1 = new DataDictionaryItem();
|
||||
item1.setItemCode("ENABLED");
|
||||
item1.setItemName("正常");
|
||||
DataDictionaryItem item2 = new DataDictionaryItem();
|
||||
item2.setItemCode("DISABLED");
|
||||
item2.setItemName("停用");
|
||||
|
||||
when(itemMapper.selectByDictCode("sys_user_status")).thenReturn(Arrays.asList(item1, item2));
|
||||
|
||||
List<DataDictionaryItem> items = dictionaryService.listItemsByDictCode("sys_user_status");
|
||||
assertEquals(2, items.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteDictionary_shouldDeleteItemsToo() {
|
||||
dictionaryService.deleteDictionary(1L);
|
||||
|
||||
verify(itemMapper).delete(any(LambdaQueryWrapper.class));
|
||||
verify(dictMapper).deleteById(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createItem_shouldSucceed() {
|
||||
DictItemRequest request = new DictItemRequest();
|
||||
request.setDictId(1L);
|
||||
request.setItemCode("NEW_ITEM");
|
||||
request.setItemName("新项");
|
||||
request.setItemValue("value");
|
||||
|
||||
when(itemMapper.insert(any(DataDictionaryItem.class))).thenAnswer(inv -> {
|
||||
DataDictionaryItem i = inv.getArgument(0);
|
||||
i.setId(20L);
|
||||
return 1;
|
||||
});
|
||||
|
||||
Long id = dictionaryService.createItem(request);
|
||||
assertEquals(20L, id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.water.system.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.water.system.entity.SysLog;
|
||||
import com.water.system.entity.dto.LogQueryRequest;
|
||||
import com.water.system.mapper.SysLogMapper;
|
||||
import com.water.system.service.impl.LogServiceImpl;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class LogServiceImplTest {
|
||||
|
||||
@Mock
|
||||
private SysLogMapper logMapper;
|
||||
|
||||
@InjectMocks
|
||||
private LogServiceImpl logService;
|
||||
|
||||
@Test
|
||||
void pageLogs_shouldReturnPagedResult() {
|
||||
Page<SysLog> page = new Page<>(1, 10);
|
||||
SysLog log1 = new SysLog();
|
||||
log1.setId(1L);
|
||||
log1.setLogType("LOGIN");
|
||||
log1.setUsername("admin");
|
||||
log1.setCreatedTime(LocalDateTime.now());
|
||||
|
||||
SysLog log2 = new SysLog();
|
||||
log2.setId(2L);
|
||||
log2.setLogType("OPERATE");
|
||||
log2.setUsername("user1");
|
||||
log2.setModule("用户管理");
|
||||
|
||||
page.setRecords(Arrays.asList(log1, log2));
|
||||
page.setTotal(2);
|
||||
|
||||
when(logMapper.selectPage(any(Page.class), any(LambdaQueryWrapper.class))).thenReturn(page);
|
||||
|
||||
LogQueryRequest request = new LogQueryRequest();
|
||||
request.setPageNum(1);
|
||||
request.setPageSize(10);
|
||||
|
||||
IPage<SysLog> result = logService.pageLogs(request);
|
||||
|
||||
assertEquals(2, result.getTotal());
|
||||
assertEquals("LOGIN", result.getRecords().get(0).getLogType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void pageLogs_withFilters_shouldApplyAllConditions() {
|
||||
Page<SysLog> page = new Page<>(1, 10);
|
||||
page.setRecords(Arrays.asList());
|
||||
page.setTotal(0);
|
||||
|
||||
when(logMapper.selectPage(any(Page.class), any(LambdaQueryWrapper.class))).thenReturn(page);
|
||||
|
||||
LogQueryRequest request = new LogQueryRequest();
|
||||
request.setLogType("OPERATE");
|
||||
request.setUsername("admin");
|
||||
request.setModule("系统管理");
|
||||
request.setStatus(1);
|
||||
request.setStartTime("2024-01-01");
|
||||
request.setEndTime("2024-12-31");
|
||||
|
||||
IPage<SysLog> result = logService.pageLogs(request);
|
||||
|
||||
assertNotNull(result);
|
||||
verify(logMapper).selectPage(any(Page.class), any(LambdaQueryWrapper.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void saveLog_shouldInsert() {
|
||||
SysLog log = new SysLog();
|
||||
log.setLogType("LOGIN");
|
||||
log.setUsername("testuser");
|
||||
log.setIp("127.0.0.1");
|
||||
|
||||
logService.saveLog(log);
|
||||
|
||||
verify(logMapper).insert(log);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.water.system.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.water.common.core.exception.BusinessException;
|
||||
import com.water.system.entity.SysRole;
|
||||
import com.water.system.entity.dto.RoleRequest;
|
||||
import com.water.system.mapper.SysRoleMapper;
|
||||
import com.water.system.service.impl.RoleServiceImpl;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class RoleServiceImplTest {
|
||||
|
||||
@Mock
|
||||
private SysRoleMapper roleMapper;
|
||||
|
||||
@InjectMocks
|
||||
private RoleServiceImpl roleService;
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
// ObjectMapper is final, inject manually via reflection or just let it be used
|
||||
}
|
||||
|
||||
@Test
|
||||
void listRoles_shouldReturnAllRoles() {
|
||||
SysRole admin = new SysRole();
|
||||
admin.setId(1L);
|
||||
admin.setRoleCode("SUPER_ADMIN");
|
||||
admin.setLevel(1);
|
||||
|
||||
SysRole user = new SysRole();
|
||||
user.setId(4L);
|
||||
user.setRoleCode("EMPLOYEE");
|
||||
user.setLevel(4);
|
||||
|
||||
when(roleMapper.selectList(any(LambdaQueryWrapper.class))).thenReturn(Arrays.asList(admin, user));
|
||||
|
||||
List<SysRole> roles = roleService.listRoles();
|
||||
|
||||
assertEquals(2, roles.size());
|
||||
assertEquals("SUPER_ADMIN", roles.get(0).getRoleCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void createRole_shouldSucceed() {
|
||||
RoleRequest request = new RoleRequest();
|
||||
request.setRoleCode("TEST_ROLE");
|
||||
request.setRoleName("测试角色");
|
||||
request.setLevel(3);
|
||||
request.setPermissions(Arrays.asList("user:read", "user:write"));
|
||||
|
||||
when(roleMapper.selectCount(any(LambdaQueryWrapper.class))).thenReturn(0L);
|
||||
when(roleMapper.insert(any(SysRole.class))).thenAnswer(inv -> {
|
||||
SysRole r = inv.getArgument(0);
|
||||
r.setId(10L);
|
||||
return 1;
|
||||
});
|
||||
|
||||
Long id = roleService.createRole(request);
|
||||
|
||||
assertEquals(10L, id);
|
||||
verify(roleMapper).insert(any(SysRole.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createRole_shouldFailOnDuplicateCode() {
|
||||
RoleRequest request = new RoleRequest();
|
||||
request.setRoleCode("SUPER_ADMIN");
|
||||
request.setRoleName("重复角色");
|
||||
request.setLevel(1);
|
||||
|
||||
when(roleMapper.selectCount(any(LambdaQueryWrapper.class))).thenReturn(1L);
|
||||
|
||||
assertThrows(BusinessException.class, () -> roleService.createRole(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteRole_shouldFailForAdminRoles() {
|
||||
SysRole admin = new SysRole();
|
||||
admin.setId(1L);
|
||||
admin.setLevel(1);
|
||||
admin.setRoleCode("SUPER_ADMIN");
|
||||
|
||||
when(roleMapper.selectById(1L)).thenReturn(admin);
|
||||
|
||||
assertThrows(BusinessException.class, () -> roleService.deleteRole(1L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteRole_shouldSucceedForNormalRoles() {
|
||||
SysRole role = new SysRole();
|
||||
role.setId(5L);
|
||||
role.setLevel(4);
|
||||
role.setRoleCode("EMPLOYEE");
|
||||
|
||||
when(roleMapper.selectById(5L)).thenReturn(role);
|
||||
|
||||
assertDoesNotThrow(() -> roleService.deleteRole(5L));
|
||||
verify(roleMapper).deleteById(5L);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.water.system.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.water.common.core.exception.BusinessException;
|
||||
import com.water.system.entity.SysUser;
|
||||
import com.water.system.entity.dto.UserCreateRequest;
|
||||
import com.water.system.entity.dto.UserUpdateRequest;
|
||||
import com.water.system.mapper.SysUserMapper;
|
||||
import com.water.system.service.impl.UserServiceImpl;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import java.util.Arrays;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class UserServiceImplTest {
|
||||
|
||||
@Mock
|
||||
private SysUserMapper userMapper;
|
||||
|
||||
@InjectMocks
|
||||
private UserServiceImpl userService;
|
||||
|
||||
@Test
|
||||
void createUser_shouldSucceed() {
|
||||
UserCreateRequest request = new UserCreateRequest();
|
||||
request.setUsername("testuser");
|
||||
request.setPassword("test123");
|
||||
request.setRealName("测试用户");
|
||||
request.setPhone("13800138000");
|
||||
|
||||
when(userMapper.selectByUsername("testuser")).thenReturn(null);
|
||||
when(userMapper.insert(any(SysUser.class))).thenAnswer(inv -> {
|
||||
SysUser u = inv.getArgument(0);
|
||||
u.setId(100L);
|
||||
return 1;
|
||||
});
|
||||
|
||||
Long id = userService.createUser(request);
|
||||
|
||||
assertEquals(100L, id);
|
||||
verify(userMapper).insert(any(SysUser.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createUser_shouldFailOnDuplicateUsername() {
|
||||
UserCreateRequest request = new UserCreateRequest();
|
||||
request.setUsername("admin");
|
||||
request.setPassword("pass");
|
||||
|
||||
SysUser existing = new SysUser();
|
||||
existing.setUsername("admin");
|
||||
when(userMapper.selectByUsername("admin")).thenReturn(existing);
|
||||
|
||||
assertThrows(BusinessException.class, () -> userService.createUser(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateStatus_shouldSucceed() {
|
||||
SysUser user = new SysUser();
|
||||
user.setId(1L);
|
||||
user.setStatus(1);
|
||||
when(userMapper.selectById(1L)).thenReturn(user);
|
||||
|
||||
userService.updateStatus(1L, 0);
|
||||
|
||||
verify(userMapper).updateById(argThat(u -> u.getStatus() == 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteUser_shouldFailForAdmin() {
|
||||
SysUser admin = new SysUser();
|
||||
admin.setId(1L);
|
||||
admin.setUsername("admin");
|
||||
when(userMapper.selectById(1L)).thenReturn(admin);
|
||||
|
||||
assertThrows(BusinessException.class, () -> userService.deleteUser(1L));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resetPassword_shouldSucceed() {
|
||||
SysUser user = new SysUser();
|
||||
user.setId(5L);
|
||||
user.setUsername("testuser");
|
||||
when(userMapper.selectById(5L)).thenReturn(user);
|
||||
|
||||
assertDoesNotThrow(() -> userService.resetPassword(5L, "newPass123"));
|
||||
verify(userMapper).updateById(argThat(u -> u.getPassword() != null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void pageUsers_shouldReturnPagedResult() {
|
||||
Page<SysUser> page = new Page<>(1, 10);
|
||||
SysUser user1 = new SysUser();
|
||||
user1.setId(1L);
|
||||
user1.setUsername("user1");
|
||||
user1.setPassword("hashed");
|
||||
SysUser user2 = new SysUser();
|
||||
user2.setId(2L);
|
||||
user2.setUsername("user2");
|
||||
user2.setPassword("hashed");
|
||||
page.setRecords(Arrays.asList(user1, user2));
|
||||
page.setTotal(2);
|
||||
|
||||
when(userMapper.selectPage(any(Page.class), any(LambdaQueryWrapper.class))).thenReturn(page);
|
||||
|
||||
IPage<SysUser> result = userService.pageUsers(1, 10, null, null, null);
|
||||
|
||||
assertEquals(2, result.getTotal());
|
||||
// 密码应被清除
|
||||
assertNull(result.getRecords().get(0).getPassword());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user