feat: 숏폼 복사 시 작업 완료 처리

This commit is contained in:
hehihoho3@gmail.com 2026-09-03 09:27:09 +09:00
parent 80e462acbf
commit ea08b6d6b7
5 changed files with 45 additions and 6 deletions

View File

@ -50,6 +50,11 @@ public class ShortformController {
return ApiResponse.ok(shortformService.reset(id));
}
@PostMapping("/jobs/{id}/complete")
public ApiResponse<JobDetail> complete(@PathVariable Long id) {
return ApiResponse.ok(shortformService.completeCopyWorkflow(id));
}
@PostMapping("/jobs/{id}/subtitles/retry")
public ApiResponse<Long> retrySubtitles(@PathVariable Long id) {
shortformService.retrySubtitle(id);

View File

@ -154,6 +154,14 @@ public class ShortformJob {
this.completedAt = LocalDateTime.now();
}
/** URL 또는 자막을 외부 작업 도구로 복사한 큐 작업을 완료 처리한다. */
public void completeCopyWorkflow() {
this.status = ShortformJobStatus.DONE;
if (this.completedAt == null) {
this.completedAt = LocalDateTime.now();
}
}
/**
* 결과를 비우고 다시 대기 상태로 되돌린다. 등록 정보(URL·videoId·등록시각)는 유지하므로
* 삭제 후 재등록할 필요가 없고, 큐 스킬이 PENDING 만 집어가므로 그대로 다시 실행된다.

View File

@ -133,6 +133,14 @@ public class ShortformService {
.orElseThrow(() -> new IllegalArgumentException("작업이 없습니다: " + jobId));
}
@Transactional
public JobDetail completeCopyWorkflow(Long jobId) {
ShortformJob job = jobRepository.findById(jobId)
.orElseThrow(() -> new IllegalArgumentException("작업이 없습니다: " + jobId));
job.completeCopyWorkflow();
return JobDetail.from(job);
}
@Transactional
public void delete(Long jobId) {
jobRepository.deleteById(jobId);

View File

@ -182,8 +182,7 @@
e.stopPropagation();
await navigator.clipboard.writeText(decodeURIComponent(btn.dataset.copy));
if (btn.hasAttribute('data-complete-on-copy')) {
btn.textContent = '완료';
btn.disabled = true;
await completeCopiedJob(id, el, btn);
} else {
btn.textContent = '복사됨!';
setTimeout(() => btn.textContent = label, 1200);
@ -195,8 +194,7 @@
subtitleButton.onclick = async (e) => {
e.stopPropagation();
await navigator.clipboard.writeText(detail.subtitleVtt);
subtitleButton.textContent = '완료';
subtitleButton.disabled = true;
await completeCopiedJob(id, el, subtitleButton);
};
}
}
@ -205,6 +203,17 @@
return (s || '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
async function completeCopiedJob(id, card, button) {
await api('/api/shortform/jobs/' + id + '/complete', { method: 'POST' });
button.textContent = '완료';
button.disabled = true;
const badge = card.querySelector('.sf-badge');
if (badge) {
badge.className = 'sf-badge DONE';
badge.textContent = 'DONE';
}
}
function clipHtml(c) {
return `<div class="sf-clip">
<div class="sf-clip-head">
@ -301,8 +310,7 @@
button.onclick = async (e) => {
e.stopPropagation();
await navigator.clipboard.writeText(detail.subtitleVtt);
button.textContent = '완료';
button.disabled = true;
await completeCopiedJob(id, card, button);
};
}

View File

@ -69,4 +69,14 @@ class ShortformJobResetTest {
assertThat(job.getTitle()).isEqualTo("새 클립");
assertThat(job.getRawOutput()).isEqualTo("새 Output");
}
@Test
void 복사_워크플로를_완료하면_DONE으로_변경된다() {
ShortformJob job = ShortformJob.create("https://youtu.be/abc12345678", "abc12345678");
job.completeCopyWorkflow();
assertThat(job.getStatus()).isEqualTo(ShortformJobStatus.DONE);
assertThat(job.getCompletedAt()).isNotNull();
}
}