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 +7,11 @@ import com.xly.test4.common.security.CurrentUser; | ||
| 7 | import com.xly.test4.common.security.CurrentUserContext; | 7 | import com.xly.test4.common.security.CurrentUserContext; |
| 8 | import com.xly.test4.module.usr.converter.UserConverter; | 8 | import com.xly.test4.module.usr.converter.UserConverter; |
| 9 | import com.xly.test4.module.usr.dto.UserCreateDTO; | 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 | import com.xly.test4.module.usr.entity.User; | 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 | import com.xly.test4.module.usr.mapper.UserMapper; | 15 | import com.xly.test4.module.usr.mapper.UserMapper; |
| 12 | import com.xly.test4.module.usr.service.UserService; | 16 | import com.xly.test4.module.usr.service.UserService; |
| 13 | import com.xly.test4.module.usr.vo.UserCreateVO; | 17 | import com.xly.test4.module.usr.vo.UserCreateVO; |
| @@ -16,19 +20,27 @@ import org.springframework.security.crypto.password.PasswordEncoder; | @@ -16,19 +20,27 @@ import org.springframework.security.crypto.password.PasswordEncoder; | ||
| 16 | import org.springframework.stereotype.Service; | 20 | import org.springframework.stereotype.Service; |
| 17 | import org.springframework.transaction.annotation.Transactional; | 21 | import org.springframework.transaction.annotation.Transactional; |
| 18 | 22 | ||
| 23 | +import java.util.List; | ||
| 24 | + | ||
| 19 | @Service | 25 | @Service |
| 20 | public class UserServiceImpl implements UserService { | 26 | public class UserServiceImpl implements UserService { |
| 21 | 27 | ||
| 22 | private final UserMapper userMapper; | 28 | private final UserMapper userMapper; |
| 29 | + private final EmployeeMapper employeeMapper; | ||
| 30 | + private final PermissionMapper permissionMapper; | ||
| 23 | private final UserConverter userConverter; | 31 | private final UserConverter userConverter; |
| 24 | private final PasswordEncoder passwordEncoder; | 32 | private final PasswordEncoder passwordEncoder; |
| 25 | private final String defaultPassword; | 33 | private final String defaultPassword; |
| 26 | 34 | ||
| 27 | public UserServiceImpl(UserMapper userMapper, | 35 | public UserServiceImpl(UserMapper userMapper, |
| 36 | + EmployeeMapper employeeMapper, | ||
| 37 | + PermissionMapper permissionMapper, | ||
| 28 | UserConverter userConverter, | 38 | UserConverter userConverter, |
| 29 | PasswordEncoder passwordEncoder, | 39 | PasswordEncoder passwordEncoder, |
| 30 | @Value("${app.security.default-password}") String defaultPassword) { | 40 | @Value("${app.security.default-password}") String defaultPassword) { |
| 31 | this.userMapper = userMapper; | 41 | this.userMapper = userMapper; |
| 42 | + this.employeeMapper = employeeMapper; | ||
| 43 | + this.permissionMapper = permissionMapper; | ||
| 32 | this.userConverter = userConverter; | 44 | this.userConverter = userConverter; |
| 33 | this.passwordEncoder = passwordEncoder; | 45 | this.passwordEncoder = passwordEncoder; |
| 34 | this.defaultPassword = defaultPassword; | 46 | this.defaultPassword = defaultPassword; |
| @@ -48,6 +60,24 @@ public class UserServiceImpl implements UserService { | @@ -48,6 +60,24 @@ public class UserServiceImpl implements UserService { | ||
| 48 | throw new BusinessException(ResultCode.USER_CODE_DUPLICATE, "用户号已存在"); | 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 | User user = userConverter.toEntity(dto); | 81 | User user = userConverter.toEntity(dto); |
| 52 | user.setSBrandsId(current.getBrandsId()); | 82 | user.setSBrandsId(current.getBrandsId()); |
| 53 | user.setSSubsidiaryId(current.getSubsidiaryId()); | 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,7 +6,11 @@ import com.xly.test4.common.security.CurrentUser; | ||
| 6 | import com.xly.test4.common.security.CurrentUserContext; | 6 | import com.xly.test4.common.security.CurrentUserContext; |
| 7 | import com.xly.test4.module.usr.converter.UserConverter; | 7 | import com.xly.test4.module.usr.converter.UserConverter; |
| 8 | import com.xly.test4.module.usr.dto.UserCreateDTO; | 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 | import com.xly.test4.module.usr.entity.User; | 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 | import com.xly.test4.module.usr.mapper.UserMapper; | 14 | import com.xly.test4.module.usr.mapper.UserMapper; |
| 11 | import com.xly.test4.module.usr.vo.UserCreateVO; | 15 | import com.xly.test4.module.usr.vo.UserCreateVO; |
| 12 | import org.junit.jupiter.api.AfterEach; | 16 | import org.junit.jupiter.api.AfterEach; |
| @@ -31,6 +35,8 @@ import static org.mockito.Mockito.when; | @@ -31,6 +35,8 @@ import static org.mockito.Mockito.when; | ||
| 31 | class UserServiceImplTest { | 35 | class UserServiceImplTest { |
| 32 | 36 | ||
| 33 | private UserMapper userMapper; | 37 | private UserMapper userMapper; |
| 38 | + private EmployeeMapper employeeMapper; | ||
| 39 | + private PermissionMapper permissionMapper; | ||
| 34 | private UserConverter userConverter; | 40 | private UserConverter userConverter; |
| 35 | private PasswordEncoder passwordEncoder; | 41 | private PasswordEncoder passwordEncoder; |
| 36 | private UserServiceImpl service; | 42 | private UserServiceImpl service; |
| @@ -47,9 +53,12 @@ class UserServiceImplTest { | @@ -47,9 +53,12 @@ class UserServiceImplTest { | ||
| 47 | @BeforeEach | 53 | @BeforeEach |
| 48 | void setUp() { | 54 | void setUp() { |
| 49 | userMapper = mock(UserMapper.class); | 55 | userMapper = mock(UserMapper.class); |
| 56 | + employeeMapper = mock(EmployeeMapper.class); | ||
| 57 | + permissionMapper = mock(PermissionMapper.class); | ||
| 50 | userConverter = mock(UserConverter.class); | 58 | userConverter = mock(UserConverter.class); |
| 51 | passwordEncoder = new BCryptPasswordEncoder(); | 59 | passwordEncoder = new BCryptPasswordEncoder(); |
| 52 | - service = new UserServiceImpl(userMapper, userConverter, passwordEncoder, "666666"); | 60 | + service = new UserServiceImpl(userMapper, employeeMapper, permissionMapper, |
| 61 | + userConverter, passwordEncoder, "666666"); | ||
| 53 | ctxMock = mockStatic(CurrentUserContext.class); | 62 | ctxMock = mockStatic(CurrentUserContext.class); |
| 54 | ctxMock.when(CurrentUserContext::current).thenReturn(ADMIN_CONTEXT); | 63 | ctxMock.when(CurrentUserContext::current).thenReturn(ADMIN_CONTEXT); |
| 55 | } | 64 | } |
| @@ -150,4 +159,72 @@ class UserServiceImplTest { | @@ -150,4 +159,72 @@ class UserServiceImplTest { | ||
| 150 | 159 | ||
| 151 | verify(userMapper, never()).insert(any(User.class)); | 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 | } |