Commit b27400afdbfcde2a5f56337ad9df17ed8567f51f

Authored by zichun
1 parent 0f608dff

feat(usr): createUser 外键校验 (40004/40005) REQ-USR-001

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 }
... ...