diff --git a/backend/src/main/java/com/xly/erp/module/mod/controller/ModuleController.java b/backend/src/main/java/com/xly/erp/module/mod/controller/ModuleController.java index ca5ebd0..b067c29 100644 --- a/backend/src/main/java/com/xly/erp/module/mod/controller/ModuleController.java +++ b/backend/src/main/java/com/xly/erp/module/mod/controller/ModuleController.java @@ -4,15 +4,19 @@ import com.xly.erp.common.response.Result; import com.xly.erp.module.mod.dto.CreateModuleDTO; import com.xly.erp.module.mod.dto.UpdateModuleDTO; import com.xly.erp.module.mod.service.ModuleService; +import com.xly.erp.module.mod.vo.ModuleTreeVO; import jakarta.validation.Valid; import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import java.util.List; import java.util.Map; @RestController @@ -43,4 +47,9 @@ public class ModuleController { moduleService.delete(id); return Result.ok(); } + + @GetMapping("/modules") + public Result> list(@RequestParam(required = false) String keyword) { + return Result.ok(moduleService.listTree(keyword)); + } } diff --git a/backend/src/test/java/com/xly/erp/module/mod/controller/ModuleControllerIT.java b/backend/src/test/java/com/xly/erp/module/mod/controller/ModuleControllerIT.java index 92ff89f..8998e35 100644 --- a/backend/src/test/java/com/xly/erp/module/mod/controller/ModuleControllerIT.java +++ b/backend/src/test/java/com/xly/erp/module/mod/controller/ModuleControllerIT.java @@ -388,6 +388,110 @@ class ModuleControllerIT { assertThat(stillAlive).isFalse(); } + @Test + void getEmptyKeyword_returnsCompleteTreeAsForest() throws Exception { + Integer rootId = insertOriginal("sp_test_get_root", "查询用根", "ORIG"); + jdbcTemplate.update( + "INSERT INTO tModule (sBrandsId, sSubsidiaryId, tCreateDate, sDisplayType, sProcedureName, " + + "sModuleType, sManageDeptEn, bShowPermission, sModuleNameZh, iParentId, iSortOrder, sCreatedBy, bDeleted) " + + "VALUES ('XLY','XLY', NOW(), '手机端', 'sp_test_get_child', '业务模块', 'IT', 0, '查询用子', ?, 0, 'ORIG', 0)", + rootId); + Integer childId = jdbcTemplate.queryForObject( + "SELECT iIncrement FROM tModule WHERE sProcedureName = 'sp_test_get_child'", Integer.class); + + String token = testJwtHelper.signFor("ADMIN001"); + HttpHeaders headers = jsonHeaders(); + headers.set("Authorization", "Bearer " + token); + ResponseEntity resp = rest.exchange( + listUrl(null), HttpMethod.GET, new HttpEntity<>(headers), String.class); + + JsonNode jb = objectMapper.readTree(resp.getBody()); + assertThat(jb.get("code").asInt()).isZero(); + JsonNode data = jb.get("data"); + assertThat(data.isArray()).isTrue(); + JsonNode rootNode = findById(data, rootId); + assertThat(rootNode).isNotNull(); + JsonNode childNode = findById(rootNode.get("children"), childId); + assertThat(childNode).isNotNull(); + assertThat(childNode.get("sModuleNameZh").asText()).isEqualTo("查询用子"); + } + + @Test + void getKeywordMatch_returnsForest() throws Exception { + insertOriginal("sp_test_get_kw_a", "系统模块A", "ORIG"); + insertOriginal("sp_test_get_kw_b", "用户模块B", "ORIG"); + String token = testJwtHelper.signFor("ADMIN001"); + HttpHeaders headers = jsonHeaders(); + headers.set("Authorization", "Bearer " + token); + + ResponseEntity resp = rest.exchange( + listUrl("系统"), HttpMethod.GET, new HttpEntity<>(headers), String.class); + + JsonNode jb = objectMapper.readTree(resp.getBody()); + assertThat(jb.get("code").asInt()).isZero(); + JsonNode data = jb.get("data"); + assertThat(data.isArray()).isTrue(); + for (JsonNode node : data) { + assertThat(node.get("sModuleNameZh").asText()).contains("系统"); + } + } + + @Test + void getKeywordTooLong_returns40001() throws Exception { + String token = testJwtHelper.signFor("ADMIN001"); + HttpHeaders headers = jsonHeaders(); + headers.set("Authorization", "Bearer " + token); + + ResponseEntity resp = rest.exchange( + listUrl("x".repeat(101)), HttpMethod.GET, new HttpEntity<>(headers), String.class); + + assertThat(objectMapper.readTree(resp.getBody()).get("code").asInt()).isEqualTo(40001); + } + + @Test + void getNoMatch_returnsEmptyArray() throws Exception { + String token = testJwtHelper.signFor("ADMIN001"); + HttpHeaders headers = jsonHeaders(); + headers.set("Authorization", "Bearer " + token); + + ResponseEntity resp = rest.exchange( + listUrl("不存在的关键字XYZ-zzz"), HttpMethod.GET, new HttpEntity<>(headers), String.class); + + JsonNode data = objectMapper.readTree(resp.getBody()).get("data"); + assertThat(data.isArray()).isTrue(); + assertThat(data.size()).isZero(); + } + + @Test + void getWithoutJwt_permitAllStub_returns200() throws Exception { + HttpHeaders headers = jsonHeaders(); + ResponseEntity resp = rest.exchange( + listUrl(null), HttpMethod.GET, new HttpEntity<>(headers), String.class); + assertThat(objectMapper.readTree(resp.getBody()).get("code").asInt()).isZero(); + } + + @Test + void getTamperedJwt_returns20001() throws Exception { + HttpHeaders headers = jsonHeaders(); + headers.set("Authorization", "Bearer not.a.real.jwt"); + ResponseEntity resp = rest.exchange( + listUrl(null), HttpMethod.GET, new HttpEntity<>(headers), String.class); + assertThat(objectMapper.readTree(resp.getBody()).get("code").asInt()).isEqualTo(20001); + } + + private String listUrl(String keyword) { + String base = "http://localhost:" + port + "/api/mod/modules"; + return keyword == null ? base : base + "?keyword=" + java.net.URLEncoder.encode(keyword, java.nio.charset.StandardCharsets.UTF_8); + } + + private static JsonNode findById(JsonNode array, Integer id) { + if (array == null || !array.isArray()) return null; + for (JsonNode n : array) { + if (n.get("iIncrement").asInt() == id) return n; + } + return null; + } + private Integer insertOriginal(String procedureName, String nameZh, String createdBy) { jdbcTemplate.update( "INSERT INTO tModule (sBrandsId, sSubsidiaryId, tCreateDate, sDisplayType, sProcedureName, "