diff --git a/backend/src/main/java/com/xly/erp/modules/usr/controller/UsrUserController.java b/backend/src/main/java/com/xly/erp/modules/usr/controller/UsrUserController.java index ed243ca..2ac6132 100644 --- a/backend/src/main/java/com/xly/erp/modules/usr/controller/UsrUserController.java +++ b/backend/src/main/java/com/xly/erp/modules/usr/controller/UsrUserController.java @@ -5,10 +5,13 @@ import com.xly.erp.common.response.Result; import com.xly.erp.common.response.ResultCode; import com.xly.erp.common.security.SecurityUtil; import com.xly.erp.modules.usr.dto.CreateUserDTO; +import com.xly.erp.modules.usr.dto.UpdateUserDTO; import com.xly.erp.modules.usr.service.UsrUserService; import jakarta.validation.Valid; import java.util.Map; +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.RestController; @@ -42,4 +45,17 @@ public class UsrUserController { Integer newId = usrUserService.createUser(dto); return Result.success(Map.of("id", newId)); } + + /** + * 修改用户:仅超级管理员可调用(spec § 3.9,先于业务校验)。REQ-USR-002。 + */ + @PutMapping("/users/{id}") + public Result> updateUser(@PathVariable Integer id, + @Valid @RequestBody UpdateUserDTO dto) { + if (!ADMIN_USER_TYPE.equals(SecurityUtil.currentUserType())) { + throw new BusinessException(ResultCode.FORBIDDEN); + } + Integer updatedId = usrUserService.updateUser(id, dto); + return Result.success(Map.of("id", updatedId)); + } } diff --git a/backend/src/test/java/com/xly/erp/modules/usr/controller/UsrUserControllerTest.java b/backend/src/test/java/com/xly/erp/modules/usr/controller/UsrUserControllerTest.java index ca94211..7842577 100644 --- a/backend/src/test/java/com/xly/erp/modules/usr/controller/UsrUserControllerTest.java +++ b/backend/src/test/java/com/xly/erp/modules/usr/controller/UsrUserControllerTest.java @@ -4,14 +4,19 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.ArgumentMatchers.eq; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import com.fasterxml.jackson.databind.ObjectMapper; +import com.xly.erp.common.exception.BusinessException; import com.xly.erp.common.exception.GlobalExceptionHandler; +import com.xly.erp.common.response.ResultCode; import com.xly.erp.common.security.SecurityUtil; import com.xly.erp.modules.usr.dto.CreateUserDTO; +import com.xly.erp.modules.usr.dto.UpdateUserDTO; import com.xly.erp.modules.usr.service.UsrUserService; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -99,4 +104,68 @@ class UsrUserControllerTest { .andExpect(jsonPath("$.code").value(40001)); verify(usrUserService, never()).createUser(any(CreateUserDTO.class)); } + + // ---------------- REQ-USR-002 T4:修改用户 Controller + 管理员前置 ---------------- + + private String validUpdateBody() throws Exception { + UpdateUserDTO dto = new UpdateUserDTO(); + dto.setSUserType("普通用户"); + dto.setSLanguage("中文"); + return objectMapper.writeValueAsString(dto); + } + + @Test + void adminUpdateReturnsCodeZeroWithId() throws Exception { + securityUtilMock.when(SecurityUtil::currentUserType).thenReturn("超级管理员"); + when(usrUserService.updateUser(eq(55), any(UpdateUserDTO.class))).thenReturn(55); + + mockMvc.perform(put("/api/usr/users/55") + .contentType("application/json") + .content(validUpdateBody())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(0)) + .andExpect(jsonPath("$.data.id").value(55)) + .andExpect(jsonPath("$.data.sPassword").doesNotExist()) + .andExpect(jsonPath("$.data.password").doesNotExist()); + } + + @Test + void nonAdminUpdateReturns40301() throws Exception { + securityUtilMock.when(SecurityUtil::currentUserType).thenReturn("普通用户"); + + mockMvc.perform(put("/api/usr/users/55") + .contentType("application/json") + .content(validUpdateBody())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(40301)); + verify(usrUserService, never()).updateUser(any(), any(UpdateUserDTO.class)); + } + + @Test + void invalidBodyUpdateReturns40001() throws Exception { + securityUtilMock.when(SecurityUtil::currentUserType).thenReturn("超级管理员"); + UpdateUserDTO dto = new UpdateUserDTO(); + dto.setSLanguage("中文"); // 缺 sUserType → @NotBlank 失败 + String body = objectMapper.writeValueAsString(dto); + + mockMvc.perform(put("/api/usr/users/55") + .contentType("application/json") + .content(body)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(40001)); + verify(usrUserService, never()).updateUser(any(), any(UpdateUserDTO.class)); + } + + @Test + void userNotFoundReturns40401() throws Exception { + securityUtilMock.when(SecurityUtil::currentUserType).thenReturn("超级管理员"); + when(usrUserService.updateUser(eq(404), any(UpdateUserDTO.class))) + .thenThrow(new BusinessException(ResultCode.NOT_FOUND)); + + mockMvc.perform(put("/api/usr/users/404") + .contentType("application/json") + .content(validUpdateBody())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(40401)); + } }