结构和功能的删除接口
This commit is contained in:
@@ -386,7 +386,43 @@
|
|||||||
|
|
||||||
`CadStructure`
|
`CadStructure`
|
||||||
|
|
||||||
### 4.5 CadStructure 响应字段
|
### 4.5 删除 CAD 结构
|
||||||
|
|
||||||
|
`DELETE /api/cad-models/{modelId}/structures/{structureId}`
|
||||||
|
|
||||||
|
按模型归属校验后逻辑删除结构。删除后该结构不会再出现在结构列表和结构详情中;后端会同步清理该结构关联的建模历史关系,以及功能中引用该结构的关系。
|
||||||
|
|
||||||
|
#### Path 参数
|
||||||
|
|
||||||
|
| 参数 | 类型 | 必填 | 约束 | 说明 |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| `modelId` | number | 是 | 正整数 | 模型 ID |
|
||||||
|
| `structureId` | number | 是 | 正整数 | 结构 ID,必须归属当前模型且未逻辑删除 |
|
||||||
|
|
||||||
|
#### 请求体
|
||||||
|
|
||||||
|
无。
|
||||||
|
|
||||||
|
#### 响应 data
|
||||||
|
|
||||||
|
无。由于响应字段全局忽略 `null`,成功时通常不返回 `data` 字段。
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": "OK",
|
||||||
|
"message": "success",
|
||||||
|
"requestId": "b3f8..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 失败说明
|
||||||
|
|
||||||
|
| HTTP 状态码 | code | 场景 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 404 | `NOT_FOUND` | 模型不存在,或结构不存在、已删除、不是当前模型下的结构 |
|
||||||
|
| 422 | `INVALID_REQUEST` | `modelId` 或 `structureId` 不是正整数 |
|
||||||
|
|
||||||
|
### 4.6 CadStructure 响应字段
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -547,7 +583,43 @@
|
|||||||
|
|
||||||
`CadFunction`
|
`CadFunction`
|
||||||
|
|
||||||
### 5.5 CadFunction 响应字段
|
### 5.5 删除 CAD 功能
|
||||||
|
|
||||||
|
`DELETE /api/cad-models/{modelId}/functions/{functionId}`
|
||||||
|
|
||||||
|
按模型归属校验后逻辑删除功能。删除后该功能不会再出现在功能列表和功能详情中;后端会同步清理该功能下的结构关系。
|
||||||
|
|
||||||
|
#### Path 参数
|
||||||
|
|
||||||
|
| 参数 | 类型 | 必填 | 约束 | 说明 |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| `modelId` | number | 是 | 正整数 | 模型 ID |
|
||||||
|
| `functionId` | number | 是 | 正整数 | 功能 ID,必须归属当前模型且未逻辑删除 |
|
||||||
|
|
||||||
|
#### 请求体
|
||||||
|
|
||||||
|
无。
|
||||||
|
|
||||||
|
#### 响应 data
|
||||||
|
|
||||||
|
无。由于响应字段全局忽略 `null`,成功时通常不返回 `data` 字段。
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": "OK",
|
||||||
|
"message": "success",
|
||||||
|
"requestId": "b3f8..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 失败说明
|
||||||
|
|
||||||
|
| HTTP 状态码 | code | 场景 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 404 | `NOT_FOUND` | 模型不存在,或功能不存在、已删除、不是当前模型下的功能 |
|
||||||
|
| 422 | `INVALID_REQUEST` | `modelId` 或 `functionId` 不是正整数 |
|
||||||
|
|
||||||
|
### 5.6 CadFunction 响应字段
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import jakarta.validation.constraints.Positive;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@@ -94,6 +95,20 @@ public class CadAnnotationController {
|
|||||||
return ApiResponse.success(cadAnnotationService.getStructure(modelId, structureId));
|
return ApiResponse.success(cadAnnotationService.getStructure(modelId, structureId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除 CAD 结构。
|
||||||
|
*
|
||||||
|
* <p>按模型归属校验后逻辑删除结构,并清理结构下的关联关系。</p>
|
||||||
|
*/
|
||||||
|
@DeleteMapping("/structures/{structureId}")
|
||||||
|
public ApiResponse<Void> deleteStructure(
|
||||||
|
@PathVariable @Positive Long modelId,
|
||||||
|
@PathVariable @Positive Long structureId
|
||||||
|
) {
|
||||||
|
cadAnnotationService.deleteStructure(modelId, structureId);
|
||||||
|
return ApiResponse.success(null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建 CAD 功能。
|
* 创建 CAD 功能。
|
||||||
*
|
*
|
||||||
@@ -143,4 +158,18 @@ public class CadAnnotationController {
|
|||||||
) {
|
) {
|
||||||
return ApiResponse.success(cadAnnotationService.getFunction(modelId, functionId));
|
return ApiResponse.success(cadAnnotationService.getFunction(modelId, functionId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除 CAD 功能。
|
||||||
|
*
|
||||||
|
* <p>按模型归属校验后逻辑删除功能,并清理功能下的结构关系。</p>
|
||||||
|
*/
|
||||||
|
@DeleteMapping("/functions/{functionId}")
|
||||||
|
public ApiResponse<Void> deleteFunction(
|
||||||
|
@PathVariable @Positive Long modelId,
|
||||||
|
@PathVariable @Positive Long functionId
|
||||||
|
) {
|
||||||
|
cadAnnotationService.deleteFunction(modelId, functionId);
|
||||||
|
return ApiResponse.success(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,11 @@ public interface CadAnnotationService {
|
|||||||
*/
|
*/
|
||||||
CadStructureResponse getStructure(Long modelId, Long structureId);
|
CadStructureResponse getStructure(Long modelId, Long structureId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 逻辑删除结构,并清理结构关联关系。
|
||||||
|
*/
|
||||||
|
void deleteStructure(Long modelId, Long structureId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建功能并建立功能与结构的关系。
|
* 创建功能并建立功能与结构的关系。
|
||||||
*/
|
*/
|
||||||
@@ -62,4 +67,9 @@ public interface CadAnnotationService {
|
|||||||
* 查询功能详情。
|
* 查询功能详情。
|
||||||
*/
|
*/
|
||||||
CadFunctionResponse getFunction(Long modelId, Long functionId);
|
CadFunctionResponse getFunction(Long modelId, Long functionId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 逻辑删除功能,并清理功能关联关系。
|
||||||
|
*/
|
||||||
|
void deleteFunction(Long modelId, Long functionId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -199,6 +199,31 @@ public class CadAnnotationServiceImpl implements CadAnnotationService {
|
|||||||
return toStructureResponse(requireStructure(modelId, structureId));
|
return toStructureResponse(requireStructure(modelId, structureId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 逻辑删除结构,并清理结构关联的建模历史关系和功能引用关系。
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public void deleteStructure(Long modelId, Long structureId) {
|
||||||
|
requireStructure(modelId, structureId);
|
||||||
|
|
||||||
|
int historyRelationCount = structureHistoryRelMapper.delete(Wrappers.lambdaQuery(CadStructureHistoryRel.class)
|
||||||
|
.eq(CadStructureHistoryRel::getModelId, modelId)
|
||||||
|
.eq(CadStructureHistoryRel::getStructureId, structureId));
|
||||||
|
int functionRelationCount = functionStructureRelMapper.delete(Wrappers.lambdaQuery(CadFunctionStructureRel.class)
|
||||||
|
.eq(CadFunctionStructureRel::getModelId, modelId)
|
||||||
|
.eq(CadFunctionStructureRel::getStructureId, structureId));
|
||||||
|
structureMapper.update(null, Wrappers.lambdaUpdate(CadStructure.class)
|
||||||
|
.eq(CadStructure::getId, structureId)
|
||||||
|
.eq(CadStructure::getModelId, modelId)
|
||||||
|
.eq(CadStructure::getDeleted, 0)
|
||||||
|
.set(CadStructure::getDeleted, 1)
|
||||||
|
.set(CadStructure::getUpdatedAt, LocalDateTime.now()));
|
||||||
|
|
||||||
|
log.info("Deleted CAD structure. modelId={}, structureId={}, historyRelationCount={}, functionRelationCount={}",
|
||||||
|
modelId, structureId, historyRelationCount, functionRelationCount);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建功能并绑定结构。
|
* 创建功能并绑定结构。
|
||||||
*
|
*
|
||||||
@@ -289,6 +314,28 @@ public class CadAnnotationServiceImpl implements CadAnnotationService {
|
|||||||
return toFunctionResponse(requireFunction(modelId, functionId));
|
return toFunctionResponse(requireFunction(modelId, functionId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 逻辑删除功能,并清理功能关联的结构关系。
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public void deleteFunction(Long modelId, Long functionId) {
|
||||||
|
requireFunction(modelId, functionId);
|
||||||
|
|
||||||
|
int structureRelationCount = functionStructureRelMapper.delete(Wrappers.lambdaQuery(CadFunctionStructureRel.class)
|
||||||
|
.eq(CadFunctionStructureRel::getModelId, modelId)
|
||||||
|
.eq(CadFunctionStructureRel::getFunctionId, functionId));
|
||||||
|
functionMapper.update(null, Wrappers.lambdaUpdate(CadFunction.class)
|
||||||
|
.eq(CadFunction::getId, functionId)
|
||||||
|
.eq(CadFunction::getModelId, modelId)
|
||||||
|
.eq(CadFunction::getDeleted, 0)
|
||||||
|
.set(CadFunction::getDeleted, 1)
|
||||||
|
.set(CadFunction::getUpdatedAt, LocalDateTime.now()));
|
||||||
|
|
||||||
|
log.info("Deleted CAD function. modelId={}, functionId={}, structureRelationCount={}",
|
||||||
|
modelId, functionId, structureRelationCount);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验模型存在并返回模型记录。
|
* 校验模型存在并返回模型记录。
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import static org.mockito.ArgumentMatchers.any;
|
|||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.BDDMockito.given;
|
import static org.mockito.BDDMockito.given;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||||
@@ -145,6 +146,15 @@ class CadAnnotationControllerTest {
|
|||||||
.andExpect(jsonPath("$.data.id").value(100));
|
.andExpect(jsonPath("$.data.id").value(100));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void deleteStructureReturnsUnifiedResponse() throws Exception {
|
||||||
|
mockMvc.perform(delete("/api/cad-models/1/structures/100"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.code").value("OK"));
|
||||||
|
|
||||||
|
verify(cadAnnotationService).deleteStructure(1L, 100L);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void createAndUpdateFunctionBindStructureIds() throws Exception {
|
void createAndUpdateFunctionBindStructureIds() throws Exception {
|
||||||
given(cadAnnotationService.createFunction(eq(1L), any())).willReturn(sampleFunction(200L, "固定功能"));
|
given(cadAnnotationService.createFunction(eq(1L), any())).willReturn(sampleFunction(200L, "固定功能"));
|
||||||
@@ -208,6 +218,15 @@ class CadAnnotationControllerTest {
|
|||||||
.andExpect(jsonPath("$.data.id").value(200));
|
.andExpect(jsonPath("$.data.id").value(200));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void deleteFunctionReturnsUnifiedResponse() throws Exception {
|
||||||
|
mockMvc.perform(delete("/api/cad-models/1/functions/200"))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.code").value("OK"));
|
||||||
|
|
||||||
|
verify(cadAnnotationService).deleteFunction(1L, 200L);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void createStructureRejectsEmptyHistoryIdsBeforeService() throws Exception {
|
void createStructureRejectsEmptyHistoryIdsBeforeService() throws Exception {
|
||||||
mockMvc.perform(post("/api/cad-models/1/structures")
|
mockMvc.perform(post("/api/cad-models/1/structures")
|
||||||
|
|||||||
@@ -262,6 +262,49 @@ class CadAnnotationServiceIntegrationTest {
|
|||||||
)).isEqualTo(1L);
|
)).isEqualTo(1L);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void deleteStructureMarksDeletedAndRemovesRelations() {
|
||||||
|
CadStructureResponse structure = cadAnnotationService.createStructure(1L, new CreateCadStructureRequest(
|
||||||
|
"加强筋",
|
||||||
|
"rib",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
List.of(10L)
|
||||||
|
));
|
||||||
|
CadFunctionResponse cadFunction = cadAnnotationService.createFunction(1L, new CreateCadFunctionRequest(
|
||||||
|
"固定功能",
|
||||||
|
"mounting",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
List.of(structure.id())
|
||||||
|
));
|
||||||
|
|
||||||
|
cadAnnotationService.deleteStructure(1L, structure.id());
|
||||||
|
|
||||||
|
assertThat(jdbcTemplate.queryForObject(
|
||||||
|
"SELECT deleted FROM cad_structure WHERE id = ?",
|
||||||
|
Integer.class,
|
||||||
|
structure.id()
|
||||||
|
)).isEqualTo(1);
|
||||||
|
assertThat(cadAnnotationService.listStructures(1L)).isEmpty();
|
||||||
|
assertThat(cadAnnotationService.getFunction(1L, cadFunction.id()).structureItems()).isEmpty();
|
||||||
|
assertThat(jdbcTemplate.queryForObject(
|
||||||
|
"SELECT COUNT(*) FROM cad_structure_history_rel WHERE structure_id = ?",
|
||||||
|
Long.class,
|
||||||
|
structure.id()
|
||||||
|
)).isEqualTo(0L);
|
||||||
|
assertThat(jdbcTemplate.queryForObject(
|
||||||
|
"SELECT COUNT(*) FROM cad_function_structure_rel WHERE structure_id = ?",
|
||||||
|
Long.class,
|
||||||
|
structure.id()
|
||||||
|
)).isEqualTo(0L);
|
||||||
|
assertThatThrownBy(() -> cadAnnotationService.getStructure(1L, structure.id()))
|
||||||
|
.isInstanceOf(BizException.class)
|
||||||
|
.hasMessage("结构不存在");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void createAndUpdateFunctionReplaceStructureRelations() {
|
void createAndUpdateFunctionReplaceStructureRelations() {
|
||||||
CadStructureResponse first = cadAnnotationService.createStructure(1L, new CreateCadStructureRequest(
|
CadStructureResponse first = cadAnnotationService.createStructure(1L, new CreateCadStructureRequest(
|
||||||
@@ -308,6 +351,43 @@ class CadAnnotationServiceIntegrationTest {
|
|||||||
assertThat(updated.structureItems()).extracting(item -> item.structureId()).containsExactly(first.id());
|
assertThat(updated.structureItems()).extracting(item -> item.structureId()).containsExactly(first.id());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void deleteFunctionMarksDeletedAndRemovesRelations() {
|
||||||
|
CadStructureResponse structure = cadAnnotationService.createStructure(1L, new CreateCadStructureRequest(
|
||||||
|
"加强筋",
|
||||||
|
"rib",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
List.of(10L)
|
||||||
|
));
|
||||||
|
CadFunctionResponse cadFunction = cadAnnotationService.createFunction(1L, new CreateCadFunctionRequest(
|
||||||
|
"固定功能",
|
||||||
|
"mounting",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
List.of(structure.id())
|
||||||
|
));
|
||||||
|
|
||||||
|
cadAnnotationService.deleteFunction(1L, cadFunction.id());
|
||||||
|
|
||||||
|
assertThat(jdbcTemplate.queryForObject(
|
||||||
|
"SELECT deleted FROM cad_function WHERE id = ?",
|
||||||
|
Integer.class,
|
||||||
|
cadFunction.id()
|
||||||
|
)).isEqualTo(1);
|
||||||
|
assertThat(cadAnnotationService.listFunctions(1L)).isEmpty();
|
||||||
|
assertThat(jdbcTemplate.queryForObject(
|
||||||
|
"SELECT COUNT(*) FROM cad_function_structure_rel WHERE function_id = ?",
|
||||||
|
Long.class,
|
||||||
|
cadFunction.id()
|
||||||
|
)).isEqualTo(0L);
|
||||||
|
assertThatThrownBy(() -> cadAnnotationService.getFunction(1L, cadFunction.id()))
|
||||||
|
.isInstanceOf(BizException.class)
|
||||||
|
.hasMessage("功能不存在");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void queryStructuresAndFunctionsExcludeDeletedAndKeepRelationOrder() {
|
void queryStructuresAndFunctionsExcludeDeletedAndKeepRelationOrder() {
|
||||||
CadStructureResponse active = cadAnnotationService.createStructure(1L, new CreateCadStructureRequest(
|
CadStructureResponse active = cadAnnotationService.createStructure(1L, new CreateCadStructureRequest(
|
||||||
|
|||||||
Reference in New Issue
Block a user