3030@ Slf4j
3131@ Service
3232@ RequiredArgsConstructor
33+ @ Transactional
3334public class AnswerPersistenceService {
3435
3536 private final QuestionRepository questionRepository ;
3637 private final AnswerRepository answerRepository ;
3738 private final FeedbackRepository feedbackRepository ;
3839 private final ObjectMapper objectMapper ;
3940
40- @ Transactional
41- public AnswerSubmitResult saveAnswerAndFeedback (final UUID questionId , final UUID memberId , final String answer , final FeedbackResult result , final FeedbackDetail detail ) {
41+ public AnswerSubmitResult saveAnswerAndFeedback (final UUID questionId , final UUID memberId , final String answerText , final FeedbackResult result , final FeedbackDetail detail ) {
4242 InterviewQuestion question = getQuestion (questionId , memberId );
4343 question .updateStatus (QuestionStatus .ANSWERED );
4444
45- InterviewAnswer interviewAnswer = saveAnswer (answer , question );
46- InterviewFeedback feedback = saveFeedback (result , detail , interviewAnswer );
45+ // 답변 + 피드백 저장
46+ InterviewAnswer answer = saveAnswer (answerText , question );
47+ InterviewFeedback feedback = saveTextFeedback (result , detail , answer );
48+
49+ // 조건부 UPDATE로 최초 답변 여부 원자적 판단 (race condition 방지)
4750 boolean firstAnswer = isFirstTodayAnswer (questionId , feedback .getScore ());
4851
52+ // sendScore는 트랜잭션 외부에서 호출하므로 firstAnswer를 함께 반환
4953 return new AnswerSubmitResult (feedback , firstAnswer );
5054 }
5155
56+ public InterviewAnswer saveVideoAnswer (final UUID questionId , final UUID memberId ) {
57+ InterviewQuestion question = getQuestion (questionId , memberId );
58+ question .updateStatus (QuestionStatus .ANSWERED );
59+ return answerRepository .save (new InterviewAnswer (question , "" , AnswerType .VIDEO , null ));
60+ }
61+
62+ public void updateMediaUrl (final UUID answerId , final String mediaUrl ) {
63+ InterviewAnswer interviewAnswer = getAnswer (answerId );
64+ interviewAnswer .updateMediaUrl (mediaUrl );
65+ }
66+
67+ public void updateAnswer (final UUID answerId , final String answer ) {
68+ InterviewAnswer interviewAnswer = getAnswer (answerId );
69+ interviewAnswer .updateAnswer (answer );
70+ }
71+
72+ public void saveVideoFeedback (final UUID answerId , final FeedbackResult result , final FeedbackDetail detail , final FeedbackType feedbackType ) {
73+ InterviewAnswer interviewAnswer = getAnswer (answerId );
74+ feedbackRepository .save (new InterviewFeedback (interviewAnswer , serializeFeedback (detail ), feedbackType , result .score ()));
75+ }
76+
77+ public void saveCombinedFeedback (final UUID answerId , final CombinedFeedbackResult result ) {
78+ InterviewAnswer interviewAnswer = getAnswer (answerId );
79+ feedbackRepository .save (new InterviewFeedback (interviewAnswer , result .combineFeedback (), FeedbackType .COMBINED , result .score ()));
80+ }
81+
5282 private InterviewQuestion getQuestion (UUID questionId , UUID memberId ) {
5383 return questionRepository .findByIdAndMemberId (questionId , memberId )
5484 .orElseThrow (() -> new BusinessException (ErrorCode .QUESTION_NOT_FOUND ));
@@ -60,7 +90,7 @@ private InterviewAnswer saveAnswer(String answer, InterviewQuestion question) {
6090 );
6191 }
6292
63- private InterviewFeedback saveFeedback (FeedbackResult result , FeedbackDetail detail , InterviewAnswer interviewAnswer ) {
93+ private InterviewFeedback saveTextFeedback (FeedbackResult result , FeedbackDetail detail , InterviewAnswer interviewAnswer ) {
6494 return feedbackRepository .save (
6595 new InterviewFeedback (interviewAnswer , serializeFeedback (detail ), FeedbackType .TEXT , result .score ())
6696 );
@@ -72,46 +102,9 @@ private boolean isFirstTodayAnswer(final UUID questionId, final Integer score) {
72102 return questionRepository .updateLatestScoreIfFirstTime (questionId , score , startOfDay , endOfDay ) > 0 ;
73103 }
74104
75- @ Transactional
76- public InterviewAnswer createVideoAnswer (final UUID questionId , final UUID memberId ) {
77- InterviewQuestion question = getQuestion (questionId , memberId );
78- question .updateStatus (QuestionStatus .ANSWERED );
79-
80- return answerRepository .save (
81- new InterviewAnswer (question , "" , AnswerType .VIDEO , null ));
82- }
83-
84- @ Transactional
85- public void updateMediaUrl (final UUID answerId , final String mediaUrl ) {
86- InterviewAnswer interviewAnswer = answerRepository .findById (answerId )
87- .orElseThrow (() -> new BusinessException (ErrorCode .ANSWER_NOT_FOUND ));
88- interviewAnswer .updateMediaUrl (mediaUrl );
89- }
90-
91- @ Transactional
92- public void updateAnswer (final UUID answerId , final String answer ) {
93- InterviewAnswer interviewAnswer = answerRepository .findById (answerId )
94- .orElseThrow (() -> new BusinessException (ErrorCode .ANSWER_NOT_FOUND ));
95- interviewAnswer .updateAnswer (answer );
96- }
97-
98- @ Transactional
99- public void saveFeedback (final UUID answerId , final FeedbackResult result ,
100- final FeedbackDetail detail , final FeedbackType feedbackType ) {
101- InterviewAnswer interviewAnswer = answerRepository .findById (answerId )
102- .orElseThrow (() -> new BusinessException (ErrorCode .ANSWER_NOT_FOUND ));
103-
104- feedbackRepository .save (
105- new InterviewFeedback (interviewAnswer , serializeFeedback (detail ), feedbackType , result .score ()));
106- }
107-
108- @ Transactional
109- public void saveCombinedFeedback (final UUID answerId , final CombinedFeedbackResult result ) {
110- InterviewAnswer interviewAnswer = answerRepository .findById (answerId )
105+ private InterviewAnswer getAnswer (UUID answerId ) {
106+ return answerRepository .findById (answerId )
111107 .orElseThrow (() -> new BusinessException (ErrorCode .ANSWER_NOT_FOUND ));
112-
113- feedbackRepository .save (
114- new InterviewFeedback (interviewAnswer , result .combineFeedback (), FeedbackType .COMBINED , result .score ()));
115108 }
116109
117110 private String serializeFeedback (final FeedbackDetail detail ) {
0 commit comments