Skip to content

[Feat] 분석 비동기 작업 취소 및 진행 상태 응답 추가#178

Merged
whc9999 merged 2 commits into
devfrom
feat/async-analysis-cancel-progress
Jul 26, 2026
Merged

[Feat] 분석 비동기 작업 취소 및 진행 상태 응답 추가#178
whc9999 merged 2 commits into
devfrom
feat/async-analysis-cancel-progress

Conversation

@whc9999

@whc9999 whc9999 commented Jul 26, 2026

Copy link
Copy Markdown
Collaborator

✨ 어떤 이유로 PR를 하셨나요?

  • feature 병합
  • 버그 수정(아래에 issue #를 남겨주세요)
  • 코드 개선
  • 코드 수정
  • 배포
  • 기타(아래에 자세한 내용 기입해주세요)

📋 세부 내용 - 왜 해당 PR이 필요한지 작업 내용을 자세하게 설명해주세요

  • JD 분석 비동기 작업 취소 API 추가
  • 자소서 분석 비동기 작업 취소 API 추가
  • async task 상태에 CANCELLED 추가
  • 상태 조회 및 SSE 응답에 진행 단계, 진행률, 예상 잔여 시간 필드 추가
  • 자소서 분석 취소 시 예약된 크레딧만 멱등 환불 처리
  • 취소된 작업의 worker 결과 저장 및 완료 callback 차단
  • async task 진행/취소 컬럼 schema.sql 보강
  • 취소 흐름 관련 단위 테스트 추가

📸 작업 화면 스크린샷

⚠️ PR하기 전에 확인해주세요

  • 로컬테스트를 진행하셨나요?
  • 머지할 브랜치를 확인하셨나요?
  • 관련 label을 선택하셨나요?

🚨 관련 이슈 번호 [#177 ]

Summary by CodeRabbit

  • 새 기능
    • 분석 및 채용 공고 비동기 작업을 취소할 수 있습니다.
    • 작업 상태에서 현재 단계, 진행률, 예상 잔여 시간, 단계별 상태와 취소 정보를 확인할 수 있습니다.
    • 취소된 작업은 실시간 상태 알림이 종료되며, 후속 결과 처리가 차단됩니다.
  • 버그 수정
    • 취소되거나 종료된 작업이 다시 실행·완료 처리되지 않도록 안정성을 강화했습니다.
    • 취소된 분석 작업에 대해 불필요한 크레딧 처리가 발생하지 않도록 수정했습니다.
  • 테스트
    • 작업 취소 및 취소 후 처리 차단 시나리오를 검증하는 테스트를 추가했습니다.

- JD 분석 비동기 작업 취소 API 추가
- 자소서 분석 비동기 작업 취소 API 추가
- async task 상태에 CANCELLED 추가
- 상태 조회 및 SSE 응답에 진행 단계, 진행률, 예상 잔여 시간 필드 추가
- 자소서 분석 취소 시 예약된 크레딧만 멱등 환불 처리
- 취소된 작업의 worker 결과 저장 및 완료 callback 차단
- async task 진행/취소 컬럼 schema.sql 보강
- 취소 흐름 관련 단위 테스트 추가
@whc9999 whc9999 self-assigned this Jul 26, 2026
@whc9999 whc9999 added the ✨ feat New feature or request label Jul 26, 2026
@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@whc9999, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 45 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e7db6f0f-e1a9-4b18-ab4e-47f36cf2e759

📥 Commits

Reviewing files that changed from the base of the PR and between d9fd4d4 and 83b8790.

📒 Files selected for processing (9)
  • src/main/java/com/jobdri/jobdri_api/domain/analysis/entity/AnalysisAsyncTask.java
  • src/main/java/com/jobdri/jobdri_api/domain/analysis/service/AnalysisAsyncTaskService.java
  • src/main/java/com/jobdri/jobdri_api/domain/jobposting/controller/JobPostingAiController.java
  • src/main/java/com/jobdri/jobdri_api/domain/jobposting/entity/JobPostingAsyncTask.java
  • src/main/java/com/jobdri/jobdri_api/domain/jobposting/service/JobPostingAsyncTaskService.java
  • src/main/java/com/jobdri/jobdri_api/global/async/AsyncProgressCalculator.java
  • src/test/java/com/jobdri/jobdri_api/domain/analysis/service/AnalysisWorkerBridgeServiceTest.java
  • src/test/java/com/jobdri/jobdri_api/domain/jobposting/service/JobPostingAsyncTaskServiceTest.java
  • src/test/java/com/jobdri/jobdri_api/domain/jobposting/service/JobPostingWorkerBridgeServiceTest.java
📝 Walkthrough

Walkthrough

분석 및 채용 공고 비동기 작업에 CANCELLED 상태와 진행 정보가 추가되었습니다. 두 영역에 취소 API, 취소 응답, SSE 종료 처리가 구현되었으며, 워커의 취소 작업 후속 처리가 차단되고 관련 테스트가 보강되었습니다.

Changes

비동기 작업 취소 및 진행 상태

Layer / File(s) Summary
취소 상태와 응답 계약
src/main/java/com/jobdri/jobdri_api/domain/{analysis,jobposting}/dto/response/*, src/main/java/com/jobdri/jobdri_api/domain/{analysis,jobposting}/entity/*, src/main/resources/schema.sql
두 작업 엔티티에 취소·진행 필드와 CANCELLED 상태 전이가 추가되고, 상태·취소·단계별 진행 응답 및 데이터베이스 컬럼이 확장되었습니다.
분석 작업 취소 및 진행 응답
src/main/java/com/jobdri/jobdri_api/domain/analysis/controller/AnalysisController.java, src/main/java/com/jobdri/jobdri_api/domain/analysis/service/*, src/main/java/com/jobdri/jobdri_api/domain/analysis/dto/response/*
분석 취소 API가 사용자와 작업을 검증한 뒤 취소·크레딧 환급·커밋 후 SSE 발행을 수행하며, 상태 응답에 단계별 진행 정보가 포함됩니다.
채용 공고 작업 취소 및 진행 응답
src/main/java/com/jobdri/jobdri_api/domain/jobposting/controller/JobPostingAiController.java, src/main/java/com/jobdri/jobdri_api/domain/jobposting/service/*, src/main/java/com/jobdri/jobdri_api/domain/jobposting/dto/response/*
채용 공고 취소 API가 소유자 검증 후 작업을 취소하고 SSE 상태를 발행하며, 상태 응답에 현재 단계·진행률·잔여 시간이 포함됩니다.
워커 완료·재시도 차단
src/main/java/com/jobdri/jobdri_api/domain/{analysis,jobposting}/service/*WorkerBridgeService.java, src/test/java/com/jobdri/jobdri_api/domain/*/service/*Test.java
취소 또는 취소 요청 작업에 대한 실행·재시도·실패·결과 저장·완료 처리가 차단되며, 취소 상태와 SSE 발행 관련 테스트가 추가 또는 조정되었습니다.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Controller
  participant AsyncFacadeService
  participant AsyncTaskService
  participant AsyncTask
  participant SseService
  Client->>Controller: 취소 API 요청
  Controller->>AsyncFacadeService: 사용자 검증 및 cancel 호출
  AsyncFacadeService->>AsyncTaskService: 작업 취소 요청
  AsyncTaskService->>AsyncTask: requestCancel()
  AsyncTaskService->>SseService: 커밋 후 취소 상태 발행
  AsyncTaskService-->>Controller: 취소 응답 반환
Loading

Possibly related PRs

Suggested labels: ✨ feat

Suggested reviewers: shinae1023

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed 주요 변경인 분석 비동기 작업 취소와 진행 상태 응답 확장을 간결하게 잘 요약합니다.
Description check ✅ Passed 템플릿의 핵심 섹션과 체크리스트, 관련 이슈 번호를 채웠고 작업 내용도 충분히 구체적입니다.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/async-analysis-cancel-progress

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/main/java/com/jobdri/jobdri_api/domain/jobposting/entity/JobPostingAsyncTask.java (1)

157-169: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

두 엔티티의 markFailed 종료 상태 가드가 서로 다릅니다

여기서는 isTerminal()(SUCCEEDED/FAILED/CANCELLED)로 막지만, AnalysisAsyncTask.markFailed()SUCCEEDED || CANCELLED만 검사해 FAILED 재기록을 허용합니다. 동일 개념의 상태 머신이 도메인별로 다르게 동작하면 워커 재시도/타임아웃 스윕 경로에서 실패 사유가 한쪽에서만 갱신되는 혼선이 생깁니다. 의도된 차이인지 확인하고, 아니라면 한쪽으로 통일하는 편이 좋습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/jobdri/jobdri_api/domain/jobposting/entity/JobPostingAsyncTask.java`
around lines 157 - 169, 두 엔티티의 실패 처리 상태 가드를 동일하게 통일하세요.
JobPostingAsyncTask.markFailed()의 isTerminal() 동작을 기준으로
AnalysisAsyncTask.markFailed()에서도 SUCCEEDED, FAILED, CANCELLED 상태의 재기록을 모두 차단하도록
수정하고, 기존 비종료 상태의 실패 처리 흐름은 유지하세요.
src/main/java/com/jobdri/jobdri_api/domain/jobposting/service/JobPostingAsyncTaskService.java (1)

83-99: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

취소된 작업에 markSuccess가 들어오면 성공 알림과 메트릭이 잘못 발생합니다

SUCCEEDED/FAILED만 분기하고 CANCELLED는 걸러내지 않습니다. 엔티티의 markSuccessisTerminal() 가드로 no-op이 되지만, 이후 recordProcessingMetric(task, "succeeded"), publishAfterCommit, createSuccessNotificationSafely는 그대로 실행됩니다. 결과적으로 사용자는 취소한 작업에 대해 "채용 공고 작업이 완료되었습니다" 알림을 받고, succeeded 메트릭이 오염됩니다.

JobPostingWorkerBridgeService가 앞단에서 rejectIfCancelled로 막고 있긴 하지만, 서비스 자체가 public API인 만큼 여기서도 방어해야 합니다. AnalysisAsyncTaskService.markSuccess(90-97)도 동일하게 상태 가드가 없습니다.

🐛 제안 diff
         if (task.getStatus() == TaskStatus.FAILED) {
             throw new GeneralException(
                     GeneralErrorCode.INVALID_PARAMETER,
                     "이미 실패 처리된 채용 공고 비동기 작업입니다. taskId=" + taskId
             );
         }
+        if (task.getStatus() == TaskStatus.CANCELLED) {
+            throw new GeneralException(
+                    GeneralErrorCode.INVALID_PARAMETER,
+                    "취소된 채용 공고 비동기 작업입니다. taskId=" + taskId
+            );
+        }
         task.markSuccess(serializeResult(result));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/jobdri/jobdri_api/domain/jobposting/service/JobPostingAsyncTaskService.java`
around lines 83 - 99, Update JobPostingAsyncTaskService.markSuccess to
explicitly handle CANCELLED tasks before invoking markSuccess or any success
side effects; reject the request using the established invalid-parameter error
pattern, so recordProcessingMetric, publishAfterCommit, and
createSuccessNotificationSafely are not called. Apply the same cancellation
guard to AnalysisAsyncTaskService.markSuccess.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/java/com/jobdri/jobdri_api/domain/analysis/entity/AnalysisAsyncTask.java`:
- Around line 195-200: Remove the redundant status check inside requestCancel()
in AnalysisAsyncTask.java at lines 195-200 by checking only cancelledAt == null;
apply the same simplification in JobPostingAsyncTask.java at lines 176-181.
Preserve the existing cancellation timestamp assignment and return behavior.

In
`@src/main/java/com/jobdri/jobdri_api/domain/analysis/service/AnalysisAsyncTaskService.java`:
- Around line 288-295: 취소 시 마지막 실제 진행 단계가 유지되도록 두 서비스의 indexOfStep을 수정하세요.
AnalysisAsyncTaskService의 indexOfStep과 JobPostingAsyncTaskService의 indexOfStep은
미지 코드에 -1을 반환하도록 변경하고, 각 서비스의 buildSteps 및 resolveStepStatus 호출부가 취소된
currentStep을 마지막 유효 단계 기준으로 처리하도록 조정하세요. 엔티티의 취소 상태와 currentStep을 분리해 취소 직전 단계를
보존하세요.
- Around line 41-49: Extract the duplicated progress calculation logic from
AnalysisAsyncTaskService and JobPostingAsyncTaskService into a shared component
such as AsyncProgressCalculator, parameterized by progress steps and default
estimated remaining seconds. Move resolveCurrentStep, resolveProgressPercent,
resolveEstimatedRemainingSeconds, buildSteps, indexOfStep, resolveStepStatus,
and ProgressStepDefinition there; map each domain-specific TaskStatus enum to a
shared status representation, and update both services to delegate through the
component so fallback behavior is centralized.
- Around line 127-131: In the cancellation flow around requestCancel, invoke
task.requestCancel() before releaseCreditIfNeeded(task), and only release
credits when the cancellation transition actually succeeds. Preserve the
existing processing metric behavior for tasks whose status becomes CANCELLED,
while ensuring terminal SUCCEEDED or FAILED tasks are not refunded.

In
`@src/main/java/com/jobdri/jobdri_api/domain/jobposting/controller/JobPostingAiController.java`:
- Line 120: Rename the JobPostingAiController method
cancelIngestJobPostingAsyncStatus to cancelIngestJobPostingAsyncTask to reflect
that it cancels the async job itself, while preserving its implementation and
public URL contract.

In
`@src/test/java/com/jobdri/jobdri_api/domain/analysis/service/AnalysisWorkerBridgeServiceTest.java`:
- Around line 59-72: 취소된 worker callback이 결과를 저장하거나 성공 처리하지 않는지 검증하는 테스트를 보강하세요.
src/test/java/com/jobdri/jobdri_api/domain/analysis/service/AnalysisWorkerBridgeServiceTest.java#L59-L72에서는
취소된 작업에 대해 completeTask와 storeGeneratedResult가 실패하고 결과 upsert·도메인
저장·markSuccess가 호출되지 않는 테스트를 추가하세요.
src/test/java/com/jobdri/jobdri_api/domain/jobposting/service/JobPostingWorkerBridgeServiceTest.java#L86-L98에서는
completeTask, finalizeAndComplete, storeFinalizeResult 각각이 취소 후 upsert·공고 생성·성공
전이를 수행하지 않는지 검증하세요.

In
`@src/test/java/com/jobdri/jobdri_api/domain/jobposting/service/JobPostingAsyncTaskServiceTest.java`:
- Around line 263-279: Expand cancelTaskMarksCancelledAndPublishesStatus and
related JobPostingAsyncTaskServiceTest coverage to include unauthorized
cancellation asserting GeneralException when findByTaskIdAndUserId returns
empty, terminal SUCCEEDED/FAILED cancellation preserving status/message without
SSE publication, repeated cancellation preserving the original cancelledAt, and
RUNNING cancellation resetting progressPercent to 0 while setting completedAt
and cancelledAt.

---

Outside diff comments:
In
`@src/main/java/com/jobdri/jobdri_api/domain/jobposting/entity/JobPostingAsyncTask.java`:
- Around line 157-169: 두 엔티티의 실패 처리 상태 가드를 동일하게 통일하세요.
JobPostingAsyncTask.markFailed()의 isTerminal() 동작을 기준으로
AnalysisAsyncTask.markFailed()에서도 SUCCEEDED, FAILED, CANCELLED 상태의 재기록을 모두 차단하도록
수정하고, 기존 비종료 상태의 실패 처리 흐름은 유지하세요.

In
`@src/main/java/com/jobdri/jobdri_api/domain/jobposting/service/JobPostingAsyncTaskService.java`:
- Around line 83-99: Update JobPostingAsyncTaskService.markSuccess to explicitly
handle CANCELLED tasks before invoking markSuccess or any success side effects;
reject the request using the established invalid-parameter error pattern, so
recordProcessingMetric, publishAfterCommit, and createSuccessNotificationSafely
are not called. Apply the same cancellation guard to
AnalysisAsyncTaskService.markSuccess.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 46ad38dd-124b-4acd-890c-4fae7cf073b1

📥 Commits

Reviewing files that changed from the base of the PR and between bf55ac6 and d9fd4d4.

📒 Files selected for processing (22)
  • src/main/java/com/jobdri/jobdri_api/domain/analysis/controller/AnalysisController.java
  • src/main/java/com/jobdri/jobdri_api/domain/analysis/dto/response/AnalysisAsyncCancelResponse.java
  • src/main/java/com/jobdri/jobdri_api/domain/analysis/dto/response/AnalysisAsyncStatusResponse.java
  • src/main/java/com/jobdri/jobdri_api/domain/analysis/dto/response/AnalysisProgressStepResponse.java
  • src/main/java/com/jobdri/jobdri_api/domain/analysis/entity/AnalysisAsyncTask.java
  • src/main/java/com/jobdri/jobdri_api/domain/analysis/service/AnalysisAsyncFacadeService.java
  • src/main/java/com/jobdri/jobdri_api/domain/analysis/service/AnalysisAsyncSseService.java
  • src/main/java/com/jobdri/jobdri_api/domain/analysis/service/AnalysisAsyncTaskService.java
  • src/main/java/com/jobdri/jobdri_api/domain/analysis/service/AnalysisWorkerBridgeService.java
  • src/main/java/com/jobdri/jobdri_api/domain/jobposting/controller/JobPostingAiController.java
  • src/main/java/com/jobdri/jobdri_api/domain/jobposting/dto/response/JobPostingAsyncCancelResponse.java
  • src/main/java/com/jobdri/jobdri_api/domain/jobposting/dto/response/JobPostingAsyncStatusResponse.java
  • src/main/java/com/jobdri/jobdri_api/domain/jobposting/dto/response/JobPostingProgressStepResponse.java
  • src/main/java/com/jobdri/jobdri_api/domain/jobposting/entity/JobPostingAsyncTask.java
  • src/main/java/com/jobdri/jobdri_api/domain/jobposting/service/JobPostingAsyncFacadeService.java
  • src/main/java/com/jobdri/jobdri_api/domain/jobposting/service/JobPostingAsyncSseService.java
  • src/main/java/com/jobdri/jobdri_api/domain/jobposting/service/JobPostingAsyncTaskService.java
  • src/main/java/com/jobdri/jobdri_api/domain/jobposting/service/JobPostingWorkerBridgeService.java
  • src/main/resources/schema.sql
  • src/test/java/com/jobdri/jobdri_api/domain/analysis/service/AnalysisWorkerBridgeServiceTest.java
  • src/test/java/com/jobdri/jobdri_api/domain/jobposting/service/JobPostingAsyncTaskServiceTest.java
  • src/test/java/com/jobdri/jobdri_api/domain/jobposting/service/JobPostingWorkerBridgeServiceTest.java

Comment on lines +41 to +49
private static final int DEFAULT_ESTIMATED_REMAINING_SECONDS = 180;
private static final List<ProgressStepDefinition> PROGRESS_STEPS = List.of(
new ProgressStepDefinition("VALIDATING_INPUT", "분석할 내용을 확인하고 있어요"),
new ProgressStepDefinition("PREPARING_CONTEXT", "공고와 자소서를 준비하고 있어요"),
new ProgressStepDefinition("CALLING_LLM", "자기소개서를 평가하고 있어요"),
new ProgressStepDefinition("VALIDATING_RESULT", "분석 결과를 검증하고 있어요"),
new ProgressStepDefinition("SAVING_RESULT", "분석 결과를 저장하고 있어요"),
new ProgressStepDefinition("COMPLETED", "분석이 완료되었습니다")
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

진행 단계 계산 로직이 두 서비스에 통째로 복제되어 있습니다

PROGRESS_STEPS 정의를 제외한 resolveCurrentStep / resolveProgressPercent / resolveEstimatedRemainingSeconds / buildSteps / indexOfStep / resolveStepStatus / ProgressStepDefinitionJobPostingAsyncTaskService(274-347, 449-451)와 문자 그대로 동일합니다. 상태 판정 규칙이 한쪽에서만 바뀌면 두 API의 진행률 계약이 조용히 어긋납니다.

단계 목록과 기본 ETA만 파라미터로 받는 공용 컴포넌트(예: AsyncProgressCalculator)로 추출하고, 도메인별 TaskStatus enum은 공용 상태 열거로 매핑해 넘기는 구조를 권합니다. 위에서 지적한 indexOfStep fallback 수정도 한 곳에서 끝납니다.

Also applies to: 275-308, 380-382

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/jobdri/jobdri_api/domain/analysis/service/AnalysisAsyncTaskService.java`
around lines 41 - 49, Extract the duplicated progress calculation logic from
AnalysisAsyncTaskService and JobPostingAsyncTaskService into a shared component
such as AsyncProgressCalculator, parameterized by progress steps and default
estimated remaining seconds. Move resolveCurrentStep, resolveProgressPercent,
resolveEstimatedRemainingSeconds, buildSteps, indexOfStep, resolveStepStatus,
and ProgressStepDefinition there; map each domain-specific TaskStatus enum to a
shared status representation, and update both services to delegate through the
component so fallback behavior is centralized.

- 취소 시 currentStep을 CANCELLED로 덮지 않고 직전 진행 단계 유지
- async 진행 상태 계산 로직을 공통 AsyncProgressCalculator로 분리
- 취소 전이 성공 후에만 예약 크레딧을 환불하도록 처리 순서 수정
- 완료/실패된 task 취소 요청은 상태 유지 및 SSE 미발행 처리
- 취소된 task의 성공 전이와 worker 결과 저장 차단
- AnalysisAsyncTask 실패 처리 terminal guard를 JobPostingAsyncTask와 통일
- JD 분석 취소 컨트롤러 메서드명 정리
- 취소 상태 worker callback 및 task 취소 케이스 테스트 보강
@whc9999
whc9999 merged commit b62e0fd into dev Jul 26, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ feat New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant