Commit 5ebb96abf86dbdd817361525db999509e724bddd
1 parent
9e549f65
feat(usr): 修改用户 Controller 与管理员权限前置 REQ-USR-002
Showing
2 changed files
with
85 additions
and
0 deletions
backend/src/main/java/com/xly/erp/modules/usr/controller/UsrUserController.java
| ... | ... | @@ -5,10 +5,13 @@ import com.xly.erp.common.response.Result; |
| 5 | 5 | import com.xly.erp.common.response.ResultCode; |
| 6 | 6 | import com.xly.erp.common.security.SecurityUtil; |
| 7 | 7 | import com.xly.erp.modules.usr.dto.CreateUserDTO; |
| 8 | +import com.xly.erp.modules.usr.dto.UpdateUserDTO; | |
| 8 | 9 | import com.xly.erp.modules.usr.service.UsrUserService; |
| 9 | 10 | import jakarta.validation.Valid; |
| 10 | 11 | import java.util.Map; |
| 12 | +import org.springframework.web.bind.annotation.PathVariable; | |
| 11 | 13 | import org.springframework.web.bind.annotation.PostMapping; |
| 14 | +import org.springframework.web.bind.annotation.PutMapping; | |
| 12 | 15 | import org.springframework.web.bind.annotation.RequestBody; |
| 13 | 16 | import org.springframework.web.bind.annotation.RequestMapping; |
| 14 | 17 | import org.springframework.web.bind.annotation.RestController; |
| ... | ... | @@ -42,4 +45,17 @@ public class UsrUserController { |
| 42 | 45 | Integer newId = usrUserService.createUser(dto); |
| 43 | 46 | return Result.success(Map.of("id", newId)); |
| 44 | 47 | } |
| 48 | + | |
| 49 | + /** | |
| 50 | + * 修改用户:仅超级管理员可调用(spec § 3.9,先于业务校验)。REQ-USR-002。 | |
| 51 | + */ | |
| 52 | + @PutMapping("/users/{id}") | |
| 53 | + public Result<Map<String, Object>> updateUser(@PathVariable Integer id, | |
| 54 | + @Valid @RequestBody UpdateUserDTO dto) { | |
| 55 | + if (!ADMIN_USER_TYPE.equals(SecurityUtil.currentUserType())) { | |
| 56 | + throw new BusinessException(ResultCode.FORBIDDEN); | |
| 57 | + } | |
| 58 | + Integer updatedId = usrUserService.updateUser(id, dto); | |
| 59 | + return Result.success(Map.of("id", updatedId)); | |
| 60 | + } | |
| 45 | 61 | } | ... | ... |
backend/src/test/java/com/xly/erp/modules/usr/controller/UsrUserControllerTest.java
| ... | ... | @@ -4,14 +4,19 @@ import static org.mockito.ArgumentMatchers.any; |
| 4 | 4 | import static org.mockito.Mockito.never; |
| 5 | 5 | import static org.mockito.Mockito.verify; |
| 6 | 6 | import static org.mockito.Mockito.when; |
| 7 | +import static org.mockito.ArgumentMatchers.eq; | |
| 7 | 8 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; |
| 9 | +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; | |
| 8 | 10 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; |
| 9 | 11 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
| 10 | 12 | |
| 11 | 13 | import com.fasterxml.jackson.databind.ObjectMapper; |
| 14 | +import com.xly.erp.common.exception.BusinessException; | |
| 12 | 15 | import com.xly.erp.common.exception.GlobalExceptionHandler; |
| 16 | +import com.xly.erp.common.response.ResultCode; | |
| 13 | 17 | import com.xly.erp.common.security.SecurityUtil; |
| 14 | 18 | import com.xly.erp.modules.usr.dto.CreateUserDTO; |
| 19 | +import com.xly.erp.modules.usr.dto.UpdateUserDTO; | |
| 15 | 20 | import com.xly.erp.modules.usr.service.UsrUserService; |
| 16 | 21 | import org.junit.jupiter.api.AfterEach; |
| 17 | 22 | import org.junit.jupiter.api.BeforeEach; |
| ... | ... | @@ -99,4 +104,68 @@ class UsrUserControllerTest { |
| 99 | 104 | .andExpect(jsonPath("$.code").value(40001)); |
| 100 | 105 | verify(usrUserService, never()).createUser(any(CreateUserDTO.class)); |
| 101 | 106 | } |
| 107 | + | |
| 108 | + // ---------------- REQ-USR-002 T4:修改用户 Controller + 管理员前置 ---------------- | |
| 109 | + | |
| 110 | + private String validUpdateBody() throws Exception { | |
| 111 | + UpdateUserDTO dto = new UpdateUserDTO(); | |
| 112 | + dto.setSUserType("普通用户"); | |
| 113 | + dto.setSLanguage("中文"); | |
| 114 | + return objectMapper.writeValueAsString(dto); | |
| 115 | + } | |
| 116 | + | |
| 117 | + @Test | |
| 118 | + void adminUpdateReturnsCodeZeroWithId() throws Exception { | |
| 119 | + securityUtilMock.when(SecurityUtil::currentUserType).thenReturn("超级管理员"); | |
| 120 | + when(usrUserService.updateUser(eq(55), any(UpdateUserDTO.class))).thenReturn(55); | |
| 121 | + | |
| 122 | + mockMvc.perform(put("/api/usr/users/55") | |
| 123 | + .contentType("application/json") | |
| 124 | + .content(validUpdateBody())) | |
| 125 | + .andExpect(status().isOk()) | |
| 126 | + .andExpect(jsonPath("$.code").value(0)) | |
| 127 | + .andExpect(jsonPath("$.data.id").value(55)) | |
| 128 | + .andExpect(jsonPath("$.data.sPassword").doesNotExist()) | |
| 129 | + .andExpect(jsonPath("$.data.password").doesNotExist()); | |
| 130 | + } | |
| 131 | + | |
| 132 | + @Test | |
| 133 | + void nonAdminUpdateReturns40301() throws Exception { | |
| 134 | + securityUtilMock.when(SecurityUtil::currentUserType).thenReturn("普通用户"); | |
| 135 | + | |
| 136 | + mockMvc.perform(put("/api/usr/users/55") | |
| 137 | + .contentType("application/json") | |
| 138 | + .content(validUpdateBody())) | |
| 139 | + .andExpect(status().isOk()) | |
| 140 | + .andExpect(jsonPath("$.code").value(40301)); | |
| 141 | + verify(usrUserService, never()).updateUser(any(), any(UpdateUserDTO.class)); | |
| 142 | + } | |
| 143 | + | |
| 144 | + @Test | |
| 145 | + void invalidBodyUpdateReturns40001() throws Exception { | |
| 146 | + securityUtilMock.when(SecurityUtil::currentUserType).thenReturn("超级管理员"); | |
| 147 | + UpdateUserDTO dto = new UpdateUserDTO(); | |
| 148 | + dto.setSLanguage("中文"); // 缺 sUserType → @NotBlank 失败 | |
| 149 | + String body = objectMapper.writeValueAsString(dto); | |
| 150 | + | |
| 151 | + mockMvc.perform(put("/api/usr/users/55") | |
| 152 | + .contentType("application/json") | |
| 153 | + .content(body)) | |
| 154 | + .andExpect(status().isOk()) | |
| 155 | + .andExpect(jsonPath("$.code").value(40001)); | |
| 156 | + verify(usrUserService, never()).updateUser(any(), any(UpdateUserDTO.class)); | |
| 157 | + } | |
| 158 | + | |
| 159 | + @Test | |
| 160 | + void userNotFoundReturns40401() throws Exception { | |
| 161 | + securityUtilMock.when(SecurityUtil::currentUserType).thenReturn("超级管理员"); | |
| 162 | + when(usrUserService.updateUser(eq(404), any(UpdateUserDTO.class))) | |
| 163 | + .thenThrow(new BusinessException(ResultCode.NOT_FOUND)); | |
| 164 | + | |
| 165 | + mockMvc.perform(put("/api/usr/users/404") | |
| 166 | + .contentType("application/json") | |
| 167 | + .content(validUpdateBody())) | |
| 168 | + .andExpect(status().isOk()) | |
| 169 | + .andExpect(jsonPath("$.code").value(40401)); | |
| 170 | + } | |
| 102 | 171 | } | ... | ... |