Commit 716b0b5b884839517ab1d0aba1985662e3ef6e99

Authored by zichun
1 parent fa2044fd

feat(mod): DELETE /api/modules/{id} controller REQ-MOD-003

backend/src/main/java/com/xly/erp/module/mod/controller/ModuleController.java
... ... @@ -4,9 +4,11 @@ import com.xly.erp.common.response.ApiResponse;
4 4 import com.xly.erp.module.mod.dto.ModuleCreateDTO;
5 5 import com.xly.erp.module.mod.dto.ModuleUpdateDTO;
6 6 import com.xly.erp.module.mod.service.ModuleService;
  7 +import com.xly.erp.module.mod.vo.ModuleDeleteResultVO;
7 8 import com.xly.erp.module.mod.vo.ModuleVO;
8 9 import jakarta.validation.Valid;
9 10 import lombok.RequiredArgsConstructor;
  11 +import org.springframework.web.bind.annotation.DeleteMapping;
10 12 import org.springframework.web.bind.annotation.PathVariable;
11 13 import org.springframework.web.bind.annotation.PostMapping;
12 14 import org.springframework.web.bind.annotation.PutMapping;
... ... @@ -32,4 +34,10 @@ public class ModuleController {
32 34 public ApiResponse<ModuleVO> update(@PathVariable Integer id, @Valid @RequestBody ModuleUpdateDTO dto) {
33 35 return ApiResponse.ok(moduleService.update(id, dto));
34 36 }
  37 +
  38 + /** REQ-MOD-003 模块软删除 — REQ-USR-004 完成后追加 @PreAuthorize("hasAuthority('MOD:DELETE')") */
  39 + @DeleteMapping("/{id}")
  40 + public ApiResponse<ModuleDeleteResultVO> delete(@PathVariable Integer id) {
  41 + return ApiResponse.ok(moduleService.delete(id));
  42 + }
35 43 }
... ...
backend/src/test/java/com/xly/erp/module/mod/controller/ModuleControllerIT.java
... ... @@ -18,6 +18,7 @@ import org.springframework.transaction.annotation.Transactional;
18 18 import java.time.LocalDateTime;
19 19  
20 20 import static org.assertj.core.api.Assertions.assertThat;
  21 +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
21 22 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
22 23 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
23 24 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
... ... @@ -267,6 +268,90 @@ class ModuleControllerIT {
267 268 .andExpect(jsonPath("$.code").value(40010));
268 269 }
269 270  
  271 + // ============================================================
  272 + // REQ-MOD-003 DELETE 系列
  273 + // ============================================================
  274 +
  275 + @Test
  276 + void delete_validLeaf_returns200WithBDeletedTrue() throws Exception {
  277 + Integer id = insertExisting("sp_del_leaf_" + System.nanoTime(), null);
  278 +
  279 + mockMvc.perform(delete("/api/modules/" + id))
  280 + .andExpect(status().isOk())
  281 + .andExpect(jsonPath("$.code").value(200))
  282 + .andExpect(jsonPath("$.data.iIncrement").value(id))
  283 + .andExpect(jsonPath("$.data.bDeleted").value(true));
  284 +
  285 + ModuleEntity reloaded = moduleMapper.selectById(id);
  286 + assertThat(reloaded.getBDeleted()).isTrue();
  287 + assertThat(reloaded.getTDeletedDate()).isNotNull();
  288 + }
  289 +
  290 + @Test
  291 + void delete_targetNotFound_returns40421() throws Exception {
  292 + mockMvc.perform(delete("/api/modules/999999"))
  293 + .andExpect(status().isOk())
  294 + .andExpect(jsonPath("$.code").value(40421));
  295 + }
  296 +
  297 + @Test
  298 + void delete_targetAlreadyDeleted_returns40421() throws Exception {
  299 + Integer id = insertExisting("sp_del_already_" + System.nanoTime(), null);
  300 + // 手工置 bDeleted=true
  301 + ModuleEntity patch = new ModuleEntity();
  302 + patch.setIIncrement(id);
  303 + patch.setBDeleted(true);
  304 + moduleMapper.updateById(patch);
  305 +
  306 + mockMvc.perform(delete("/api/modules/" + id))
  307 + .andExpect(status().isOk())
  308 + .andExpect(jsonPath("$.code").value(40421));
  309 + }
  310 +
  311 + @Test
  312 + void delete_hasUndeletedChildren_returns40912() throws Exception {
  313 + Integer parentId = insertExisting("sp_del_par_" + System.nanoTime(), null);
  314 + insertExisting("sp_del_chi_" + System.nanoTime(), parentId);
  315 +
  316 + mockMvc.perform(delete("/api/modules/" + parentId))
  317 + .andExpect(status().isOk())
  318 + .andExpect(jsonPath("$.code").value(40912));
  319 +
  320 + // parent 仍未删除
  321 + assertThat(moduleMapper.selectById(parentId).getBDeleted()).isFalse();
  322 + }
  323 +
  324 + @Test
  325 + void delete_softDeletedChildren_doesNotBlock_returns200() throws Exception {
  326 + Integer parentId = insertExisting("sp_del_pp_" + System.nanoTime(), null);
  327 + Integer childId = insertExisting("sp_del_cc_" + System.nanoTime(), parentId);
  328 +
  329 + // 先 DELETE child(应成功)
  330 + mockMvc.perform(delete("/api/modules/" + childId))
  331 + .andExpect(status().isOk())
  332 + .andExpect(jsonPath("$.code").value(200));
  333 +
  334 + // 再 DELETE parent(已删除子不阻塞)
  335 + mockMvc.perform(delete("/api/modules/" + parentId))
  336 + .andExpect(status().isOk())
  337 + .andExpect(jsonPath("$.code").value(200));
  338 +
  339 + assertThat(moduleMapper.selectById(parentId).getBDeleted()).isTrue();
  340 + }
  341 +
  342 + @Test
  343 + void delete_responseVOContainsOnlyIIncrementAndBDeleted() throws Exception {
  344 + Integer id = insertExisting("sp_del_vo_" + System.nanoTime(), null);
  345 +
  346 + mockMvc.perform(delete("/api/modules/" + id))
  347 + .andExpect(status().isOk())
  348 + .andExpect(jsonPath("$.data.iIncrement").value(id))
  349 + .andExpect(jsonPath("$.data.bDeleted").value(true))
  350 + .andExpect(jsonPath("$.data.sProcedureName").doesNotExist())
  351 + .andExpect(jsonPath("$.data.sDisplayType").doesNotExist())
  352 + .andExpect(jsonPath("$.data.sModuleNameZh").doesNotExist());
  353 + }
  354 +
270 355 @Test
271 356 void put_ignoresProcedureNameField_doesNotChange() throws Exception {
272 357 String origProc = "sp_put_keep_" + System.nanoTime();
... ...