diff --git a/backend/pom.xml b/backend/pom.xml
index 1662a69..bac8aeb 100644
--- a/backend/pom.xml
+++ b/backend/pom.xml
@@ -99,6 +99,17 @@
org.springframework.boot
spring-boot-maven-plugin
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ **/*Test.java
+ **/*Tests.java
+ **/*IT.java
+
+
+
diff --git a/backend/src/main/java/com/xly/erp/common/security/SecurityContextHelper.java b/backend/src/main/java/com/xly/erp/common/security/SecurityContextHelper.java
index 0e9be2b..d0b49ef 100644
--- a/backend/src/main/java/com/xly/erp/common/security/SecurityContextHelper.java
+++ b/backend/src/main/java/com/xly/erp/common/security/SecurityContextHelper.java
@@ -1,5 +1,6 @@
package com.xly.erp.common.security;
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
@@ -10,7 +11,7 @@ public final class SecurityContextHelper {
public static String currentUserNo() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
- if (auth == null || auth.getPrincipal() == null) {
+ if (auth == null || auth instanceof AnonymousAuthenticationToken) {
return null;
}
Object p = auth.getPrincipal();
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 782a57a..e43ba64 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
@@ -73,6 +73,104 @@ class ModuleControllerIT {
assertThat(row.get("sCreatedBy")).isEqualTo("ADMIN001");
}
+ @Test
+ void postEmptyBody_returns40001_withFieldHint() throws Exception {
+ String token = testJwtHelper.signFor("ADMIN001");
+ HttpHeaders headers = jsonHeaders();
+ headers.set("Authorization", "Bearer " + token);
+
+ ResponseEntity resp = rest.exchange(
+ url(),
+ HttpMethod.POST,
+ new HttpEntity<>("{}", headers),
+ String.class);
+
+ JsonNode body = objectMapper.readTree(resp.getBody());
+ assertThat(body.get("code").asInt()).isEqualTo(40001);
+ assertThat(body.get("msg").asText()).containsAnyOf(
+ "sProcedureName", "sDisplayType", "sModuleType", "sManageDeptEn", "sModuleNameZh");
+ }
+
+ @Test
+ void postInvalidDisplayType_returns40010() throws Exception {
+ String token = testJwtHelper.signFor("ADMIN001");
+ HttpHeaders headers = jsonHeaders();
+ headers.set("Authorization", "Bearer " + token);
+ Map bad = validBody("sp_test_bad_type", "枚举非法");
+ bad.put("sDisplayType", "火星");
+
+ ResponseEntity resp = rest.exchange(
+ url(), HttpMethod.POST, new HttpEntity<>(bad, headers), String.class);
+
+ JsonNode body = objectMapper.readTree(resp.getBody());
+ assertThat(body.get("code").asInt()).isEqualTo(40010);
+ }
+
+ @Test
+ void postDuplicateProcedureName_returns40020() throws Exception {
+ String token = testJwtHelper.signFor("ADMIN001");
+ HttpHeaders headers = jsonHeaders();
+ headers.set("Authorization", "Bearer " + token);
+ Map first = validBody("sp_test_dup", "首次");
+ ResponseEntity r1 = rest.exchange(
+ url(), HttpMethod.POST, new HttpEntity<>(first, headers), String.class);
+ assertThat(objectMapper.readTree(r1.getBody()).get("code").asInt()).isZero();
+
+ Map dup = validBody("sp_test_dup", "重复");
+ ResponseEntity r2 = rest.exchange(
+ url(), HttpMethod.POST, new HttpEntity<>(dup, headers), String.class);
+ JsonNode body = objectMapper.readTree(r2.getBody());
+ assertThat(body.get("code").asInt()).isEqualTo(40020);
+ }
+
+ @Test
+ void postWithMissingParent_returns40021() throws Exception {
+ String token = testJwtHelper.signFor("ADMIN001");
+ HttpHeaders headers = jsonHeaders();
+ headers.set("Authorization", "Bearer " + token);
+ Map orphan = validBody("sp_test_orphan", "缺父");
+ orphan.put("iParentId", 99999999);
+
+ ResponseEntity resp = rest.exchange(
+ url(), HttpMethod.POST, new HttpEntity<>(orphan, headers), String.class);
+
+ JsonNode body = objectMapper.readTree(resp.getBody());
+ assertThat(body.get("code").asInt()).isEqualTo(40021);
+ }
+
+ @Test
+ void postWithoutJwt_permitAllStub_returns200_andCreatedBySTUBADMIN() throws Exception {
+ HttpHeaders headers = jsonHeaders();
+ Map body = validBody("sp_test_nojwt", "无JWT");
+
+ ResponseEntity resp = rest.exchange(
+ url(), HttpMethod.POST, new HttpEntity<>(body, headers), String.class);
+
+ JsonNode jb = objectMapper.readTree(resp.getBody());
+ assertThat(jb.get("code").asInt()).isZero();
+ int newId = jb.get("data").get("iIncrement").asInt();
+ String createdBy = jdbcTemplate.queryForObject(
+ "SELECT sCreatedBy FROM tModule WHERE iIncrement = ?", String.class, newId);
+ assertThat(createdBy).isEqualTo("STUB_ADMIN");
+ }
+
+ @Test
+ void postWithTamperedJwt_returns20001() throws Exception {
+ HttpHeaders headers = jsonHeaders();
+ headers.set("Authorization", "Bearer not.a.real.jwt");
+ Map body = validBody("sp_test_tampered", "伪JWT");
+
+ ResponseEntity resp = rest.exchange(
+ url(), HttpMethod.POST, new HttpEntity<>(body, headers), String.class);
+
+ JsonNode jb = objectMapper.readTree(resp.getBody());
+ assertThat(jb.get("code").asInt()).isEqualTo(20001);
+ }
+
+ private String url() {
+ return "http://localhost:" + port + "/api/mod/modules";
+ }
+
static HttpHeaders jsonHeaders() {
HttpHeaders h = new HttpHeaders();
h.setContentType(MediaType.APPLICATION_JSON);