Skip to content

Commit 2995178

Browse files
committed
[Fix] 토스 결제 승인 응답 파싱 오류 수정
1 parent 64e2104 commit 2995178

8 files changed

Lines changed: 158 additions & 167 deletions

File tree

src/main/java/com/jobdri/jobdri_api/domain/analysis/service/AnalysisService.java

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import com.jobdri.jobdri_api.domain.analysis.repository.AnalysisRepository;
1212
import com.jobdri.jobdri_api.domain.analysis.repository.QuestionAnalysisRepository;
1313
import com.jobdri.jobdri_api.domain.analysis.repository.QuestionRepository;
14-
import com.jobdri.jobdri_api.domain.audit.service.AuditLogService;
14+
import com.jobdri.jobdri_api.domain.audit.annotation.AuditLogEvent;
1515
import com.jobdri.jobdri_api.domain.mockapply.entity.MockApply;
1616
import com.jobdri.jobdri_api.domain.mockapply.entity.MockApplyStatus;
1717
import com.jobdri.jobdri_api.domain.mockapply.repository.MockApplyRepository;
@@ -43,9 +43,9 @@ public class AnalysisService {
4343
private final QuestionAnalysisRepository questionAnalysisRepository;
4444
private final AnalysisAiClient analysisAiClient;
4545
private final CreditService creditService;
46-
private final AuditLogService auditLogService;
4746

4847
@Transactional
48+
@AuditLogEvent(action = "ANALYSIS_RUN", targetType = "MOCK_APPLY", targetId = "#arg1")
4949
public AnalysisResponse analyze(User user, Long mockApplyId) {
5050
MockApply mockApply = getOwnedMockApply(user, mockApplyId);
5151
List<Question> questions = questionRepository.findAllByMockApplyIdOrderByIdAsc(mockApply.getId());
@@ -80,24 +80,7 @@ public AnalysisResponse analyze(User user, Long mockApplyId) {
8080
questionAnalysisRepository.saveAll(questionAnalyses);
8181
mockApply.updateStatus(MockApplyStatus.COMPLETED);
8282

83-
AnalysisResponse response = toResponse(mockApply, analysis, questions, questionAnalyses);
84-
auditLogService.record(
85-
user,
86-
"ANALYSIS_RUN",
87-
"MOCK_APPLY",
88-
mockApply.getId(),
89-
null,
90-
new AnalysisAuditValue(
91-
analysis.getId(),
92-
analysis.getScore(),
93-
analysis.getJobFit(),
94-
analysis.getImpact(),
95-
analysis.getCompleteness(),
96-
questionAnalyses.size()
97-
)
98-
);
99-
100-
return response;
83+
return toResponse(mockApply, analysis, questions, questionAnalyses);
10184
} catch (RuntimeException e) {
10285
creditService.refund(user, 1, "자소서 분석 실패 환불", referenceId);
10386
throw e;
@@ -243,13 +226,4 @@ private QuestionAnalysisStatus normalizeStatus(String status) {
243226
}
244227
}
245228

246-
private record AnalysisAuditValue(
247-
Long analysisId,
248-
int score,
249-
int jobFit,
250-
int impact,
251-
int completeness,
252-
int highlightCount
253-
) {
254-
}
255229
}

src/main/java/com/jobdri/jobdri_api/domain/analysis/service/QuestionService.java

Lines changed: 7 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import com.jobdri.jobdri_api.domain.analysis.entity.Question;
1212
import com.jobdri.jobdri_api.domain.analysis.repository.CustomQuestionCandidateRepository;
1313
import com.jobdri.jobdri_api.domain.analysis.repository.QuestionRepository;
14-
import com.jobdri.jobdri_api.domain.audit.service.AuditLogService;
14+
import com.jobdri.jobdri_api.domain.audit.annotation.AuditLogEvent;
1515
import com.jobdri.jobdri_api.domain.mockapply.entity.MockApply;
1616
import com.jobdri.jobdri_api.domain.mockapply.entity.MockApplyStatus;
1717
import com.jobdri.jobdri_api.domain.mockapply.repository.MockApplyRepository;
@@ -51,7 +51,6 @@ public class QuestionService {
5151
private final MockApplyRepository mockApplyRepository;
5252
private final QuestionRepository questionRepository;
5353
private final CustomQuestionCandidateRepository customQuestionCandidateRepository;
54-
private final AuditLogService auditLogService;
5554

5655
public List<QuestionCandidateResponse> getQuestionCandidates(User user, Long mockApplyId) {
5756
MockApply mockApply = getOwnedMockApply(user, mockApplyId);
@@ -84,6 +83,7 @@ public List<QuestionCandidateResponse> getQuestionCandidates(User user, Long moc
8483
}
8584

8685
@Transactional
86+
@AuditLogEvent(action = "CUSTOM_QUESTION_CANDIDATE_ADD", targetType = "MOCK_APPLY", targetId = "#arg1")
8787
public QuestionCandidateResponse addCustomQuestionCandidate(
8888
User user,
8989
Long mockApplyId,
@@ -96,23 +96,13 @@ public QuestionCandidateResponse addCustomQuestionCandidate(
9696
CustomQuestionCandidate candidate = findOrCreateCustomCandidate(mockApply, content, request.charLimit());
9797
boolean selected = questionRepository.existsByMockApplyIdAndContent(mockApply.getId(), candidate.getContent());
9898

99-
QuestionCandidateResponse response = new QuestionCandidateResponse(
99+
return new QuestionCandidateResponse(
100100
candidate.getId(),
101101
candidate.getContent(),
102102
candidate.getLimit(),
103103
selected,
104104
true
105105
);
106-
auditLogService.record(
107-
user,
108-
"CUSTOM_QUESTION_CANDIDATE_ADD",
109-
"MOCK_APPLY",
110-
mockApply.getId(),
111-
null,
112-
response
113-
);
114-
115-
return response;
116106
}
117107

118108
private CustomQuestionCandidate findOrCreateCustomCandidate(
@@ -153,6 +143,7 @@ public QuestionSelectionResponse getSelectedQuestions(User user, Long mockApplyI
153143
}
154144

155145
@Transactional
146+
@AuditLogEvent(action = "QUESTION_SELECTION_SAVE", targetType = "MOCK_APPLY", targetId = "#arg1")
156147
public QuestionSelectionResponse saveSelectedQuestions(
157148
User user,
158149
Long mockApplyId,
@@ -162,9 +153,6 @@ public QuestionSelectionResponse saveSelectedQuestions(
162153
validateSelectionCount(request.questions().size());
163154

164155
List<Question> existingQuestions = questionRepository.findAllByMockApplyId(mockApply.getId());
165-
List<QuestionAuditValue> beforeQuestions = existingQuestions.stream()
166-
.map(QuestionAuditValue::from)
167-
.toList();
168156
questionRepository.deleteAll(existingQuestions);
169157

170158
List<Question> questions = request.questions().stream()
@@ -178,24 +166,15 @@ public QuestionSelectionResponse saveSelectedQuestions(
178166
List<Question> savedQuestions = questionRepository.saveAll(questions);
179167
mockApply.updateStatus(MockApplyStatus.ANSWER_WRITE);
180168

181-
QuestionSelectionResponse response = new QuestionSelectionResponse(
169+
return new QuestionSelectionResponse(
182170
mockApply.getId(),
183171
mockApply.getStatus(),
184172
savedQuestions.stream().map(QuestionResponse::from).toList()
185173
);
186-
auditLogService.record(
187-
user,
188-
"QUESTION_SELECTION_SAVE",
189-
"MOCK_APPLY",
190-
mockApply.getId(),
191-
beforeQuestions,
192-
savedQuestions.stream().map(QuestionAuditValue::from).toList()
193-
);
194-
195-
return response;
196174
}
197175

198176
@Transactional
177+
@AuditLogEvent(action = "QUESTION_ANSWER_SAVE", targetType = "MOCK_APPLY", targetId = "#arg1")
199178
public QuestionAnswerResponse saveAnswers(
200179
User user,
201180
Long mockApplyId,
@@ -205,9 +184,6 @@ public QuestionAnswerResponse saveAnswers(
205184
List<Question> questions = questionRepository.findAllByMockApplyIdOrderByIdAsc(mockApply.getId());
206185
Map<Long, Question> questionMap = questions.stream()
207186
.collect(Collectors.toMap(Question::getId, Function.identity()));
208-
List<QuestionAnswerAuditValue> beforeAnswers = questions.stream()
209-
.map(QuestionAnswerAuditValue::from)
210-
.toList();
211187

212188
for (QuestionAnswerSaveRequest.AnswerItem item : request.answers()) {
213189
Question question = questionMap.get(item.questionId());
@@ -220,21 +196,11 @@ public QuestionAnswerResponse saveAnswers(
220196
question.updateAnswer(normalizeAnswer(item.answer()));
221197
}
222198

223-
QuestionAnswerResponse response = new QuestionAnswerResponse(
199+
return new QuestionAnswerResponse(
224200
mockApply.getId(),
225201
mockApply.getStatus(),
226202
questions.stream().map(QuestionResponse::from).toList()
227203
);
228-
auditLogService.record(
229-
user,
230-
"QUESTION_ANSWER_SAVE",
231-
"MOCK_APPLY",
232-
mockApply.getId(),
233-
beforeAnswers,
234-
questions.stream().map(QuestionAnswerAuditValue::from).toList()
235-
);
236-
237-
return response;
238204
}
239205

240206
private MockApply getOwnedMockApply(User user, Long mockApplyId) {
@@ -284,16 +250,4 @@ private String normalizeAnswer(String answer) {
284250

285251
private record QuestionCandidate(Long id, String content, int charLimit) {
286252
}
287-
288-
private record QuestionAuditValue(Long questionId, String content, int charLimit) {
289-
private static QuestionAuditValue from(Question question) {
290-
return new QuestionAuditValue(question.getId(), question.getContent(), question.getLimit());
291-
}
292-
}
293-
294-
private record QuestionAnswerAuditValue(Long questionId, String answer) {
295-
private static QuestionAnswerAuditValue from(Question question) {
296-
return new QuestionAnswerAuditValue(question.getId(), question.getAnswer());
297-
}
298-
}
299253
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.jobdri.jobdri_api.domain.audit.annotation;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
@Target(ElementType.METHOD)
9+
@Retention(RetentionPolicy.RUNTIME)
10+
public @interface AuditLogEvent {
11+
String action();
12+
13+
String targetType();
14+
15+
String targetId() default "";
16+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package com.jobdri.jobdri_api.domain.audit.aop;
2+
3+
import com.jobdri.jobdri_api.domain.audit.annotation.AuditLogEvent;
4+
import com.jobdri.jobdri_api.domain.audit.service.AuditLogService;
5+
import com.jobdri.jobdri_api.domain.user.entity.User;
6+
import lombok.RequiredArgsConstructor;
7+
import lombok.extern.slf4j.Slf4j;
8+
import org.aspectj.lang.ProceedingJoinPoint;
9+
import org.aspectj.lang.annotation.Around;
10+
import org.aspectj.lang.annotation.Aspect;
11+
import org.aspectj.lang.reflect.MethodSignature;
12+
import org.springframework.context.expression.MethodBasedEvaluationContext;
13+
import org.springframework.core.DefaultParameterNameDiscoverer;
14+
import org.springframework.core.ParameterNameDiscoverer;
15+
import org.springframework.expression.ExpressionParser;
16+
import org.springframework.expression.spel.standard.SpelExpressionParser;
17+
import org.springframework.stereotype.Component;
18+
19+
import java.lang.reflect.Method;
20+
import java.util.LinkedHashMap;
21+
import java.util.Map;
22+
23+
@Aspect
24+
@Component
25+
@Slf4j
26+
@RequiredArgsConstructor
27+
public class AuditLogAspect {
28+
29+
private static final String RESULT_VARIABLE = "result";
30+
31+
private final AuditLogService auditLogService;
32+
private final ExpressionParser expressionParser = new SpelExpressionParser();
33+
private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
34+
35+
@Around("@annotation(auditLogEvent)")
36+
public Object recordAuditLog(ProceedingJoinPoint joinPoint, AuditLogEvent auditLogEvent) throws Throwable {
37+
Map<String, Object> beforeValue = extractParameters(joinPoint);
38+
39+
Object result = joinPoint.proceed();
40+
41+
try {
42+
auditLogService.record(
43+
extractUser(joinPoint),
44+
auditLogEvent.action(),
45+
auditLogEvent.targetType(),
46+
evaluateTargetId(joinPoint, auditLogEvent.targetId(), result),
47+
beforeValue,
48+
result
49+
);
50+
} catch (RuntimeException e) {
51+
log.warn("Audit log recording failed. action={}, method={}",
52+
auditLogEvent.action(),
53+
joinPoint.getSignature().toShortString(),
54+
e
55+
);
56+
throw e;
57+
}
58+
59+
return result;
60+
}
61+
62+
private User extractUser(ProceedingJoinPoint joinPoint) {
63+
for (Object arg : joinPoint.getArgs()) {
64+
if (arg instanceof User user) {
65+
return user;
66+
}
67+
}
68+
return null;
69+
}
70+
71+
private Long evaluateTargetId(ProceedingJoinPoint joinPoint, String targetIdExpression, Object result) {
72+
if (targetIdExpression == null || targetIdExpression.isBlank()) {
73+
return null;
74+
}
75+
76+
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
77+
MethodBasedEvaluationContext context = new MethodBasedEvaluationContext(
78+
null,
79+
method,
80+
joinPoint.getArgs(),
81+
parameterNameDiscoverer
82+
);
83+
Object[] args = joinPoint.getArgs();
84+
for (int i = 0; i < args.length; i++) {
85+
context.setVariable("arg" + i, args[i]);
86+
context.setVariable("p" + i, args[i]);
87+
}
88+
context.setVariable(RESULT_VARIABLE, result);
89+
90+
Object value = expressionParser.parseExpression(targetIdExpression).getValue(context);
91+
if (value instanceof Number number) {
92+
return number.longValue();
93+
}
94+
if (value instanceof String string && !string.isBlank()) {
95+
return Long.parseLong(string);
96+
}
97+
return null;
98+
}
99+
100+
private Map<String, Object> extractParameters(ProceedingJoinPoint joinPoint) {
101+
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
102+
String[] parameterNames = signature.getParameterNames();
103+
Object[] args = joinPoint.getArgs();
104+
105+
Map<String, Object> parameters = new LinkedHashMap<>();
106+
for (int i = 0; i < args.length; i++) {
107+
if (args[i] instanceof User) {
108+
continue;
109+
}
110+
String parameterName = parameterNames != null && i < parameterNames.length
111+
? parameterNames[i]
112+
: "arg" + i;
113+
parameters.put(parameterName, args[i]);
114+
}
115+
return parameters;
116+
}
117+
}

src/main/java/com/jobdri/jobdri_api/domain/audit/service/AuditLogService.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import jakarta.servlet.http.HttpServletRequest;
99
import lombok.RequiredArgsConstructor;
1010
import org.springframework.stereotype.Service;
11-
import org.springframework.transaction.annotation.Propagation;
1211
import org.springframework.transaction.annotation.Transactional;
1312
import org.springframework.web.context.request.RequestContextHolder;
1413
import org.springframework.web.context.request.ServletRequestAttributes;
@@ -22,7 +21,7 @@ public class AuditLogService {
2221
private final AuditLogRepository auditLogRepository;
2322
private final ObjectMapper objectMapper;
2423

25-
@Transactional(propagation = Propagation.MANDATORY)
24+
@Transactional
2625
public void record(
2726
User user,
2827
String action,

0 commit comments

Comments
 (0)