Commit 9e549f65fcb6c42bd80d512f14773921c126b13a

Authored by zichun
1 parent 13500faf

feat(usr): 修改用户关联职员校验与权限组全量覆盖 REQ-USR-002

backend/src/main/java/com/xly/erp/modules/usr/service/impl/UsrUserServiceImpl.java
@@ -140,7 +140,26 @@ public class UsrUserServiceImpl implements UsrUserService { @@ -140,7 +140,26 @@ public class UsrUserServiceImpl implements UsrUserService {
140 throw new BusinessException(ResultCode.NOT_FOUND); 140 throw new BusinessException(ResultCode.NOT_FOUND);
141 } 141 }
142 142
143 - // 2. 组装目标实体:仅 set 主键 + 非 null 可更新列。 143 + // 2. 关联职员存在性校验(可选)——置于主记录更新前,非法即整体回滚不留副作用。
  144 + if (dto.getIEmployeeId() != null && usrEmployeeMapper.selectById(dto.getIEmployeeId()) == null) {
  145 + throw new BusinessException(ResultCode.PARAM_INVALID, "关联职员不存在");
  146 + }
  147 +
  148 + // 3. 权限存在性校验 + 去重(可选)——非 null 才参与全量覆盖,元素须存在。
  149 + List<Integer> dedupedPermissionIds = null;
  150 + if (dto.getPermissionIds() != null) {
  151 + dedupedPermissionIds = dto.getPermissionIds().stream()
  152 + .filter(java.util.Objects::nonNull)
  153 + .distinct()
  154 + .toList();
  155 + for (Integer permissionId : dedupedPermissionIds) {
  156 + if (usrPermissionMapper.selectById(permissionId) == null) {
  157 + throw new BusinessException(ResultCode.PARAM_INVALID, "权限不存在: " + permissionId);
  158 + }
  159 + }
  160 + }
  161 +
  162 + // 4. 组装目标实体:仅 set 主键 + 非 null 可更新列。
144 // 不 set sUserName / sPassword / sCreator / tCreateDate / 租户列, 163 // 不 set sUserName / sPassword / sCreator / tCreateDate / 租户列,
145 // 依赖 MP updateById「null 字段不参与 SET」语义保持原值不被覆盖。 164 // 依赖 MP updateById「null 字段不参与 SET」语义保持原值不被覆盖。
146 UsrUser target = new UsrUser(); 165 UsrUser target = new UsrUser();
@@ -161,6 +180,16 @@ public class UsrUserServiceImpl implements UsrUserService { @@ -161,6 +180,16 @@ public class UsrUserServiceImpl implements UsrUserService {
161 } 180 }
162 usrUserMapper.updateById(target); 181 usrUserMapper.updateById(target);
163 182
  183 + // 5. 权限组全量覆盖(permissionIds 非 null):先删该用户全部旧授权,
  184 + // 再对去重集合逐行插入(空集合则只删不插 = 清空;null 不进入本分支 = 不改)。
  185 + if (dedupedPermissionIds != null) {
  186 + usrUserPermissionMapper.delete(Wrappers.<UsrUserPermission>lambdaQuery()
  187 + .eq(UsrUserPermission::getIUserId, id));
  188 + for (Integer permissionId : new LinkedHashSet<>(dedupedPermissionIds)) {
  189 + usrUserPermissionMapper.insert(new UsrUserPermission(id, permissionId));
  190 + }
  191 + }
  192 +
164 return id; 193 return id;
165 } 194 }
166 } 195 }
backend/src/test/java/com/xly/erp/modules/usr/service/UsrUserServiceImplTest.java
@@ -274,4 +274,87 @@ class UsrUserServiceImplTest { @@ -274,4 +274,87 @@ class UsrUserServiceImplTest {
274 assertThat(saved.getICanModifyBill()).isNull(); 274 assertThat(saved.getICanModifyBill()).isNull();
275 assertThat(saved.getIIsVoid()).isNull(); 275 assertThat(saved.getIIsVoid()).isNull();
276 } 276 }
  277 +
  278 + // ---------------- REQ-USR-002 T3:关联职员存在性 + 权限组全量覆盖 ----------------
  279 +
  280 + @Test
  281 + @SuppressWarnings("unchecked")
  282 + void nonExistentEmployeeOnUpdateThrows40001() {
  283 + when(usrUserMapper.selectById(55)).thenReturn(new UsrUser());
  284 + when(usrEmployeeMapper.selectById(999)).thenReturn(null);
  285 + UpdateUserDTO dto = minimalUpdateDto();
  286 + dto.setIEmployeeId(999);
  287 +
  288 + assertThatThrownBy(() -> service.updateUser(55, dto))
  289 + .isInstanceOf(BusinessException.class)
  290 + .extracting(e -> ((BusinessException) e).getResultCode())
  291 + .isEqualTo(ResultCode.PARAM_INVALID);
  292 + verify(usrUserMapper, never()).updateById(any(UsrUser.class));
  293 + verify(usrUserPermissionMapper, never()).delete(any(Wrapper.class));
  294 + verify(usrUserPermissionMapper, never()).insert(any(UsrUserPermission.class));
  295 + }
  296 +
  297 + @Test
  298 + @SuppressWarnings("unchecked")
  299 + void nonExistentPermissionOnUpdateThrows40001() {
  300 + when(usrUserMapper.selectById(55)).thenReturn(new UsrUser());
  301 + when(usrPermissionMapper.selectById(7)).thenReturn(null);
  302 + UpdateUserDTO dto = minimalUpdateDto();
  303 + dto.setPermissionIds(List.of(7));
  304 +
  305 + assertThatThrownBy(() -> service.updateUser(55, dto))
  306 + .isInstanceOf(BusinessException.class)
  307 + .extracting(e -> ((BusinessException) e).getResultCode())
  308 + .isEqualTo(ResultCode.PARAM_INVALID);
  309 + verify(usrUserMapper, never()).updateById(any(UsrUser.class));
  310 + verify(usrUserPermissionMapper, never()).delete(any(Wrapper.class));
  311 + verify(usrUserPermissionMapper, never()).insert(any(UsrUserPermission.class));
  312 + }
  313 +
  314 + @Test
  315 + @SuppressWarnings("unchecked")
  316 + void permissionIdsOverwriteDeletesThenInserts() {
  317 + when(usrUserMapper.selectById(55)).thenReturn(new UsrUser());
  318 + when(usrUserMapper.updateById(any(UsrUser.class))).thenReturn(1);
  319 + when(usrPermissionMapper.selectById(10)).thenReturn(new UsrPermission());
  320 + when(usrPermissionMapper.selectById(20)).thenReturn(new UsrPermission());
  321 + UpdateUserDTO dto = minimalUpdateDto();
  322 + dto.setPermissionIds(List.of(10, 10, 20));
  323 +
  324 + service.updateUser(55, dto);
  325 +
  326 + verify(usrUserPermissionMapper, times(1)).delete(any(Wrapper.class));
  327 + ArgumentCaptor<UsrUserPermission> captor = ArgumentCaptor.forClass(UsrUserPermission.class);
  328 + verify(usrUserPermissionMapper, times(2)).insert(captor.capture());
  329 + List<UsrUserPermission> grants = captor.getAllValues();
  330 + assertThat(grants).extracting(UsrUserPermission::getIUserId).containsOnly(55);
  331 + assertThat(grants).extracting(UsrUserPermission::getIPermissionId)
  332 + .containsExactlyInAnyOrder(10, 20);
  333 + }
  334 +
  335 + @Test
  336 + @SuppressWarnings("unchecked")
  337 + void emptyPermissionIdsClearsAll() {
  338 + when(usrUserMapper.selectById(55)).thenReturn(new UsrUser());
  339 + when(usrUserMapper.updateById(any(UsrUser.class))).thenReturn(1);
  340 + UpdateUserDTO dto = minimalUpdateDto();
  341 + dto.setPermissionIds(List.of());
  342 +
  343 + service.updateUser(55, dto);
  344 +
  345 + verify(usrUserPermissionMapper, times(1)).delete(any(Wrapper.class));
  346 + verify(usrUserPermissionMapper, never()).insert(any(UsrUserPermission.class));
  347 + }
  348 +
  349 + @Test
  350 + @SuppressWarnings("unchecked")
  351 + void nullPermissionIdsLeavesGrantsUntouched() {
  352 + when(usrUserMapper.selectById(55)).thenReturn(new UsrUser());
  353 + when(usrUserMapper.updateById(any(UsrUser.class))).thenReturn(1);
  354 +
  355 + service.updateUser(55, minimalUpdateDto());
  356 +
  357 + verify(usrUserPermissionMapper, never()).delete(any(Wrapper.class));
  358 + verify(usrUserPermissionMapper, never()).insert(any(UsrUserPermission.class));
  359 + }
277 } 360 }