Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// src/main/java/com/chaineeproject/chainee/controller/JobApplicationController.java
package com.chaineeproject.chainee.controller;

import com.chaineeproject.chainee.dto.JobApplicationRequest;
Expand All @@ -6,8 +7,11 @@
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.web.bind.annotation.*;

@RestController
Expand All @@ -19,14 +23,17 @@ public class JobApplicationController {
private final JobApplicationService jobApplicationService;

@PostMapping("/{postId}/apply")
@Operation(summary = "채용 공고 지원", description = "지정한 채용 공고 ID에 대해 지원서를 제출합니다.")
@Operation(summary = "채용 공고 지원", description = "요청 사용자의 JWT uid를 지원자로 하여 지정한 공고에 지원합니다.")
public ResponseEntity<JobApplicationResponse> applyToJob(
@AuthenticationPrincipal Jwt jwt,
@Parameter(description = "지원할 채용 공고 ID", example = "1")
@PathVariable Long postId,

@RequestBody JobApplicationRequest request
@Valid @RequestBody JobApplicationRequest request
) {
jobApplicationService.applyToJob(postId, request);
Long applicantId = com.chaineeproject.chainee.security.SecurityUtils.uidOrNull(jwt);
if (applicantId == null) return ResponseEntity.status(401).build();

jobApplicationService.applyToJob(postId, applicantId, request.resumeId());
return ResponseEntity.ok(new JobApplicationResponse(true, "APPLICATION_SUBMITTED"));
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
// src/main/java/com/chaineeproject/chainee/dto/JobApplicationRequest.java
package com.chaineeproject.chainee.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;

@Schema(description = "채용 공고 지원 요청 DTO")
public record JobApplicationRequest(
@Schema(description = "지원자의 ID", example = "19")
Long applicantId,

@Schema(description = "지원자가 선택한 이력서 ID", example = "42")
Long resumeId
@Schema(description = "지원자가 제출할 이력서 ID", example = "42")
@NotNull Long resumeId
) {}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// src/main/java/com/chaineeproject/chainee/service/JobApplicationService.java
package com.chaineeproject.chainee.service;

import com.chaineeproject.chainee.dto.JobApplicationRequest;
import com.chaineeproject.chainee.entity.*;
import com.chaineeproject.chainee.event.JobApplicationCreatedEvent;
import com.chaineeproject.chainee.exception.ApplicationException;
Expand All @@ -25,25 +24,28 @@ public class JobApplicationService {
private final ApplicationEventPublisher eventPublisher;

@Transactional
public void applyToJob(Long postId, JobApplicationRequest request) {
// 1. 지원자 조회 (ID)
User applicant = userRepository.findById(request.applicantId())
public void applyToJob(Long postId, Long applicantId, Long resumeId) {
// 1) 지원자 조회(JWT uid로 강제)
User applicant = userRepository.findById(applicantId)
.orElseThrow(() -> new ApplicationException(ErrorCode.APPLICANT_NOT_FOUND));

// 2. 공고 조회
// 2) 공고 조회
JobPost post = jobPostRepository.findById(postId)
.orElseThrow(() -> new ApplicationException(ErrorCode.JOB_POST_NOT_FOUND));

// 3. 이력서 조회
Resume resume = resumeRepository.findById(request.resumeId())
// 3) 이력서 조회 + 소유권 검증(남의 이력서로 지원 방지)
Resume resume = resumeRepository.findById(resumeId)
.orElseThrow(() -> new ApplicationException(ErrorCode.RESUME_NOT_FOUND));
if (resume.getOwner() == null || !resume.getOwner().getId().equals(applicantId)) {
throw new ApplicationException(ErrorCode.FORBIDDEN); // 또는 전용 에러코드 정의 가능: NOT_OWNER_OF_RESUME
}

// 4. 중복 지원 방지
if (jobApplicationRepository.existsByPostIdAndApplicantId(postId, applicant.getId())) {
// 4) 중복 지원 방지
if (jobApplicationRepository.existsByPostIdAndApplicantId(postId, applicantId)) {
throw new ApplicationException(ErrorCode.DUPLICATE_APPLICATION);
}

// 5. 지원 엔티티 생성 및 저장
// 5) 저장
JobApplication application = new JobApplication();
application.setPost(post);
application.setApplicant(applicant);
Expand All @@ -53,6 +55,8 @@ public void applyToJob(Long postId, JobApplicationRequest request) {

jobApplicationRepository.save(application);
jobPostRepository.incrementApplicantCount(postId);

// 6) 이벤트(알림용)
eventPublisher.publishEvent(new JobApplicationCreatedEvent(application.getId()));
}
}