Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 44 additions & 1 deletion src/main/java/com/dokdok/topic/api/PreOpinionApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
Expand All @@ -27,7 +28,7 @@ public interface PreOpinionApi {
- 권한: 약속의 멤버
- 제약: 본인이 사전 의견을 작성한 경우에만 조회 가능
- 응답: 확정된 주제 목록, 멤버별 사전 의견 (책 평가, 주제 답변)

**응답 구조**
- topics: 확정된 주제 목록 (confirmOrder 순)
- members: 멤버별 사전 의견 (프로필, 책 평가, 주제별 답변)
Expand Down Expand Up @@ -126,4 +127,46 @@ ResponseEntity<ApiResponse<PreOpinionResponse>> findAnswers(
@PathVariable("gatheringId") Long gatheringId,
@PathVariable("meetingId") Long meetingId
);

@Operation(
summary = "내 사전의견 삭제 (developer: 경서영)",
description = """
현재 로그인 사용자의 약속에 대한 사전의견을 삭제합니다.
- 권한: 약속의 멤버
""",
parameters = {
@Parameter(name = "gatheringId", description = "모임 식별자", in = ParameterIn.PATH, required = true),
@Parameter(name = "meetingId", description = "약속 식별자", in = ParameterIn.PATH, required = true)
}
)
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(
responseCode = "200",
description = "내 사전의견 삭제 성공",
content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
examples = @ExampleObject(value = """
{"code":"DELETED","message":"내 사전의견이 삭제되었습니다.","data":null}
"""))
),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "403", description = "모임 멤버가 아님",
content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
examples = @ExampleObject(value = """
{"code": "G002", "message": "모임의 멤버가 아닙니다.", "data": null}
"""))),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "답변을 찾을 수 없음",
content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
examples = @ExampleObject(value = """
{"code": "E103", "message": "답변을 찾을 수 없습니다.", "data": null}
"""))),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "서버 오류",
content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
examples = @ExampleObject(value = """
{"code": "E000", "message": "서버 에러가 발생했습니다. 담당자에게 문의 바랍니다.", "data": null}
""")))
})
@DeleteMapping(value = "/me", produces = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<ApiResponse<Void>> deleteMyAnswer(
@PathVariable("gatheringId") Long gatheringId,
@PathVariable("meetingId") Long meetingId
);
}
39 changes: 0 additions & 39 deletions src/main/java/com/dokdok/topic/api/TopicAnswerApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -226,43 +226,4 @@ ResponseEntity<ApiResponse<TopicAnswerSubmitResponse>> submitMyAnswer(
@PathVariable("topicId") Long topicId
);

@Operation(
summary = "내 토픽 답변 삭제 (developer: 경서영)",
description = """
현재 로그인 사용자의 토픽 답변을 삭제합니다.
- 권한: 모임 구성원
""",
parameters = {
@Parameter(name = "gatheringId", description = "모임 식별자", in = ParameterIn.PATH, required = true),
@Parameter(name = "meetingId", description = "약속 식별자", in = ParameterIn.PATH, required = true),
@Parameter(name = "topicId", description = "토픽 식별자", in = ParameterIn.PATH, required = true)
}
)
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(
responseCode = "200",
description = "내 토픽 답변 삭제 성공"
),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "403", description = "모임 멤버가 아님",
content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
examples = @ExampleObject(value = """
{"code": "G002", "message": "모임의 멤버가 아닙니다.", "data": null}
"""))),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "답변을 찾을 수 없음",
content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
examples = @ExampleObject(value = """
{"code": "E103", "message": "답변을 찾을 수 없습니다.", "data": null}
"""))),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "서버 오류",
content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
examples = @ExampleObject(value = """
{"code": "E000", "message": "서버 에러가 발생했습니다. 담당자에게 문의 바랍니다.", "data": null}
""")))
})
@DeleteMapping(value = "/me", produces = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<ApiResponse<Void>> deleteMyAnswer(
@PathVariable("gatheringId") Long gatheringId,
@PathVariable("meetingId") Long meetingId,
@PathVariable("topicId") Long topicId
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
Expand All @@ -28,4 +25,16 @@ public ResponseEntity<ApiResponse<PreOpinionResponse>> findAnswers(
PreOpinionResponse response = preOpinionService.findPreOpinions(gatheringId, meetingId);
return ApiResponse.success(response, "약속의 사전 의견 목록 조회를 성공했습니다.");
}

@Override
@DeleteMapping(value = "/me", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ApiResponse<Void>> deleteMyAnswer(
@PathVariable("gatheringId") Long gatheringId,
@PathVariable("meetingId") Long meetingId
) {

preOpinionService.deleteMyAnswer(gatheringId, meetingId);

return ApiResponse.deleted("내 사전의견이 삭제되었습니다.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,4 @@ public ResponseEntity<ApiResponse<TopicAnswerSubmitResponse>> submitMyAnswer(

return ApiResponse.success(response, "답변이 제출되었습니다.");
}

@Override
public ResponseEntity<ApiResponse<Void>> deleteMyAnswer(
@PathVariable("gatheringId") Long gatheringId,
@PathVariable("meetingId") Long meetingId,
@PathVariable("topicId") Long topicId
) {

topicAnswerService.deleteMyAnswer(gatheringId, meetingId, topicId);

return ApiResponse.deleted("내 답변이 삭제되었습니다.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ public record MemberPreOpinion(

@Schema(description = "멤버 정보")
public record MemberInfo(
@Schema(description = "멤버 ID", example = "1")
Long memberId,
@Schema(description = "사용자 ID", example = "1")
Long userId,

@Schema(description = "닉네임", example = "독서왕")
String nickname,
Expand All @@ -82,12 +82,12 @@ public record MemberInfo(
String role
) {
public static MemberInfo of(
Long memberId,
Long userId,
String nickname,
String profileImage,
String role
) {
return new MemberInfo(memberId, nickname, profileImage, role);
return new MemberInfo(userId, nickname, profileImage, role);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,16 @@ WHERE ta.topic.id IN (
"""
)
List<TopicAnswer> findByMeetingId(Long meetingId);

@Query("""
SELECT ta
FROM TopicAnswer ta
JOIN FETCH ta.topic t
WHERE ta.user.id = :userId
AND t.meeting.id = :meetingId
""")
List<TopicAnswer> findByTopicAnswers(
@Param("meetingId") Long meetingId,
@Param("userId") Long userId
);
}
18 changes: 17 additions & 1 deletion src/main/java/com/dokdok/topic/service/PreOpinionService.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,22 @@ public PreOpinionResponse findPreOpinions(Long gatheringId, Long meetingId) {
return new PreOpinionResponse(topicInfos, preOpinionData);
}

@Transactional
public void deleteMyAnswer(
Long gatheringId,
Long meetingId
) {
Long userId = SecurityUtil.getCurrentUserId();

gatheringValidator.validateGathering(gatheringId);
meetingValidator.validateMeetingInGathering(meetingId, gatheringId);
meetingValidator.validateMeetingMember(meetingId, userId);

List<TopicAnswer> topicAnswers = topicValidator.getTopicAnswers(meetingId, userId);

topicAnswers.forEach(TopicAnswer::softDelete);
}

private void validateAccess(Long gatheringId, Long meetingId, Long userId) {
gatheringValidator.validateGathering(gatheringId);
meetingValidator.validateMeetingInGathering(meetingId, gatheringId);
Expand Down Expand Up @@ -157,7 +173,7 @@ private PreOpinionResponse.MemberPreOpinion toMemberPreOpinion(MeetingMember mm,
String role = resolveRole(mm, maps.gatheringRoleByUserId());

PreOpinionResponse.MemberInfo memberInfo
= PreOpinionResponse.MemberInfo.of(memberId, user.getNickname(), presignedUrl, role);
= PreOpinionResponse.MemberInfo.of(user.getId(), user.getNickname(), presignedUrl, role);

BookReview review = maps.bookReviewByUserId().get(memberId);
BookReviewInfo bookReviewInfo = review != null
Expand Down
19 changes: 2 additions & 17 deletions src/main/java/com/dokdok/topic/service/TopicAnswerService.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@RequiredArgsConstructor
public class TopicAnswerService {
Expand Down Expand Up @@ -120,21 +122,4 @@ public TopicAnswerSubmitResponse submitMyAnswer(
return TopicAnswerSubmitResponse.from(answer);
}

@Transactional
public void deleteMyAnswer(
Long gatheringId,
Long meetingId,
Long topicId
) {
Long userId = SecurityUtil.getCurrentUserId();

gatheringValidator.validateGathering(gatheringId);
meetingValidator.validateMeetingInGathering(meetingId, gatheringId);
meetingValidator.validateMeetingMember(meetingId, userId);
topicValidator.validateTopicInMeeting(topicId, meetingId);

TopicAnswer answer = topicValidator.getTopicAnswer(topicId, userId);

answer.softDelete();
}
}
16 changes: 16 additions & 0 deletions src/main/java/com/dokdok/topic/service/TopicValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,22 @@ public TopicAnswer getTopicAnswer(Long topicId, Long userId) {
.orElseThrow(() -> new TopicException(TopicErrorCode.TOPIC_ANSWER_NOT_FOUND));
}

public List<TopicAnswer> getTopicAnswers(Long meetingId, Long userId) {
List<TopicAnswer> topicAnswers = topicAnswerRepository.findByTopicAnswers(meetingId, userId);

if(topicAnswers.isEmpty()) {
throw new TopicException(TopicErrorCode.TOPIC_ANSWER_NOT_FOUND);
}

boolean allDeleted = topicAnswers.stream().allMatch(TopicAnswer::isDeleted);

if (allDeleted) {
throw new TopicException(TopicErrorCode.TOPIC_ANSWER_ALREADY_DELETED);
}

return topicAnswers;
}

/**
* 주제에 대한 삭제 권한 검증한다
* 권한 소유 : 모임장, 약속장, 주제 제안자
Expand Down
Loading