From bf2370ba1158d83515f157eabcf9a9a3ebcb0b21 Mon Sep 17 00:00:00 2001 From: zichun Date: Wed, 29 Apr 2026 18:00:16 +0800 Subject: [PATCH] feat(mod): module delete service + soft delete REQ-MOD-003 --- backend/src/main/java/com/xly/erp/module/mod/service/ModuleService.java | 2 ++ backend/src/main/java/com/xly/erp/module/mod/service/impl/ModuleServiceImpl.java | 19 +++++++++++++++++++ backend/src/test/java/com/xly/erp/module/mod/service/ModuleServiceImplTest.java | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 0 deletions(-) diff --git a/backend/src/main/java/com/xly/erp/module/mod/service/ModuleService.java b/backend/src/main/java/com/xly/erp/module/mod/service/ModuleService.java index 214b94c..798d0ab 100644 --- a/backend/src/main/java/com/xly/erp/module/mod/service/ModuleService.java +++ b/backend/src/main/java/com/xly/erp/module/mod/service/ModuleService.java @@ -7,4 +7,6 @@ public interface ModuleService { Integer create(CreateModuleDTO dto); Integer update(Integer id, UpdateModuleDTO dto); + + void delete(Integer id); } diff --git a/backend/src/main/java/com/xly/erp/module/mod/service/impl/ModuleServiceImpl.java b/backend/src/main/java/com/xly/erp/module/mod/service/impl/ModuleServiceImpl.java index aa78dfa..2187eff 100644 --- a/backend/src/main/java/com/xly/erp/module/mod/service/impl/ModuleServiceImpl.java +++ b/backend/src/main/java/com/xly/erp/module/mod/service/impl/ModuleServiceImpl.java @@ -93,6 +93,25 @@ public class ModuleServiceImpl implements ModuleService { return id; } + @Override + public void delete(Integer id) { + Module original = moduleMapper.selectById(id); + if (original == null || Boolean.TRUE.equals(original.getBDeleted())) { + throw new BizException(40400, "模块不存在或已删除"); + } + if (moduleMapper.hasActiveChildren(id)) { + throw new BizException(40901, "模块仍有未删除子节点"); + } + + Module entity = new Module(); + entity.setIIncrement(id); + entity.setBDeleted(true); + entity.setTDeletedDate(LocalDateTime.now()); + String authedUserNo = SecurityContextHelper.currentUserNo(); + entity.setSDeletedBy(authedUserNo != null ? authedUserNo : stub.getStubUserNo()); + moduleMapper.updateById(entity); + } + private void validateParent(Integer id, Integer parentId) { if (parentId == null) { return; diff --git a/backend/src/test/java/com/xly/erp/module/mod/service/ModuleServiceImplTest.java b/backend/src/test/java/com/xly/erp/module/mod/service/ModuleServiceImplTest.java index b7c40d9..ca40fb1 100644 --- a/backend/src/test/java/com/xly/erp/module/mod/service/ModuleServiceImplTest.java +++ b/backend/src/test/java/com/xly/erp/module/mod/service/ModuleServiceImplTest.java @@ -238,6 +238,74 @@ class ModuleServiceImplTest { verify(moduleMapper, never()).updateById(any(Module.class)); } + @Test + void deleteWithValidId_softDeletes_andSetsAuditFields() { + when(moduleMapper.selectById(10)).thenReturn(stubExistingModule(10)); + when(moduleMapper.hasActiveChildren(10)).thenReturn(false); + when(moduleMapper.updateById(any(Module.class))).thenReturn(1); + + service.delete(10); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Module.class); + verify(moduleMapper).updateById(captor.capture()); + Module passed = captor.getValue(); + assertThat(passed.getIIncrement()).isEqualTo(10); + assertThat(passed.getBDeleted()).isTrue(); + assertThat(passed.getTDeletedDate()).isNotNull(); + assertThat(passed.getSDeletedBy()).isEqualTo("STUB_ADMIN"); + assertThat(passed.getSProcedureName()).isNull(); + assertThat(passed.getSCreatedBy()).isNull(); + assertThat(passed.getSBrandsId()).isNull(); + } + + @Test + void deleteWithTargetNotFound_throws40400() { + when(moduleMapper.selectById(99)).thenReturn(null); + + assertThatThrownBy(() -> service.delete(99)) + .isInstanceOf(BizException.class) + .hasFieldOrPropertyWithValue("code", 40400); + verify(moduleMapper, never()).updateById(any(Module.class)); + } + + @Test + void deleteWithTargetAlreadyDeleted_throws40400() { + Module deleted = stubExistingModule(10); + deleted.setBDeleted(true); + when(moduleMapper.selectById(10)).thenReturn(deleted); + + assertThatThrownBy(() -> service.delete(10)) + .isInstanceOf(BizException.class) + .hasFieldOrPropertyWithValue("code", 40400); + verify(moduleMapper, never()).updateById(any(Module.class)); + } + + @Test + void deleteWithActiveChildren_throws40901() { + when(moduleMapper.selectById(10)).thenReturn(stubExistingModule(10)); + when(moduleMapper.hasActiveChildren(10)).thenReturn(true); + + assertThatThrownBy(() -> service.delete(10)) + .isInstanceOf(BizException.class) + .hasFieldOrPropertyWithValue("code", 40901); + verify(moduleMapper, never()).updateById(any(Module.class)); + } + + @Test + void deleteSetsDeletedByFromAuthenticatedUser() { + SecurityContextHolder.getContext().setAuthentication( + new UsernamePasswordAuthenticationToken("BOB", null, Collections.emptyList())); + when(moduleMapper.selectById(10)).thenReturn(stubExistingModule(10)); + when(moduleMapper.hasActiveChildren(10)).thenReturn(false); + when(moduleMapper.updateById(any(Module.class))).thenReturn(1); + + service.delete(10); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Module.class); + verify(moduleMapper).updateById(captor.capture()); + assertThat(captor.getValue().getSDeletedBy()).isEqualTo("BOB"); + } + private UpdateModuleDTO baseUpdateDto() { UpdateModuleDTO dto = new UpdateModuleDTO(); dto.setSDisplayType("手机端"); -- libgit2 0.22.2