Commit b27400afdbfcde2a5f56337ad9df17ed8567f51f
1 parent
0f608dff
feat(usr): createUser 外键校验 (40004/40005) REQ-USR-001
Showing
2 changed files
with
108 additions
and
1 deletions
backend/src/main/java/com/xly/test4/module/usr/service/impl/UserServiceImpl.java
| ... | ... | @@ -7,7 +7,11 @@ import com.xly.test4.common.security.CurrentUser; |
| 7 | 7 | import com.xly.test4.common.security.CurrentUserContext; |
| 8 | 8 | import com.xly.test4.module.usr.converter.UserConverter; |
| 9 | 9 | import com.xly.test4.module.usr.dto.UserCreateDTO; |
| 10 | +import com.xly.test4.module.usr.entity.Employee; | |
| 11 | +import com.xly.test4.module.usr.entity.Permission; | |
| 10 | 12 | import com.xly.test4.module.usr.entity.User; |
| 13 | +import com.xly.test4.module.usr.mapper.EmployeeMapper; | |
| 14 | +import com.xly.test4.module.usr.mapper.PermissionMapper; | |
| 11 | 15 | import com.xly.test4.module.usr.mapper.UserMapper; |
| 12 | 16 | import com.xly.test4.module.usr.service.UserService; |
| 13 | 17 | import com.xly.test4.module.usr.vo.UserCreateVO; |
| ... | ... | @@ -16,19 +20,27 @@ import org.springframework.security.crypto.password.PasswordEncoder; |
| 16 | 20 | import org.springframework.stereotype.Service; |
| 17 | 21 | import org.springframework.transaction.annotation.Transactional; |
| 18 | 22 | |
| 23 | +import java.util.List; | |
| 24 | + | |
| 19 | 25 | @Service |
| 20 | 26 | public class UserServiceImpl implements UserService { |
| 21 | 27 | |
| 22 | 28 | private final UserMapper userMapper; |
| 29 | + private final EmployeeMapper employeeMapper; | |
| 30 | + private final PermissionMapper permissionMapper; | |
| 23 | 31 | private final UserConverter userConverter; |
| 24 | 32 | private final PasswordEncoder passwordEncoder; |
| 25 | 33 | private final String defaultPassword; |
| 26 | 34 | |
| 27 | 35 | public UserServiceImpl(UserMapper userMapper, |
| 36 | + EmployeeMapper employeeMapper, | |
| 37 | + PermissionMapper permissionMapper, | |
| 28 | 38 | UserConverter userConverter, |
| 29 | 39 | PasswordEncoder passwordEncoder, |
| 30 | 40 | @Value("${app.security.default-password}") String defaultPassword) { |
| 31 | 41 | this.userMapper = userMapper; |
| 42 | + this.employeeMapper = employeeMapper; | |
| 43 | + this.permissionMapper = permissionMapper; | |
| 32 | 44 | this.userConverter = userConverter; |
| 33 | 45 | this.passwordEncoder = passwordEncoder; |
| 34 | 46 | this.defaultPassword = defaultPassword; |
| ... | ... | @@ -48,6 +60,24 @@ public class UserServiceImpl implements UserService { |
| 48 | 60 | throw new BusinessException(ResultCode.USER_CODE_DUPLICATE, "用户号已存在"); |
| 49 | 61 | } |
| 50 | 62 | |
| 63 | + if (dto.getEmployeeId() != null) { | |
| 64 | + Employee employee = employeeMapper.selectOne(new LambdaQueryWrapper<Employee>() | |
| 65 | + .eq(Employee::getIIncrement, dto.getEmployeeId()) | |
| 66 | + .eq(Employee::getIIsDisabled, 0)); | |
| 67 | + if (employee == null) { | |
| 68 | + throw new BusinessException(ResultCode.EMPLOYEE_INVALID, "员工不存在或已作废"); | |
| 69 | + } | |
| 70 | + } | |
| 71 | + | |
| 72 | + if (dto.getPermissionIds() != null && !dto.getPermissionIds().isEmpty()) { | |
| 73 | + List<Permission> found = permissionMapper.selectList(new LambdaQueryWrapper<Permission>() | |
| 74 | + .in(Permission::getIIncrement, dto.getPermissionIds()) | |
| 75 | + .eq(Permission::getIIsDisabled, 0)); | |
| 76 | + if (found.size() != dto.getPermissionIds().size()) { | |
| 77 | + throw new BusinessException(ResultCode.PERMISSION_INVALID, "权限分类含非法项"); | |
| 78 | + } | |
| 79 | + } | |
| 80 | + | |
| 51 | 81 | User user = userConverter.toEntity(dto); |
| 52 | 82 | user.setSBrandsId(current.getBrandsId()); |
| 53 | 83 | user.setSSubsidiaryId(current.getSubsidiaryId()); | ... | ... |
backend/src/test/java/com/xly/test4/module/usr/service/impl/UserServiceImplTest.java
| ... | ... | @@ -6,7 +6,11 @@ import com.xly.test4.common.security.CurrentUser; |
| 6 | 6 | import com.xly.test4.common.security.CurrentUserContext; |
| 7 | 7 | import com.xly.test4.module.usr.converter.UserConverter; |
| 8 | 8 | import com.xly.test4.module.usr.dto.UserCreateDTO; |
| 9 | +import com.xly.test4.module.usr.entity.Employee; | |
| 10 | +import com.xly.test4.module.usr.entity.Permission; | |
| 9 | 11 | import com.xly.test4.module.usr.entity.User; |
| 12 | +import com.xly.test4.module.usr.mapper.EmployeeMapper; | |
| 13 | +import com.xly.test4.module.usr.mapper.PermissionMapper; | |
| 10 | 14 | import com.xly.test4.module.usr.mapper.UserMapper; |
| 11 | 15 | import com.xly.test4.module.usr.vo.UserCreateVO; |
| 12 | 16 | import org.junit.jupiter.api.AfterEach; |
| ... | ... | @@ -31,6 +35,8 @@ import static org.mockito.Mockito.when; |
| 31 | 35 | class UserServiceImplTest { |
| 32 | 36 | |
| 33 | 37 | private UserMapper userMapper; |
| 38 | + private EmployeeMapper employeeMapper; | |
| 39 | + private PermissionMapper permissionMapper; | |
| 34 | 40 | private UserConverter userConverter; |
| 35 | 41 | private PasswordEncoder passwordEncoder; |
| 36 | 42 | private UserServiceImpl service; |
| ... | ... | @@ -47,9 +53,12 @@ class UserServiceImplTest { |
| 47 | 53 | @BeforeEach |
| 48 | 54 | void setUp() { |
| 49 | 55 | userMapper = mock(UserMapper.class); |
| 56 | + employeeMapper = mock(EmployeeMapper.class); | |
| 57 | + permissionMapper = mock(PermissionMapper.class); | |
| 50 | 58 | userConverter = mock(UserConverter.class); |
| 51 | 59 | passwordEncoder = new BCryptPasswordEncoder(); |
| 52 | - service = new UserServiceImpl(userMapper, userConverter, passwordEncoder, "666666"); | |
| 60 | + service = new UserServiceImpl(userMapper, employeeMapper, permissionMapper, | |
| 61 | + userConverter, passwordEncoder, "666666"); | |
| 53 | 62 | ctxMock = mockStatic(CurrentUserContext.class); |
| 54 | 63 | ctxMock.when(CurrentUserContext::current).thenReturn(ADMIN_CONTEXT); |
| 55 | 64 | } |
| ... | ... | @@ -150,4 +159,72 @@ class UserServiceImplTest { |
| 150 | 159 | |
| 151 | 160 | verify(userMapper, never()).insert(any(User.class)); |
| 152 | 161 | } |
| 162 | + | |
| 163 | + @Test | |
| 164 | + @SuppressWarnings("unchecked") | |
| 165 | + void createUser_invalidEmployeeId_throws40004() { | |
| 166 | + stubConverterReturnsEmptyUser(); | |
| 167 | + when(employeeMapper.selectOne(any(Wrapper.class))).thenReturn(null); | |
| 168 | + | |
| 169 | + UserCreateDTO dto = baseDTO(); | |
| 170 | + dto.setEmployeeId(99999); | |
| 171 | + | |
| 172 | + assertThatThrownBy(() -> service.createUser(dto)) | |
| 173 | + .isInstanceOf(BusinessException.class) | |
| 174 | + .matches(e -> ((BusinessException) e).getCode() == 40004); | |
| 175 | + | |
| 176 | + verify(userMapper, never()).insert(any(User.class)); | |
| 177 | + } | |
| 178 | + | |
| 179 | + @Test | |
| 180 | + @SuppressWarnings("unchecked") | |
| 181 | + void createUser_disabledEmployee_throws40004() { | |
| 182 | + stubConverterReturnsEmptyUser(); | |
| 183 | + // 模拟"员工存在但 iIsDisabled=1":Wrapper 含 eq(iIsDisabled,0) 时不返回任何记录 | |
| 184 | + when(employeeMapper.selectOne(any(Wrapper.class))).thenReturn(null); | |
| 185 | + | |
| 186 | + UserCreateDTO dto = baseDTO(); | |
| 187 | + dto.setEmployeeId(7); | |
| 188 | + | |
| 189 | + assertThatThrownBy(() -> service.createUser(dto)) | |
| 190 | + .isInstanceOf(BusinessException.class) | |
| 191 | + .matches(e -> ((BusinessException) e).getCode() == 40004); | |
| 192 | + } | |
| 193 | + | |
| 194 | + @Test | |
| 195 | + @SuppressWarnings("unchecked") | |
| 196 | + void createUser_invalidPermissionIds_throws40005() { | |
| 197 | + stubConverterReturnsEmptyUser(); | |
| 198 | + // 传 3 个 id,只查到 2 条 | |
| 199 | + when(permissionMapper.selectList(any(Wrapper.class))) | |
| 200 | + .thenReturn(List.of(new Permission(), new Permission())); | |
| 201 | + | |
| 202 | + UserCreateDTO dto = baseDTO(); | |
| 203 | + dto.setPermissionIds(List.of(1, 2, 3)); | |
| 204 | + | |
| 205 | + assertThatThrownBy(() -> service.createUser(dto)) | |
| 206 | + .isInstanceOf(BusinessException.class) | |
| 207 | + .matches(e -> ((BusinessException) e).getCode() == 40005); | |
| 208 | + | |
| 209 | + verify(userMapper, never()).insert(any(User.class)); | |
| 210 | + } | |
| 211 | + | |
| 212 | + @Test | |
| 213 | + @SuppressWarnings("unchecked") | |
| 214 | + void createUser_permissionIdsNullOrEmpty_skipsCheck() { | |
| 215 | + stubConverterReturnsEmptyUser(); | |
| 216 | + UserCreateDTO dto = baseDTO(); | |
| 217 | + dto.setPermissionIds(null); | |
| 218 | + | |
| 219 | + service.createUser(dto); | |
| 220 | + verify(permissionMapper, never()).selectList(any(Wrapper.class)); | |
| 221 | + | |
| 222 | + UserCreateDTO dto2 = baseDTO(); | |
| 223 | + dto2.setUserCode("U-NEW-002"); | |
| 224 | + dto2.setUserName("newuser2"); | |
| 225 | + dto2.setPermissionIds(List.of()); | |
| 226 | + | |
| 227 | + service.createUser(dto2); | |
| 228 | + verify(permissionMapper, never()).selectList(any(Wrapper.class)); | |
| 229 | + } | |
| 153 | 230 | } | ... | ... |