From 9e549f65fcb6c42bd80d512f14773921c126b13a Mon Sep 17 00:00:00 2001 From: zichun Date: Mon, 1 Jun 2026 14:01:36 +0800 Subject: [PATCH] feat(usr): 修改用户关联职员校验与权限组全量覆盖 REQ-USR-002 --- backend/src/main/java/com/xly/erp/modules/usr/service/impl/UsrUserServiceImpl.java | 31 ++++++++++++++++++++++++++++++- backend/src/test/java/com/xly/erp/modules/usr/service/UsrUserServiceImplTest.java | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/com/xly/erp/modules/usr/service/impl/UsrUserServiceImpl.java b/backend/src/main/java/com/xly/erp/modules/usr/service/impl/UsrUserServiceImpl.java index 33842cf..ba5a42c 100644 --- a/backend/src/main/java/com/xly/erp/modules/usr/service/impl/UsrUserServiceImpl.java +++ b/backend/src/main/java/com/xly/erp/modules/usr/service/impl/UsrUserServiceImpl.java @@ -140,7 +140,26 @@ public class UsrUserServiceImpl implements UsrUserService { throw new BusinessException(ResultCode.NOT_FOUND); } - // 2. 组装目标实体:仅 set 主键 + 非 null 可更新列。 + // 2. 关联职员存在性校验(可选)——置于主记录更新前,非法即整体回滚不留副作用。 + if (dto.getIEmployeeId() != null && usrEmployeeMapper.selectById(dto.getIEmployeeId()) == null) { + throw new BusinessException(ResultCode.PARAM_INVALID, "关联职员不存在"); + } + + // 3. 权限存在性校验 + 去重(可选)——非 null 才参与全量覆盖,元素须存在。 + List dedupedPermissionIds = null; + if (dto.getPermissionIds() != null) { + dedupedPermissionIds = dto.getPermissionIds().stream() + .filter(java.util.Objects::nonNull) + .distinct() + .toList(); + for (Integer permissionId : dedupedPermissionIds) { + if (usrPermissionMapper.selectById(permissionId) == null) { + throw new BusinessException(ResultCode.PARAM_INVALID, "权限不存在: " + permissionId); + } + } + } + + // 4. 组装目标实体:仅 set 主键 + 非 null 可更新列。 // 不 set sUserName / sPassword / sCreator / tCreateDate / 租户列, // 依赖 MP updateById「null 字段不参与 SET」语义保持原值不被覆盖。 UsrUser target = new UsrUser(); @@ -161,6 +180,16 @@ public class UsrUserServiceImpl implements UsrUserService { } usrUserMapper.updateById(target); + // 5. 权限组全量覆盖(permissionIds 非 null):先删该用户全部旧授权, + // 再对去重集合逐行插入(空集合则只删不插 = 清空;null 不进入本分支 = 不改)。 + if (dedupedPermissionIds != null) { + usrUserPermissionMapper.delete(Wrappers.lambdaQuery() + .eq(UsrUserPermission::getIUserId, id)); + for (Integer permissionId : new LinkedHashSet<>(dedupedPermissionIds)) { + usrUserPermissionMapper.insert(new UsrUserPermission(id, permissionId)); + } + } + return id; } } diff --git a/backend/src/test/java/com/xly/erp/modules/usr/service/UsrUserServiceImplTest.java b/backend/src/test/java/com/xly/erp/modules/usr/service/UsrUserServiceImplTest.java index ddb5e02..71b8586 100644 --- a/backend/src/test/java/com/xly/erp/modules/usr/service/UsrUserServiceImplTest.java +++ b/backend/src/test/java/com/xly/erp/modules/usr/service/UsrUserServiceImplTest.java @@ -274,4 +274,87 @@ class UsrUserServiceImplTest { assertThat(saved.getICanModifyBill()).isNull(); assertThat(saved.getIIsVoid()).isNull(); } + + // ---------------- REQ-USR-002 T3:关联职员存在性 + 权限组全量覆盖 ---------------- + + @Test + @SuppressWarnings("unchecked") + void nonExistentEmployeeOnUpdateThrows40001() { + when(usrUserMapper.selectById(55)).thenReturn(new UsrUser()); + when(usrEmployeeMapper.selectById(999)).thenReturn(null); + UpdateUserDTO dto = minimalUpdateDto(); + dto.setIEmployeeId(999); + + assertThatThrownBy(() -> service.updateUser(55, dto)) + .isInstanceOf(BusinessException.class) + .extracting(e -> ((BusinessException) e).getResultCode()) + .isEqualTo(ResultCode.PARAM_INVALID); + verify(usrUserMapper, never()).updateById(any(UsrUser.class)); + verify(usrUserPermissionMapper, never()).delete(any(Wrapper.class)); + verify(usrUserPermissionMapper, never()).insert(any(UsrUserPermission.class)); + } + + @Test + @SuppressWarnings("unchecked") + void nonExistentPermissionOnUpdateThrows40001() { + when(usrUserMapper.selectById(55)).thenReturn(new UsrUser()); + when(usrPermissionMapper.selectById(7)).thenReturn(null); + UpdateUserDTO dto = minimalUpdateDto(); + dto.setPermissionIds(List.of(7)); + + assertThatThrownBy(() -> service.updateUser(55, dto)) + .isInstanceOf(BusinessException.class) + .extracting(e -> ((BusinessException) e).getResultCode()) + .isEqualTo(ResultCode.PARAM_INVALID); + verify(usrUserMapper, never()).updateById(any(UsrUser.class)); + verify(usrUserPermissionMapper, never()).delete(any(Wrapper.class)); + verify(usrUserPermissionMapper, never()).insert(any(UsrUserPermission.class)); + } + + @Test + @SuppressWarnings("unchecked") + void permissionIdsOverwriteDeletesThenInserts() { + when(usrUserMapper.selectById(55)).thenReturn(new UsrUser()); + when(usrUserMapper.updateById(any(UsrUser.class))).thenReturn(1); + when(usrPermissionMapper.selectById(10)).thenReturn(new UsrPermission()); + when(usrPermissionMapper.selectById(20)).thenReturn(new UsrPermission()); + UpdateUserDTO dto = minimalUpdateDto(); + dto.setPermissionIds(List.of(10, 10, 20)); + + service.updateUser(55, dto); + + verify(usrUserPermissionMapper, times(1)).delete(any(Wrapper.class)); + ArgumentCaptor captor = ArgumentCaptor.forClass(UsrUserPermission.class); + verify(usrUserPermissionMapper, times(2)).insert(captor.capture()); + List grants = captor.getAllValues(); + assertThat(grants).extracting(UsrUserPermission::getIUserId).containsOnly(55); + assertThat(grants).extracting(UsrUserPermission::getIPermissionId) + .containsExactlyInAnyOrder(10, 20); + } + + @Test + @SuppressWarnings("unchecked") + void emptyPermissionIdsClearsAll() { + when(usrUserMapper.selectById(55)).thenReturn(new UsrUser()); + when(usrUserMapper.updateById(any(UsrUser.class))).thenReturn(1); + UpdateUserDTO dto = minimalUpdateDto(); + dto.setPermissionIds(List.of()); + + service.updateUser(55, dto); + + verify(usrUserPermissionMapper, times(1)).delete(any(Wrapper.class)); + verify(usrUserPermissionMapper, never()).insert(any(UsrUserPermission.class)); + } + + @Test + @SuppressWarnings("unchecked") + void nullPermissionIdsLeavesGrantsUntouched() { + when(usrUserMapper.selectById(55)).thenReturn(new UsrUser()); + when(usrUserMapper.updateById(any(UsrUser.class))).thenReturn(1); + + service.updateUser(55, minimalUpdateDto()); + + verify(usrUserPermissionMapper, never()).delete(any(Wrapper.class)); + verify(usrUserPermissionMapper, never()).insert(any(UsrUserPermission.class)); + } } -- libgit2 0.22.2