Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,23 @@ public class QuestionTailorController {
private final QuestionTailorService questionTailorService;

// 분석 서버 전용 콜백. 2xx가 늦거나 실패하면 결과가 폐기되므로 저장과 전달만 하고 바로 응답한다.
// tailorId는 우리가 콜백 주소에 실어 보낸 준비 건 id다. 배포 전에 나간 요청의 콜백에는 없다.
@PostMapping("/callback")
public ResponseEntity<Void> callback(
@RequestParam(name = QuestionTailorService.TAILOR_ID_PARAM, required = false) Long tailorId,
@RequestBody QuestionTailorCallbackRequest request
) {
questionTailorService.handleCallback(request);
questionTailorService.handleCallback(request, tailorId);
return ResponseEntity.ok().build();
}

// N:1 전용 콜백. 1:1과 본문 형태가 달라 경로를 나눈다.
@PostMapping("/multi/callback")
public ResponseEntity<Void> multiCallback(
@RequestParam(name = QuestionTailorService.TAILOR_ID_PARAM, required = false) Long tailorId,
@RequestBody QuestionTailorMultiCallbackRequest request
) {
questionTailorService.handleMultiCallback(request);
questionTailorService.handleMultiCallback(request, tailorId);
return ResponseEntity.ok().build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
@Table(name = "question_tailor")
@Getter
@Setter
@Builder
@Builder(toBuilder = true)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class QuestionTailorEntity {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,59 @@ and t.status in (repit.repit_api_server.domain.userdata.question.entity.enums.Ta
and t.status = repit.repit_api_server.domain.userdata.question.entity.enums.TailorStatus.WAITING
""")
int claimResume(@Param("tailorId") Long tailorId, @Param("now") LocalDateTime now);

/**
* 분석 서버가 돌려준 jobId를 남긴다. jobId가 아직 없을 때만 채운다.
*
* <p>콜백이 접수 응답보다 먼저 올 수 있다. 접수 응답을 받고 엔티티를 통째로 저장하면 이미 들어온 결과를 PENDING으로
* 되돌린다. 그래서 접수 응답은 이 칸 하나만 조건을 담아 고친다.
*/
@Transactional
@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query("""
update QuestionTailorEntity t
set t.jobId = :jobId
where t.tailorId = :tailorId
and t.jobId is null
""")
int recordJobId(@Param("tailorId") Long tailorId, @Param("jobId") String jobId);

/**
* 분석 서버에 접수하지 못한 새 건을 지운다. 그사이 콜백이 와서 결과가 들어갔거나 jobId를 받았으면 남긴다.
*
* <p>분석 서버를 부르기 전에 저장해 두므로, 요청이 실패하면 콜백이 올 리 없는 PENDING이 남는다. 그대로 두면 면접을
* 다시 시작해도 이 건이 최신이라 제한 시간까지 기다려야 한다.
*/
@Transactional
@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query("""
delete from QuestionTailorEntity t
where t.tailorId = :tailorId
and t.jobId is null
and t.status = repit.repit_api_server.domain.userdata.question.entity.enums.TailorStatus.PENDING
""")
int discardUnaccepted(@Param("tailorId") Long tailorId);

/**
* 콜백 결과를 확정한다. 아직 PENDING일 때만 상태와 결과를 한 문장으로 함께 고치고 1을 돌려받는다.
*
* <p>상태를 읽은 뒤 따로 저장하면, 그사이 실패 처리(시간 초과, 사이클 대기에서 이어가기 실패)가 claimExpiration으로
* 먼저 확정한 건을 콜백이 덮거나, 반대로 실패 처리가 들고 있던 낡은 엔티티로 콜백 결과를 덮는다. 둘 다 조건부 갱신이라
* 한쪽만 이긴다. jobId는 비어 있을 때만 채운다 — 접수 응답이 먼저 남긴 값을 지우지 않는다.
*/
@Transactional
@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query("""
update QuestionTailorEntity t
set t.status = :#{#tailor.status},
t.tailored = :#{#tailor.tailored},
t.questions = :#{#tailor.questions},
t.errorStatusCode = :#{#tailor.errorStatusCode},
t.errorMessage = :#{#tailor.errorMessage},
t.jobId = coalesce(t.jobId, :#{#tailor.jobId}),
t.updatedAt = local datetime
where t.tailorId = :#{#tailor.tailorId}
and t.status = repit.repit_api_server.domain.userdata.question.entity.enums.TailorStatus.PENDING
""")
int settleCallback(@Param("tailor") QuestionTailorEntity tailor);
}
Loading
Loading