Skip to content

Commit 01394fa

Browse files
authored
feat : 답변 수정 삭제 api (#61)
* 답변 수정/삭제 구현 * 답변 수정 삭제 테스트 작성
1 parent aa6d72e commit 01394fa

File tree

14 files changed

+313
-69
lines changed

14 files changed

+313
-69
lines changed

src/main/java/com/oronaminc/join/answer/api/AnswerController.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import com.oronaminc.join.answer.dto.AnswerGetResponse;
44
import com.oronaminc.join.answer.service.AnswerService;
55
import com.oronaminc.join.member.security.MemberDetails;
6+
import io.swagger.v3.oas.annotations.Operation;
7+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
68
import lombok.RequiredArgsConstructor;
79
import org.springframework.http.HttpStatus;
810
import org.springframework.http.ResponseEntity;
@@ -20,6 +22,15 @@ public class AnswerController {
2022

2123
private final AnswerService answerService;
2224

25+
@Operation(
26+
summary = "답변 조회",
27+
description = "답변 보기 클릭 시 질문에 대한 답변을 조회",
28+
responses = {
29+
@ApiResponse(responseCode = "200", description = "답변 조회 성공"),
30+
@ApiResponse(responseCode = "400", description = "답변이 없는데 답변보기 버튼이 활성화 되어 잘못된 조회 접근")
31+
32+
}
33+
)
2334
@GetMapping("/rooms/{roomId}/questions/{questionId}/answers")
2435
@ResponseStatus(HttpStatus.OK)
2536
public ResponseEntity<AnswerGetResponse> getAnswer(

src/main/java/com/oronaminc/join/answer/domain/Answer.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.oronaminc.join.answer.domain;
22

3-
import com.oronaminc.join.answer.dto.AnswerCreateRequest;
43
import com.oronaminc.join.global.entity.BaseEntity;
54
import com.oronaminc.join.member.domain.Member;
65
import com.oronaminc.join.question.domain.Question;
@@ -60,6 +59,10 @@ public static Answer create(Question question, Member member, String content) {
6059
.build();
6160
}
6261

62+
public void updataContent(String content) {
63+
this.content = content;
64+
}
65+
6366
public Long incrementEmojiCount() {
6467
return ++this.emojiCount;
6568
}

src/main/java/com/oronaminc/join/answer/dto/AnswerCreateRequest.java

Lines changed: 0 additions & 11 deletions
This file was deleted.

src/main/java/com/oronaminc/join/answer/dto/AnswerCreateResponse.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import com.oronaminc.join.global.dto.WriterDto;
44
import io.swagger.v3.oas.annotations.media.Schema;
5+
import jakarta.validation.constraints.NotBlank;
6+
import jakarta.validation.constraints.Size;
57
import java.time.LocalDateTime;
68
import lombok.Builder;
79

@@ -16,6 +18,8 @@ public record AnswerCreateResponse(
1618
@Schema(description = "답변 ID", example = "11")
1719
Long answerId,
1820
@Schema(description = "답변 내용", example = "답변입니다.")
21+
@NotBlank(message = "답변 내용을 입력해주시기 바랍니다.")
22+
@Size(max = 300, message = "답변 내용은 최대 300자까지 입력할 수 있습니다.")
1923
String content,
2024
@Schema(description = "답변 내용에 대한 공감 수", example = "23")
2125
int emojiCount,
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.oronaminc.join.answer.dto;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
5+
@Schema(description = "답변 삭제 응답 DTO")
6+
public record AnswerDeleteResponse(
7+
Long answerId,
8+
@Schema(description = "삭제 이벤트", example = "DELETE")
9+
String event
10+
) {
11+
12+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.oronaminc.join.answer.dto;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import jakarta.validation.constraints.NotBlank;
5+
import jakarta.validation.constraints.Size;
6+
7+
@Schema(description = "답변 생성/수정 요청 DTO")
8+
public record AnswerRequest(
9+
//TODO: 빈값 or " " (space) 처리
10+
@NotBlank(message = "답변 내용을 입력해주시기 바랍니다.")
11+
@Size(max = 300, message = "답변 내용은 최대 300자까지 입력할 수 있습니다.")
12+
@Schema(description = "답변 내용", example = "답변입니다.")
13+
String content
14+
) {
15+
16+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.oronaminc.join.answer.dto;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import lombok.Builder;
5+
6+
@Builder
7+
@Schema(description = "답변 수정 응답 DTO")
8+
public record AnswerUpdateResponse(
9+
Long answerId,
10+
@Schema(description = "수정 이벤트", example = "UPDATE")
11+
String event,
12+
@Schema(description = "수정된 내용", example = "수정된 답변입니다.")
13+
String content
14+
15+
) {
16+
17+
}

src/main/java/com/oronaminc/join/answer/mapper/AnswerMapper.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package com.oronaminc.join.answer.mapper;
22

33
import com.oronaminc.join.answer.domain.Answer;
4-
import com.oronaminc.join.answer.dto.AnswerCreateRequest;
4+
import com.oronaminc.join.answer.dto.AnswerRequest;
55
import com.oronaminc.join.answer.dto.AnswerCreateResponse;
66
import com.oronaminc.join.answer.dto.AnswerGetResponse;
7+
import com.oronaminc.join.answer.dto.AnswerUpdateResponse;
78
import com.oronaminc.join.global.dto.WriterDto;
89
import com.oronaminc.join.member.domain.Member;
910
import com.oronaminc.join.question.domain.Question;
@@ -43,7 +44,16 @@ public static AnswerGetResponse toAnswerGetResponse(Answer answer, Long emojiCou
4344
.build();
4445
}
4546

46-
public static Answer toEntity(Question question, Member member, AnswerCreateRequest request) {
47+
public static Answer toEntity(Question question, Member member, AnswerRequest request) {
4748
return Answer.create(question, member, request.content());
4849
}
50+
51+
public static AnswerUpdateResponse toAnswerUpdateResponse(Answer answer) {
52+
return AnswerUpdateResponse.builder()
53+
.answerId(answer.getId())
54+
.event("UPDATE")
55+
.content(answer.getContent())
56+
.build();
57+
}
58+
4959
}

src/main/java/com/oronaminc/join/answer/service/AnswerService.java

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,11 @@
44

55
import com.oronaminc.join.answer.dao.AnswerRepository;
66
import com.oronaminc.join.answer.domain.Answer;
7-
import com.oronaminc.join.answer.dto.AnswerCreateRequest;
7+
import com.oronaminc.join.answer.dto.AnswerRequest;
88
import com.oronaminc.join.answer.dto.AnswerGetResponse;
99
import com.oronaminc.join.answer.mapper.AnswerMapper;
1010
import com.oronaminc.join.emoji.domain.TargetType;
1111
import com.oronaminc.join.emoji.service.EmojiReader;
12-
import com.oronaminc.join.global.exception.ErrorCode;
1312
import com.oronaminc.join.global.exception.ErrorException;
1413
import com.oronaminc.join.member.domain.Member;
1514
import com.oronaminc.join.member.service.MemberReader;
@@ -38,7 +37,7 @@ public class AnswerService {
3837

3938
@Transactional
4039
public Answer create(Long roomId, Long memberId, Long questionId,
41-
AnswerCreateRequest requestDto) {
40+
AnswerRequest request) {
4241

4342
Member member = memberReader.getById(memberId);
4443
Room room = roomReader.getById(roomId);
@@ -50,7 +49,7 @@ public Answer create(Long roomId, Long memberId, Long questionId,
5049
throw new ErrorException(BADREQUEST_DUPLICATION_ANSWER);
5150
}
5251

53-
Answer answer = AnswerMapper.toEntity(question, member, requestDto);
52+
Answer answer = AnswerMapper.toEntity(question, member, request);
5453

5554
answerRepository.save(answer);
5655

@@ -71,12 +70,28 @@ public AnswerGetResponse getAnswer(Long roomId, Long questionId, Long memberId)
7170
return AnswerMapper.toAnswerGetResponse(answer, emojiCount, isEmojied);
7271
}
7372

73+
@Transactional
74+
public Answer update(Long answerId, AnswerRequest request) {
75+
Answer answer = answerReader.getById(answerId);
76+
77+
answer.updataContent(request.content());
78+
79+
return answer;
80+
}
81+
82+
@Transactional
83+
public void delete(Long answerId) {
84+
Answer answer = answerReader.getById(answerId);
85+
answerRepository.delete(answer);
86+
}
87+
88+
@Transactional
7489
public void deleteByQuestion(Long questionId) {
7590
answerRepository.deleteByQuestionId(questionId);
7691
}
7792

93+
@Transactional
7894
public void deleteByQuestionList(List<Question> questions) {
7995
answerRepository.deleteByQuestionIn(questions);
8096
}
81-
8297
}
Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,61 @@
11
package com.oronaminc.join.answer.util;
22

3-
import static com.oronaminc.join.global.exception.ErrorCode.NOT_FOUND_PARTICIPANT;
3+
import static com.oronaminc.join.global.exception.ErrorCode.UNAUTHORIZED_DELETE_ANSWER;
4+
import static com.oronaminc.join.global.exception.ErrorCode.UNAUTHORIZED_EDIT_ANSWER;
45
import static com.oronaminc.join.global.exception.ErrorCode.UNAUTHORIZED_ROLE_ANSWER;
56

7+
import com.oronaminc.join.answer.domain.Answer;
8+
import com.oronaminc.join.answer.service.AnswerReader;
69
import com.oronaminc.join.global.exception.ErrorException;
7-
import com.oronaminc.join.participant.dao.ParticipantRepository;
810
import com.oronaminc.join.participant.domain.Participant;
911
import com.oronaminc.join.participant.domain.ParticipantType;
12+
import com.oronaminc.join.participant.service.ParticipantReader;
13+
import com.oronaminc.join.question.domain.Question;
14+
import com.oronaminc.join.room.domain.Room;
1015
import lombok.RequiredArgsConstructor;
1116
import org.springframework.stereotype.Component;
1217

1318
@Component
1419
@RequiredArgsConstructor
1520
public class PermissionValidator {
1621

17-
private final ParticipantRepository participantRepository;
22+
private final ParticipantReader participantReader;
23+
private final AnswerReader answerReader;
1824

1925
public void validateAnswerPermission(Long roomId, Long memberId) {
20-
Participant participant = participantRepository.findByRoomIdAndMemberId(roomId,memberId)
21-
.orElseThrow(() -> new ErrorException(NOT_FOUND_PARTICIPANT));
26+
// TODO: 생성시 조건 ( null 이면 안됨, " " 안됨, 팀원이나 발표자, 질문 작성자 본인만 생성가능 )
27+
Participant participant = participantReader.getByRoomIdAndMemberId(roomId, memberId);
2228
ParticipantType type = participant.getParticipantType();
2329

24-
if(type == ParticipantType.GUEST){
30+
if (type == ParticipantType.GUEST) {
2531
throw new ErrorException(UNAUTHORIZED_ROLE_ANSWER);
2632
}
2733
}
2834

35+
public void validateAnswerUpdatePermission(Long answerId, Long memberId) {
36+
Answer answer = answerReader.getById(answerId);
37+
38+
if (!answer.getMember().getId().equals(memberId)) {
39+
throw new ErrorException(UNAUTHORIZED_EDIT_ANSWER);
40+
}
41+
}
42+
43+
public void validateAnswerDeletePermission(Long answerId, Long memberId) {
44+
Answer answer = answerReader.getById(answerId);
45+
46+
Room room = answer.getQuestion().getRoom();
47+
Question question = answer.getQuestion();
48+
49+
Participant participant = participantReader.getByRoomIdAndMemberId(room.getId(), memberId);
50+
ParticipantType type = participant.getParticipantType();
51+
52+
boolean isQuestionWriter = question.getMember().getId().equals(memberId);
53+
54+
if (!(isQuestionWriter || type == ParticipantType.TEAM
55+
|| type == ParticipantType.PRESENTER)) {
56+
throw new ErrorException(UNAUTHORIZED_DELETE_ANSWER);
57+
}
58+
59+
}
60+
2961
}

0 commit comments

Comments
 (0)