Commit 0017a259116cfd31ab45bca69435bd7e09bf77c0

Authored by zichun
1 parent 7eec9887

feat(mod): module create error branches REQ-MOD-001

backend/src/main/java/com/xly/erp/module/mod/service/impl/ModuleServiceImpl.java
@@ -8,15 +8,19 @@ import com.xly.erp.module.mod.dto.CreateModuleDTO; @@ -8,15 +8,19 @@ import com.xly.erp.module.mod.dto.CreateModuleDTO;
8 import com.xly.erp.module.mod.entity.Module; 8 import com.xly.erp.module.mod.entity.Module;
9 import com.xly.erp.module.mod.mapper.ModuleMapper; 9 import com.xly.erp.module.mod.mapper.ModuleMapper;
10 import com.xly.erp.module.mod.service.ModuleService; 10 import com.xly.erp.module.mod.service.ModuleService;
  11 +import org.springframework.dao.DuplicateKeyException;
11 import org.springframework.stereotype.Service; 12 import org.springframework.stereotype.Service;
12 import org.springframework.transaction.annotation.Transactional; 13 import org.springframework.transaction.annotation.Transactional;
13 14
14 import java.time.LocalDateTime; 15 import java.time.LocalDateTime;
  16 +import java.util.Set;
15 17
16 @Service 18 @Service
17 @Transactional(rollbackFor = Exception.class) 19 @Transactional(rollbackFor = Exception.class)
18 public class ModuleServiceImpl implements ModuleService { 20 public class ModuleServiceImpl implements ModuleService {
19 21
  22 + private static final Set<String> DISPLAY_TYPES = Set.of("手机端", "前端业务", "系统配置", "接口");
  23 +
20 private final ModuleMapper moduleMapper; 24 private final ModuleMapper moduleMapper;
21 private final TenantProperties tenant; 25 private final TenantProperties tenant;
22 private final StubSecurityProperties stub; 26 private final StubSecurityProperties stub;
@@ -31,6 +35,9 @@ public class ModuleServiceImpl implements ModuleService { @@ -31,6 +35,9 @@ public class ModuleServiceImpl implements ModuleService {
31 35
32 @Override 36 @Override
33 public Integer create(CreateModuleDTO dto) { 37 public Integer create(CreateModuleDTO dto) {
  38 + if (!DISPLAY_TYPES.contains(dto.getSDisplayType())) {
  39 + throw new BizException(40010, "显示类型枚举不合法");
  40 + }
34 if (dto.getIParentId() != null && !moduleMapper.existsActiveById(dto.getIParentId())) { 41 if (dto.getIParentId() != null && !moduleMapper.existsActiveById(dto.getIParentId())) {
35 throw new BizException(40021, "父模块不存在或已删除"); 42 throw new BizException(40021, "父模块不存在或已删除");
36 } 43 }
@@ -51,7 +58,11 @@ public class ModuleServiceImpl implements ModuleService { @@ -51,7 +58,11 @@ public class ModuleServiceImpl implements ModuleService {
51 m.setSCreatedBy(authedUserNo != null ? authedUserNo : stub.getStubUserNo()); 58 m.setSCreatedBy(authedUserNo != null ? authedUserNo : stub.getStubUserNo());
52 m.setBDeleted(false); 59 m.setBDeleted(false);
53 60
54 - moduleMapper.insert(m); 61 + try {
  62 + moduleMapper.insert(m);
  63 + } catch (DuplicateKeyException e) {
  64 + throw new BizException(40020, "存储过程名称已存在");
  65 + }
55 return m.getIIncrement(); 66 return m.getIIncrement();
56 } 67 }
57 } 68 }
backend/src/test/java/com/xly/erp/module/mod/service/ModuleServiceImplTest.java
@@ -11,8 +11,12 @@ import org.junit.jupiter.api.AfterEach; @@ -11,8 +11,12 @@ import org.junit.jupiter.api.AfterEach;
11 import org.junit.jupiter.api.BeforeEach; 11 import org.junit.jupiter.api.BeforeEach;
12 import org.junit.jupiter.api.Test; 12 import org.junit.jupiter.api.Test;
13 import org.mockito.ArgumentCaptor; 13 import org.mockito.ArgumentCaptor;
  14 +import org.springframework.dao.DuplicateKeyException;
  15 +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
14 import org.springframework.security.core.context.SecurityContextHolder; 16 import org.springframework.security.core.context.SecurityContextHolder;
15 17
  18 +import java.util.Collections;
  19 +
16 import static org.assertj.core.api.Assertions.assertThat; 20 import static org.assertj.core.api.Assertions.assertThat;
17 import static org.assertj.core.api.Assertions.assertThatThrownBy; 21 import static org.assertj.core.api.Assertions.assertThatThrownBy;
18 import static org.mockito.ArgumentMatchers.any; 22 import static org.mockito.ArgumentMatchers.any;
@@ -82,6 +86,52 @@ class ModuleServiceImplTest { @@ -82,6 +86,52 @@ class ModuleServiceImplTest {
82 verify(moduleMapper, never()).insert(any(Module.class)); 86 verify(moduleMapper, never()).insert(any(Module.class));
83 } 87 }
84 88
  89 + @Test
  90 + void createWithInvalidDisplayType_throws40010() {
  91 + CreateModuleDTO dto = baseDto();
  92 + dto.setSDisplayType("未知");
  93 +
  94 + assertThatThrownBy(() -> service.create(dto))
  95 + .isInstanceOf(BizException.class)
  96 + .hasFieldOrPropertyWithValue("code", 40010);
  97 + verify(moduleMapper, never()).insert(any(Module.class));
  98 + }
  99 +
  100 + @Test
  101 + void createWithNullParentId_skipsParentCheck() {
  102 + CreateModuleDTO dto = baseDto();
  103 + dto.setIParentId(null);
  104 +
  105 + service.create(dto);
  106 +
  107 + verify(moduleMapper, never()).findActiveFlagById(any());
  108 + verify(moduleMapper, times(1)).insert(any(Module.class));
  109 + }
  110 +
  111 + @Test
  112 + void mapperDuplicateKey_throws40020() {
  113 + CreateModuleDTO dto = baseDto();
  114 + when(moduleMapper.insert(any(Module.class)))
  115 + .thenThrow(new DuplicateKeyException("uk_procedure_name"));
  116 +
  117 + assertThatThrownBy(() -> service.create(dto))
  118 + .isInstanceOf(BizException.class)
  119 + .hasFieldOrPropertyWithValue("code", 40020);
  120 + }
  121 +
  122 + @Test
  123 + void usesAuthenticatedUserNoAsCreatedBy() {
  124 + SecurityContextHolder.getContext().setAuthentication(
  125 + new UsernamePasswordAuthenticationToken("ALICE", null, Collections.emptyList()));
  126 + CreateModuleDTO dto = baseDto();
  127 +
  128 + service.create(dto);
  129 +
  130 + ArgumentCaptor<Module> captor = ArgumentCaptor.forClass(Module.class);
  131 + verify(moduleMapper).insert(captor.capture());
  132 + assertThat(captor.getValue().getSCreatedBy()).isEqualTo("ALICE");
  133 + }
  134 +
85 private CreateModuleDTO baseDto() { 135 private CreateModuleDTO baseDto() {
86 CreateModuleDTO dto = new CreateModuleDTO(); 136 CreateModuleDTO dto = new CreateModuleDTO();
87 dto.setSDisplayType("手机端"); 137 dto.setSDisplayType("手机端");