Commit 9e549f65fcb6c42bd80d512f14773921c126b13a
1 parent
13500faf
feat(usr): 修改用户关联职员校验与权限组全量覆盖 REQ-USR-002
Showing
2 changed files
with
113 additions
and
1 deletions
backend/src/main/java/com/xly/erp/modules/usr/service/impl/UsrUserServiceImpl.java
| ... | ... | @@ -140,7 +140,26 @@ public class UsrUserServiceImpl implements UsrUserService { |
| 140 | 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 | 163 | // 不 set sUserName / sPassword / sCreator / tCreateDate / 租户列, |
| 145 | 164 | // 依赖 MP updateById「null 字段不参与 SET」语义保持原值不被覆盖。 |
| 146 | 165 | UsrUser target = new UsrUser(); |
| ... | ... | @@ -161,6 +180,16 @@ public class UsrUserServiceImpl implements UsrUserService { |
| 161 | 180 | } |
| 162 | 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 | 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 | 274 | assertThat(saved.getICanModifyBill()).isNull(); |
| 275 | 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 | } | ... | ... |