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