From f3a18fdf372079a6a4891879413237e59eaa927b Mon Sep 17 00:00:00 2001 From: 10 Date: Thu, 19 Dec 2024 09:03:33 +0900 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20=EC=9C=A0=EC=8B=A4=EB=AC=BC=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20API?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80=20(#296)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :sparkles: 유실물 상태 업데이트 API 추가 (#295) * :recycle: import문 최적화 (#295) * :recycle: command에서 status로 변경 (#295) * :recycle: 유실물 상태 변경 시, Lost112 예외 처리 (#295) --- application/src/docs/asciidoc/lost/posts.adoc | 15 ++++++ .../lost/adapter/web/in/LostPostController.kt | 9 ++++ .../web/in/dto/UpdateLostPostStatusDto.kt | 31 +++++++++++ .../application/port/in/LostPostUseCase.kt | 3 ++ .../application/service/LostPostService.kt | 18 +++++++ .../web/in/LostPostControllerDocsTest.kt | 45 ++++++++++++++++ .../service/LostPostServiceTest.kt | 53 +++++++++++++++++-- .../command/in/UpdateLostPostStatusCommand.kt | 8 +++ .../api/lost/domain/entity/LostPostEntity.kt | 4 ++ 9 files changed, 183 insertions(+), 3 deletions(-) create mode 100644 application/src/main/kotlin/backend/team/ahachul_backend/api/lost/adapter/web/in/dto/UpdateLostPostStatusDto.kt create mode 100644 core/src/main/kotlin/backend/team/ahachul_backend/api/lost/application/command/in/UpdateLostPostStatusCommand.kt diff --git a/application/src/docs/asciidoc/lost/posts.adoc b/application/src/docs/asciidoc/lost/posts.adoc index acca010d..7e903667 100644 --- a/application/src/docs/asciidoc/lost/posts.adoc +++ b/application/src/docs/asciidoc/lost/posts.adoc @@ -52,6 +52,21 @@ include::{snippets}/update-lost-post/http-response.adoc[] .Response Fields include::{snippets}/update-lost-post/response-fields.adoc[] +==== 유실물 상태 업데이트 + +.Request +include::{snippets}/update-lost-post-status/http-request.adoc[] +.Request Headers +include::{snippets}/update-lost-post-status/request-headers.adoc[] +.Path Parameters +include::{snippets}/update-lost-post-status/path-parameters.adoc[] +.Request Fields +include::{snippets}/update-lost-post-status/request-fields.adoc[] +.Response +include::{snippets}/update-lost-post-status/http-response.adoc[] +.Response Fields +include::{snippets}/update-lost-post-status/response-fields.adoc[] + ==== 유실물 삭제 .Request diff --git a/application/src/main/kotlin/backend/team/ahachul_backend/api/lost/adapter/web/in/LostPostController.kt b/application/src/main/kotlin/backend/team/ahachul_backend/api/lost/adapter/web/in/LostPostController.kt index 64b4fe3b..e9945d71 100644 --- a/application/src/main/kotlin/backend/team/ahachul_backend/api/lost/adapter/web/in/LostPostController.kt +++ b/application/src/main/kotlin/backend/team/ahachul_backend/api/lost/adapter/web/in/LostPostController.kt @@ -47,6 +47,15 @@ class LostPostController( return CommonResponse.success(lostPostService.updateLostPost(request.toCommand(lostId, imageFiles))) } + @Authentication + @PatchMapping("/v1/lost-posts/{lostId}/status") + fun updateLostPostStatus( + @PathVariable("lostId") lostId: Long, + @RequestBody request: UpdateLostPostStatusDto.Request + ): CommonResponse { + return CommonResponse.success(lostPostService.updateLostPostStatus(request.toCommand(lostId))) + } + @Authentication @DeleteMapping("/v1/lost-posts/{lostId}") fun deleteLostPost(@PathVariable("lostId") lostId: Long): CommonResponse { diff --git a/application/src/main/kotlin/backend/team/ahachul_backend/api/lost/adapter/web/in/dto/UpdateLostPostStatusDto.kt b/application/src/main/kotlin/backend/team/ahachul_backend/api/lost/adapter/web/in/dto/UpdateLostPostStatusDto.kt new file mode 100644 index 00000000..8d9dc5a7 --- /dev/null +++ b/application/src/main/kotlin/backend/team/ahachul_backend/api/lost/adapter/web/in/dto/UpdateLostPostStatusDto.kt @@ -0,0 +1,31 @@ +package backend.team.ahachul_backend.api.lost.adapter.web.`in`.dto + +import backend.team.ahachul_backend.api.lost.application.service.command.`in`.UpdateLostPostStatusCommand +import backend.team.ahachul_backend.api.lost.domain.entity.LostPostEntity +import backend.team.ahachul_backend.api.lost.domain.model.LostStatus + +class UpdateLostPostStatusDto { + + data class Request( + val status: LostStatus + ) { + fun toCommand(id: Long): UpdateLostPostStatusCommand { + return UpdateLostPostStatusCommand( + id = id, + status = status + ) + } + } + + data class Response( + val id: Long + ) { + companion object { + fun from(entity: LostPostEntity): Response { + return Response( + id = entity.id + ) + } + } + } +} diff --git a/application/src/main/kotlin/backend/team/ahachul_backend/api/lost/application/port/in/LostPostUseCase.kt b/application/src/main/kotlin/backend/team/ahachul_backend/api/lost/application/port/in/LostPostUseCase.kt index 4e88f7c8..2b97e94a 100644 --- a/application/src/main/kotlin/backend/team/ahachul_backend/api/lost/application/port/in/LostPostUseCase.kt +++ b/application/src/main/kotlin/backend/team/ahachul_backend/api/lost/application/port/in/LostPostUseCase.kt @@ -1,6 +1,7 @@ package backend.team.ahachul_backend.api.lost.application.port.`in` import backend.team.ahachul_backend.api.lost.adapter.web.`in`.dto.* +import backend.team.ahachul_backend.api.lost.application.service.command.`in`.UpdateLostPostStatusCommand import backend.team.ahachul_backend.api.lost.application.service.command.`in`.CreateLostPostCommand import backend.team.ahachul_backend.api.lost.application.service.command.`in`.SearchLostPostCommand import backend.team.ahachul_backend.api.lost.application.service.command.`in`.UpdateLostPostCommand @@ -16,5 +17,7 @@ interface LostPostUseCase { fun updateLostPost(command: UpdateLostPostCommand): UpdateLostPostDto.Response + fun updateLostPostStatus(command: UpdateLostPostStatusCommand): UpdateLostPostStatusDto.Response + fun deleteLostPost(id: Long): DeleteLostPostDto.Response } diff --git a/application/src/main/kotlin/backend/team/ahachul_backend/api/lost/application/service/LostPostService.kt b/application/src/main/kotlin/backend/team/ahachul_backend/api/lost/application/service/LostPostService.kt index c1415126..e71583ce 100644 --- a/application/src/main/kotlin/backend/team/ahachul_backend/api/lost/application/service/LostPostService.kt +++ b/application/src/main/kotlin/backend/team/ahachul_backend/api/lost/application/service/LostPostService.kt @@ -10,6 +10,7 @@ import backend.team.ahachul_backend.api.lost.application.port.out.LostPostWriter import backend.team.ahachul_backend.api.lost.application.service.command.`in`.CreateLostPostCommand import backend.team.ahachul_backend.api.lost.application.service.command.`in`.SearchLostPostCommand import backend.team.ahachul_backend.api.lost.application.service.command.`in`.UpdateLostPostCommand +import backend.team.ahachul_backend.api.lost.application.service.command.`in`.UpdateLostPostStatusCommand import backend.team.ahachul_backend.api.lost.application.service.command.out.GetRecommendLostPostsCommand import backend.team.ahachul_backend.api.lost.application.service.command.out.GetSliceLostPostsCommand import backend.team.ahachul_backend.api.lost.domain.entity.CategoryEntity @@ -20,7 +21,9 @@ import backend.team.ahachul_backend.api.member.application.port.out.MemberReader import backend.team.ahachul_backend.common.domain.entity.SubwayLineEntity import backend.team.ahachul_backend.common.dto.ImageDto import backend.team.ahachul_backend.common.dto.PageInfoDto +import backend.team.ahachul_backend.common.exception.CommonException import backend.team.ahachul_backend.common.persistence.SubwayLineReader +import backend.team.ahachul_backend.common.response.ResponseCode import backend.team.ahachul_backend.common.utils.RequestUtils import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -193,6 +196,21 @@ class LostPostService( } } + @Transactional + override fun updateLostPostStatus(command: UpdateLostPostStatusCommand): UpdateLostPostStatusDto.Response { + val memberId = RequestUtils.getAttribute("memberId")!! + val entity = lostPostReader.getLostPost(command.id) + + if (entity.origin == LostOrigin.LOST112) { + throw CommonException(ResponseCode.BAD_REQUEST) + } + + entity.checkMe(memberId) + + entity.updateStatus(command.status) + return UpdateLostPostStatusDto.Response.from(entity) + } + @Transactional override fun deleteLostPost(id: Long): DeleteLostPostDto.Response { val memberId = RequestUtils.getAttribute("memberId")!! diff --git a/application/src/test/kotlin/backend/team/ahachul_backend/api/lost/adapter/web/in/LostPostControllerDocsTest.kt b/application/src/test/kotlin/backend/team/ahachul_backend/api/lost/adapter/web/in/LostPostControllerDocsTest.kt index e84721f3..ece2d29f 100644 --- a/application/src/test/kotlin/backend/team/ahachul_backend/api/lost/adapter/web/in/LostPostControllerDocsTest.kt +++ b/application/src/test/kotlin/backend/team/ahachul_backend/api/lost/adapter/web/in/LostPostControllerDocsTest.kt @@ -332,6 +332,51 @@ class LostPostControllerDocsTest: CommonDocsTestConfig() { )) } + @Test + fun updateLostPostStatus() { + // given + val response = UpdateLostPostStatusDto.Response( + id = 1 + ) + + given(lostPostUseCase.updateLostPostStatus(any())) + .willReturn(response) + + val request = UpdateLostPostStatusDto.Request( + status = LostStatus.COMPLETE + ) + + //when + val result = mockMvc.perform( + patch("/v1/lost-posts/{lostId}/status", 1L) + .header("Authorization", "Bearer ") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request)) + .accept(MediaType.APPLICATION_JSON) + ) + + //then + result.andExpect(status().isOk) + .andDo(document("update-lost-post-status", + getDocsRequest(), + getDocsResponse(), + requestHeaders( + headerWithName("Authorization").description("엑세스 토큰") + ), + pathParameters( + parameterWithName("lostId").description("유실물 아이디") + ), + requestFields( + fieldWithPath("status").type(JsonFieldType.STRING).description("유실물 찾기 완료 상태").attributes(getFormatAttribute( "PROGRESS / COMPLETE")) + ), + responseFields( + *commonResponseFields(), + fieldWithPath("result.id").type(JsonFieldType.NUMBER).description("수정한 유실물 아이디") + ) + ) + ) + } + @Test fun deleteLostPost() { // given diff --git a/application/src/test/kotlin/backend/team/ahachul_backend/api/lost/application/service/LostPostServiceTest.kt b/application/src/test/kotlin/backend/team/ahachul_backend/api/lost/application/service/LostPostServiceTest.kt index 8ae94b4e..941d0255 100644 --- a/application/src/test/kotlin/backend/team/ahachul_backend/api/lost/application/service/LostPostServiceTest.kt +++ b/application/src/test/kotlin/backend/team/ahachul_backend/api/lost/application/service/LostPostServiceTest.kt @@ -6,10 +6,10 @@ import backend.team.ahachul_backend.api.lost.application.port.`in`.LostPostUseCa import backend.team.ahachul_backend.api.lost.application.service.command.`in`.CreateLostPostCommand import backend.team.ahachul_backend.api.lost.application.service.command.`in`.SearchLostPostCommand import backend.team.ahachul_backend.api.lost.application.service.command.`in`.UpdateLostPostCommand +import backend.team.ahachul_backend.api.lost.application.service.command.`in`.UpdateLostPostStatusCommand import backend.team.ahachul_backend.api.lost.domain.entity.CategoryEntity -import backend.team.ahachul_backend.api.lost.domain.model.LostPostType -import backend.team.ahachul_backend.api.lost.domain.model.LostStatus -import backend.team.ahachul_backend.api.lost.domain.model.LostType +import backend.team.ahachul_backend.api.lost.domain.entity.LostPostEntity +import backend.team.ahachul_backend.api.lost.domain.model.* import backend.team.ahachul_backend.api.member.adapter.web.out.MemberRepository import backend.team.ahachul_backend.api.member.domain.entity.MemberEntity import backend.team.ahachul_backend.api.member.domain.model.GenderType @@ -212,6 +212,53 @@ class LostPostServiceTest( .hasMessage(ResponseCode.INVALID_AUTH.message) } + @Test + @DisplayName("유실물 상태 수정 테스트") + fun updateLostPostStatus() { + // given + val createCommand = createLostPostCommand(subwayLine.id, "내용", "휴대폰") + val entity = lostPostUseCase.createLostPost(createCommand) + + val updateCommand = UpdateLostPostStatusCommand( + id = entity.id, + status = LostStatus.COMPLETE + ) + + // when + val response = lostPostUseCase.updateLostPostStatus(updateCommand) + + // then + assertThat(response.id).isEqualTo(entity.id) + } + + @Test + @DisplayName("유실물 상태 수정 Lost112 에러") + fun updateLostPostStatusError() { + // given + val entity = lostPostRepository.save( + LostPostEntity( + title = "제목", + content = "내용", + subwayLine = subwayLine, + lostType = LostType.LOST, + category = category, + origin = LostOrigin.LOST112 + ) + ) + + val updateCommand = UpdateLostPostStatusCommand( + id = entity.id, + status = LostStatus.COMPLETE + ) + + // when & then + assertThatThrownBy { + lostPostUseCase.updateLostPostStatus(updateCommand) + } + .isExactlyInstanceOf(CommonException::class.java) + .hasMessage(ResponseCode.BAD_REQUEST.message) + } + @Test @DisplayName("유실물 삭제 테스트 - 권한이 있는 경우") fun deleteLostPost() { diff --git a/core/src/main/kotlin/backend/team/ahachul_backend/api/lost/application/command/in/UpdateLostPostStatusCommand.kt b/core/src/main/kotlin/backend/team/ahachul_backend/api/lost/application/command/in/UpdateLostPostStatusCommand.kt new file mode 100644 index 00000000..0cc4e5ac --- /dev/null +++ b/core/src/main/kotlin/backend/team/ahachul_backend/api/lost/application/command/in/UpdateLostPostStatusCommand.kt @@ -0,0 +1,8 @@ +package backend.team.ahachul_backend.api.lost.application.service.command.`in` + +import backend.team.ahachul_backend.api.lost.domain.model.LostStatus + +class UpdateLostPostStatusCommand ( + val id: Long, + val status: LostStatus +) diff --git a/core/src/main/kotlin/backend/team/ahachul_backend/api/lost/domain/entity/LostPostEntity.kt b/core/src/main/kotlin/backend/team/ahachul_backend/api/lost/domain/entity/LostPostEntity.kt index 8c4ddd96..23134154 100644 --- a/core/src/main/kotlin/backend/team/ahachul_backend/api/lost/domain/entity/LostPostEntity.kt +++ b/core/src/main/kotlin/backend/team/ahachul_backend/api/lost/domain/entity/LostPostEntity.kt @@ -108,6 +108,10 @@ class LostPostEntity( category?.let { this.category = category } } + fun updateStatus(status: LostStatus) { + this.status = status + } + fun delete() { type = LostPostType.DELETED }