diff --git a/src/main/java/com/chaineeproject/chainee/controller/EmployerApplicationController.java b/src/main/java/com/chaineeproject/chainee/controller/EmployerApplicationController.java new file mode 100644 index 0000000..d591b4e --- /dev/null +++ b/src/main/java/com/chaineeproject/chainee/controller/EmployerApplicationController.java @@ -0,0 +1,65 @@ +// src/main/java/com/chaineeproject/chainee/controller/EmployerApplicationController.java +package com.chaineeproject.chainee.controller; + +import com.chaineeproject.chainee.dto.ApplicationAcceptResponse; +import com.chaineeproject.chainee.dto.PostApplicantsView; +import com.chaineeproject.chainee.dto.ResumeView; +import com.chaineeproject.chainee.service.EmployerApplicationService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +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 +@RequiredArgsConstructor +@RequestMapping("/api") // 메서드별로 풀 패스 지정 +@Tag(name = "Employer Application", description = "구인자용 지원/관리 API") +@SecurityRequirement(name = "bearerAuth") +public class EmployerApplicationController { + + private final EmployerApplicationService employerApplicationService; + + private Long meId(Jwt jwt) { return Long.valueOf(jwt.getSubject()); } + + /** 지원 수락 */ + @PostMapping("/job/applications/{applicationId}/accept") + @Operation(summary = "지원 수락", + description = "구인자(작성자)가 특정 지원서를 수락합니다. 응답으로 구직자 DID, payment, deadline을 반환합니다.") + public ResponseEntity accept( + @AuthenticationPrincipal Jwt jwt, + @PathVariable Long applicationId + ) { + var res = employerApplicationService.acceptApplication(applicationId, meId(jwt)); + return ResponseEntity.ok(res); + } + + /** 공고 지원자 관리 목록 */ + @GetMapping("/job/posts/{postId}/applicants") + @Operation(summary = "공고 지원자 관리 목록", + description = "공고 제목, 총 지원자 수, 지원자 리스트(이름/지원 날짜/positions)를 반환합니다.") + public ResponseEntity applicantsOfPost( + @AuthenticationPrincipal Jwt jwt, + @PathVariable Long postId + ) { + return ResponseEntity.ok( + employerApplicationService.getApplicantsOfPost(meId(jwt), postId) + ); + } + + /** 구인자 전용 이력서 열람 (applicationId로 접근) */ + @GetMapping("/job/applications/{applicationId}/resume") + @Operation(summary = "지원서 기반 이력서 조회(구인자용)", + description = "공고 작성자가 자신에게 접수된 지원서의 이력서를 열람합니다.") + public ResponseEntity getResumeForEmployer( + @AuthenticationPrincipal Jwt jwt, + @PathVariable Long applicationId + ) { + return ResponseEntity.ok( + employerApplicationService.getResumeForEmployer(meId(jwt), applicationId) + ); + } +} diff --git a/src/main/java/com/chaineeproject/chainee/controller/ResumeController.java b/src/main/java/com/chaineeproject/chainee/controller/ResumeController.java index 9d3dc35..21b4b25 100644 --- a/src/main/java/com/chaineeproject/chainee/controller/ResumeController.java +++ b/src/main/java/com/chaineeproject/chainee/controller/ResumeController.java @@ -35,7 +35,7 @@ public class ResumeController { schema = @Schema(implementation = ResumeCreateRequest.class), examples = @ExampleObject(value = """ { - "applicantDid": "did:example:xyz789", + "applicantId": 123, "title": "프론트엔드 개발자 이력서", "name": "홍길동", "introduction": "React와 Next.js 기반 프로젝트를 다수 진행했습니다.", @@ -53,12 +53,9 @@ public class ResumeController { schema = @Schema(implementation = ResumeResponse.class))) ) public ResponseEntity createResume( - @AuthenticationPrincipal Jwt jwt, // 보안정책상 /api/resumes/** 가 authenticated 라면 주입 + @AuthenticationPrincipal Jwt jwt, // 인증 정책에 따라 사용 여부 결정 @Valid @RequestBody ResumeCreateRequest request ) { - // 현재 정책이 DID 기반 생성이면 uid 없이 진행해도 됨. - // 만약 로그인 사용자의 소유로도 연결하려면 아래처럼 꺼내서 service로 전달: - // Long currentUserId = com.chaineeproject.chainee.security.SecurityUtils.uidOrNull(jwt); Long resumeId = resumeService.createResume(request); return ResponseEntity.ok(new ResumeResponse(true, "RESUME_CREATED", resumeId)); } @@ -112,31 +109,6 @@ public ResponseEntity getMyResume( return ResponseEntity.ok(resumeService.getMyResume(currentUserId, resumeId)); } - // ===== 조회(공고 작성자: 지원서 통해 접근) ===== - @GetMapping("/applications/{applicationId}") - @Operation( - summary = "지원서 기반 이력서 조회(공고 작성자용)", - description = """ - 공고 작성자가 자신에게 접수된 지원서의 이력서를 열람합니다. - - 현재 로그인 사용자는 해당 지원서의 공고 작성자여야 합니다. - """, - responses = { - @ApiResponse(responseCode = "200", content = @Content( - mediaType = "application/json", - schema = @Schema(implementation = ResumeView.class))), - @ApiResponse(responseCode = "403", description = "권한 없음"), - @ApiResponse(responseCode = "404", description = "지원서/이력서 없음") - } - ) - public ResponseEntity getResumeForPostAuthor( - @AuthenticationPrincipal Jwt jwt, - @PathVariable Long applicationId - ) { - Long currentUserId = com.chaineeproject.chainee.security.SecurityUtils.uidOrNull(jwt); - if (currentUserId == null) return ResponseEntity.status(401).build(); - return ResponseEntity.ok(resumeService.getResumeByApplicationForPostAuthor(currentUserId, applicationId)); - } - // ===== 수정(본인) ===== @PutMapping("/{resumeId}") @Operation( diff --git a/src/main/java/com/chaineeproject/chainee/dto/ApplicantSummary.java b/src/main/java/com/chaineeproject/chainee/dto/ApplicantSummary.java new file mode 100644 index 0000000..731e149 --- /dev/null +++ b/src/main/java/com/chaineeproject/chainee/dto/ApplicantSummary.java @@ -0,0 +1,14 @@ +// src/main/java/com/chaineeproject/chainee/dto/ApplicantSummary.java +package com.chaineeproject.chainee.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.LocalDateTime; +import java.util.List; + +public record ApplicantSummary( + @Schema(example = "555") Long applicationId, + @Schema(example = "3") Long applicantId, + @Schema(example = "홍길동") String name, + @Schema(example = "2025-09-02T10:00:00") LocalDateTime appliedAt, + @Schema(example = "[\"Backend\",\"DevOps\"]") List positions +) {} diff --git a/src/main/java/com/chaineeproject/chainee/dto/ApplicationAcceptResponse.java b/src/main/java/com/chaineeproject/chainee/dto/ApplicationAcceptResponse.java new file mode 100644 index 0000000..910ce1a --- /dev/null +++ b/src/main/java/com/chaineeproject/chainee/dto/ApplicationAcceptResponse.java @@ -0,0 +1,20 @@ +// src/main/java/com/chaineeproject/chainee/dto/ApplicationAcceptResponse.java +package com.chaineeproject.chainee.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import java.math.BigDecimal; +import java.time.LocalDate; + +public record ApplicationAcceptResponse( + @Schema(description = "처리 성공 여부", example = "true") + boolean success, + @Schema(description = "지원 상태", example = "accepted") + String status, + @Schema(description = "구직자 DID 주소", example = "did:key:z6Mkv...") + String applicantDid, + @Schema(description = "공고 지급 금액", example = "1500000") + BigDecimal payment, + @Schema(description = "공고 마감일(채용 마감)", example = "2025-09-30") + LocalDate deadline +) {} diff --git a/src/main/java/com/chaineeproject/chainee/dto/PostApplicantsView.java b/src/main/java/com/chaineeproject/chainee/dto/PostApplicantsView.java new file mode 100644 index 0000000..71aa6e2 --- /dev/null +++ b/src/main/java/com/chaineeproject/chainee/dto/PostApplicantsView.java @@ -0,0 +1,12 @@ +// src/main/java/com/chaineeproject/chainee/dto/PostApplicantsView.java +package com.chaineeproject.chainee.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; + +public record PostApplicantsView( + @Schema(example = "101") Long postId, + @Schema(example = "백엔드 개발자 구합니다") String postTitle, + @Schema(example = "7") long totalApplicants, + List applicants +) {} diff --git a/src/main/java/com/chaineeproject/chainee/exception/ErrorCode.java b/src/main/java/com/chaineeproject/chainee/exception/ErrorCode.java index 7ef0504..24891ce 100644 --- a/src/main/java/com/chaineeproject/chainee/exception/ErrorCode.java +++ b/src/main/java/com/chaineeproject/chainee/exception/ErrorCode.java @@ -13,13 +13,16 @@ public enum ErrorCode { RESUME_NOT_FOUND(HttpStatus.NOT_FOUND, "선택한 이력서를 찾을 수 없습니다."), JOB_POST_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 ID의 구인 공고를 찾을 수 없습니다."), APPLICATION_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 ID의 지원서를 찾을 수 없습니다."), + JOB_APPLICATION_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 이력서를 찾을 수 없습니다."), RESUME_NOT_OWNED_BY_APPLICANT(HttpStatus.FORBIDDEN, "해당 이력서는 지원자 본인의 것이 아닙니다."), CANNOT_APPLY_OWN_POST(HttpStatus.FORBIDDEN, "본인이 작성한 공고에는 지원할 수 없습니다."), JOB_POST_CLOSED(HttpStatus.BAD_REQUEST, "해당 공고의 마감일이 지났습니다."), DUPLICATE_APPLICATION(HttpStatus.CONFLICT, "이미 해당 공고에 지원했습니다."), - INVALID_REQUEST(HttpStatus.BAD_REQUEST, "잘못된 요청입니다."); + INVALID_REQUEST(HttpStatus.BAD_REQUEST, "잘못된 요청입니다."), + APPLICANT_DID_REQUIRED(HttpStatus.BAD_REQUEST, "지원자의 DID가 등록 또는 검증되지 않았습니다."), + INVALID_STATUS_TRANSITION(HttpStatus.CONFLICT, "현재 상태에서 요청하신 상태 변경을 수행할 수 없습니다."); private final HttpStatus status; private final String message; diff --git a/src/main/java/com/chaineeproject/chainee/repository/JobApplicationRepository.java b/src/main/java/com/chaineeproject/chainee/repository/JobApplicationRepository.java index af0322a..792b2f0 100644 --- a/src/main/java/com/chaineeproject/chainee/repository/JobApplicationRepository.java +++ b/src/main/java/com/chaineeproject/chainee/repository/JobApplicationRepository.java @@ -1,4 +1,4 @@ -// JobApplicationRepository.java +// src/main/java/com/chaineeproject/chainee/repository/JobApplicationRepository.java package com.chaineeproject.chainee.repository; import com.chaineeproject.chainee.entity.JobApplication; @@ -10,26 +10,22 @@ public interface JobApplicationRepository extends JpaRepository { - // (기존) 파서 방식 — 필요 없으면 지워도 됨 + /** + * [유지] 서비스에서 사용 중: 중복 지원 방지 체크 + * JobApplicationService.applyToJob() 에서 사용 + */ boolean existsByPostIdAndApplicantId(Long postId, Long applicantId); - long countByPostId(Long postId); - // ✅ 언더스코어 없이 JPQL로 직접 정의 - @Query(""" - select (count(a) > 0) - from JobApplication a - where a.post.id = :postId and a.applicant.id = :applicantId - """) - boolean existsForPostAndApplicant(@Param("postId") Long postId, - @Param("applicantId") Long applicantId); - - @Query(""" - select count(a) - from JobApplication a - where a.post.id = :postId - """) - long countByPost(@Param("postId") Long postId); + /** + * [유지] 공고 지원자 수 조회(집계용) + * JobPostRepository.incrementApplicantCount(...)와 함께 사용 가능 + */ + long countByPostId(Long postId); + /** + * [선택 유지] postId + applicantId로 지원서 단건 조회 + * 필요 시 사용(예: 상태 확인/수정) + */ @Query(""" select a from JobApplication a @@ -38,6 +34,10 @@ select count(a) Optional findByPostAndApplicant(@Param("postId") Long postId, @Param("applicantId") Long applicantId); + /** + * [핵심] 지원 수락 API 용: 지원서 + 공고 + 작성자 + 지원자까지 전부 즉시 로딩 + * EmployerApplicationService.acceptApplication(...) 에서 사용 + */ @Query(""" select a from JobApplication a @@ -46,5 +46,27 @@ Optional findByPostAndApplicant(@Param("postId") Long postId, join fetch a.applicant applicant where a.id = :id """) - Optional findWithPostAndUsersById(@Param("id") Long id); + Optional findByIdWithPostAndUsers(@Param("id") Long id); + + @Query(""" + select a + from JobApplication a + join fetch a.applicant u + join a.post p + where p.id = :postId + order by a.createdAt desc, a.id desc +""") + java.util.List + findAllByPostIdWithApplicant(@Param("postId") Long postId); + + @Query(""" + select a + from JobApplication a + join fetch a.post p + join fetch p.author author + join fetch a.applicant applicant + join fetch a.resume r + where a.id = :id +""") + Optional findByIdWithPostApplicantAndResume(@Param("id") Long id); } diff --git a/src/main/java/com/chaineeproject/chainee/service/ChatService.java b/src/main/java/com/chaineeproject/chainee/service/ChatService.java index 2876b3b..faebb46 100644 --- a/src/main/java/com/chaineeproject/chainee/service/ChatService.java +++ b/src/main/java/com/chaineeproject/chainee/service/ChatService.java @@ -49,7 +49,7 @@ public Conversation startConversation(Long postId, Long applicantId, Long reques } // 지원 여부 검증 - boolean hasApplied = jobAppRepo.existsForPostAndApplicant(postId, applicantId); + boolean hasApplied = jobAppRepo.existsByPostIdAndApplicantId(postId, applicantId); if (!hasApplied) throw new IllegalStateException("APPLICANT_NOT_APPLIED"); // applicant 프록시 참조(TransientProperty 방지) diff --git a/src/main/java/com/chaineeproject/chainee/service/EmployerApplicationService.java b/src/main/java/com/chaineeproject/chainee/service/EmployerApplicationService.java new file mode 100644 index 0000000..49ea8a0 --- /dev/null +++ b/src/main/java/com/chaineeproject/chainee/service/EmployerApplicationService.java @@ -0,0 +1,118 @@ +// src/main/java/com/chaineeproject/chainee/service/EmployerApplicationService.java +package com.chaineeproject.chainee.service; + +import com.chaineeproject.chainee.dto.ApplicantSummary; +import com.chaineeproject.chainee.dto.ApplicationAcceptResponse; +import com.chaineeproject.chainee.dto.PostApplicantsView; +import com.chaineeproject.chainee.dto.ResumeView; +import com.chaineeproject.chainee.entity.JobApplication; +import com.chaineeproject.chainee.entity.JobPost; +import com.chaineeproject.chainee.entity.User; +import com.chaineeproject.chainee.exception.ApplicationException; +import com.chaineeproject.chainee.exception.ErrorCode; +import com.chaineeproject.chainee.repository.JobApplicationRepository; +import com.chaineeproject.chainee.repository.JobPostRepository; +import com.chaineeproject.chainee.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class EmployerApplicationService { + + private final JobApplicationRepository jobApplicationRepository; + private final JobPostRepository jobPostRepository; + private final UserRepository userRepository; + + /** 지원 수락: applicant DID + post payment + deadline 반환 */ + @Transactional + public ApplicationAcceptResponse acceptApplication(Long applicationId, Long employerUserId) { + JobApplication app = jobApplicationRepository.findByIdWithPostAndUsers(applicationId) + .orElseThrow(() -> new ApplicationException(ErrorCode.JOB_APPLICATION_NOT_FOUND)); + + JobPost post = app.getPost(); + + // 권한: 현재 사용자 == 공고 작성자 + if (!post.getAuthor().getId().equals(employerUserId)) { + throw new ApplicationException(ErrorCode.FORBIDDEN); + } + + // 상태 전이 검증(선택) + if (app.getStatus() != null && !"pending".equals(app.getStatus())) { + throw new ApplicationException(ErrorCode.INVALID_STATUS_TRANSITION); + } + + // 지원자 DID 검증 + User applicant = app.getApplicant(); + if (applicant.getDid() == null || applicant.getDid().isBlank() || !applicant.isDidVerified()) { + throw new ApplicationException(ErrorCode.APPLICANT_DID_REQUIRED); + } + + // 수락 처리 + app.setStatus("accepted"); + + return new ApplicationAcceptResponse( + true, + app.getStatus(), + applicant.getDid(), + post.getPayment(), + post.getDeadline() + ); + } + + /** 공고별 지원자 관리 목록 */ + @Transactional(readOnly = true) + public PostApplicantsView getApplicantsOfPost(Long employerUserId, Long postId) { + JobPost post = jobPostRepository.findById(postId) + .orElseThrow(() -> new ApplicationException(ErrorCode.JOB_POST_NOT_FOUND)); + + if (!post.getAuthor().getId().equals(employerUserId)) { + throw new ApplicationException(ErrorCode.FORBIDDEN); + } + + List apps = jobApplicationRepository.findAllByPostIdWithApplicant(postId); + + var items = apps.stream().map(a -> new ApplicantSummary( + a.getId(), + a.getApplicant().getId(), + a.getApplicant().getName(), + a.getCreatedAt(), + a.getApplicant().getPositions() == null ? List.of() : a.getApplicant().getPositions() + )).toList(); + + long total = post.getApplicantCount(); // 실시간이면 apps.size() + return new PostApplicantsView(post.getId(), post.getTitle(), total, items); + } + + /** 구인자 전용: 특정 지원서의 이력서를 열람 */ + @Transactional(readOnly = true) + public ResumeView getResumeForEmployer(Long employerUserId, Long applicationId) { + var app = jobApplicationRepository.findByIdWithPostApplicantAndResume(applicationId) + .orElseThrow(() -> new ApplicationException(ErrorCode.JOB_APPLICATION_NOT_FOUND)); + + if (!app.getPost().getAuthor().getId().equals(employerUserId)) { + throw new ApplicationException(ErrorCode.FORBIDDEN); + } + + var r = app.getResume(); + var skills = (r.getSkills() == null || r.getSkills().isBlank()) + ? java.util.List.of() + : java.util.Arrays.stream(r.getSkills().split(",")) + .map(String::trim).filter(s -> !s.isBlank()).toList(); + + return new ResumeView( + r.getId(), + r.getTitle(), + r.getName(), + r.getIntroduction(), + r.getDesiredPosition(), + skills, + r.getCareerLevel(), + r.getPortfolioUrl(), + r.getCreatedAt() + ); + } +} diff --git a/src/main/java/com/chaineeproject/chainee/service/NotificationService.java b/src/main/java/com/chaineeproject/chainee/service/NotificationService.java index 26f9cd5..53c8341 100644 --- a/src/main/java/com/chaineeproject/chainee/service/NotificationService.java +++ b/src/main/java/com/chaineeproject/chainee/service/NotificationService.java @@ -1,18 +1,21 @@ +// src/main/java/com/chaineeproject/chainee/service/NotificationService.java package com.chaineeproject.chainee.service; import com.chaineeproject.chainee.dto.NotificationView; import com.chaineeproject.chainee.dto.PagedNotificationsResponse; -import com.chaineeproject.chainee.entity.*; +import com.chaineeproject.chainee.entity.Notification; +import com.chaineeproject.chainee.entity.User; import com.chaineeproject.chainee.entity.enums.NotificationType; import com.chaineeproject.chainee.event.JobApplicationCreatedEvent; import com.chaineeproject.chainee.repository.JobApplicationRepository; import com.chaineeproject.chainee.repository.NotificationRepository; import lombok.RequiredArgsConstructor; -import org.springframework.context.event.EventListener; -import org.springframework.data.domain.*; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.event.TransactionalEventListener; +import org.springframework.transaction.event.TransactionPhase; @Service @RequiredArgsConstructor @@ -21,35 +24,32 @@ public class NotificationService { private final NotificationRepository notificationRepository; private final JobApplicationRepository jobApplicationRepository; - // ===== 알림 생성: 지원 발생 시(커밋 이후) ===== - @TransactionalEventListener(fallbackExecution = false) // default BEFORE_COMMIT, 아래서 phase 지정 - public void onJobApplicationCreated_BEFORE_COMMIT(JobApplicationCreatedEvent ignored) { - // no-op: 문서화 목적. 실제 생성은 AFTER_COMMIT에서. - } - - @TransactionalEventListener(phase = org.springframework.transaction.event.TransactionPhase.AFTER_COMMIT) + // 지원 발생 후 커밋이 완료된 다음 알림 생성 + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) public void onJobApplicationCreated(JobApplicationCreatedEvent event) { - jobApplicationRepository.findWithPostAndUsersById(event.applicationId()).ifPresent(app -> { - User author = app.getPost().getAuthor(); - User applicant = app.getApplicant(); + jobApplicationRepository.findByIdWithPostAndUsers(event.applicationId()) // ✅ 메서드명 변경 + .ifPresent(app -> { + User author = app.getPost().getAuthor(); + User applicant = app.getApplicant(); - String title = "새 지원이 도착했어요"; - String message = String.format("%s님이 \"%s\" 공고에 지원했습니다.", - nullSafe(applicant.getName(), "익명"), nullSafe(app.getPost().getTitle(), "공고")); + String title = "새 지원이 도착했어요"; + String message = String.format("%s님이 \"%s\" 공고에 지원했습니다.", + nullSafe(applicant.getName(), "익명"), + nullSafe(app.getPost().getTitle(), "공고")); - // 프론트 라우트에 맞게 링크 구성 (예시) - String link = "/jobs/" + app.getPost().getId() + "/applications/" + app.getId(); + String link = "/jobs/" + app.getPost().getId() + + "/applications/" + app.getId(); - Notification noti = Notification.builder() - .recipient(author) - .actor(applicant) - .notificationType(NotificationType.JOB_APPLICATION_RECEIVED) - .title(title) - .message(message) - .linkUrl(link) - .build(); - notificationRepository.save(noti); - }); + Notification noti = Notification.builder() + .recipient(author) + .actor(applicant) + .notificationType(NotificationType.JOB_APPLICATION_RECEIVED) + .title(title) + .message(message) + .linkUrl(link) + .build(); + notificationRepository.save(noti); + }); } private String nullSafe(String s, String def) { return (s == null || s.isBlank()) ? def : s; } @@ -82,7 +82,6 @@ public void markRead(Long currentUserId, Long notificationId) { @Transactional public void markAllRead(Long currentUserId) { - // 간단 구현: 페이지로 가져와 일괄 setRead(true) int page = 0, size = 200; Page p; do { diff --git a/src/main/java/com/chaineeproject/chainee/service/ResumeService.java b/src/main/java/com/chaineeproject/chainee/service/ResumeService.java index a7a6901..7dc20c3 100644 --- a/src/main/java/com/chaineeproject/chainee/service/ResumeService.java +++ b/src/main/java/com/chaineeproject/chainee/service/ResumeService.java @@ -1,13 +1,10 @@ -// src/main/java/com/chaineeproject/chainee/service/ResumeService.java package com.chaineeproject.chainee.service; import com.chaineeproject.chainee.dto.*; -import com.chaineeproject.chainee.entity.JobApplication; import com.chaineeproject.chainee.entity.Resume; import com.chaineeproject.chainee.entity.User; import com.chaineeproject.chainee.exception.ApplicationException; import com.chaineeproject.chainee.exception.ErrorCode; -import com.chaineeproject.chainee.repository.JobApplicationRepository; import com.chaineeproject.chainee.repository.ResumeRepository; import com.chaineeproject.chainee.repository.UserRepository; import lombok.RequiredArgsConstructor; @@ -25,7 +22,6 @@ public class ResumeService { private final UserRepository userRepository; private final ResumeRepository resumeRepository; - private final JobApplicationRepository jobApplicationRepository; // ========= 생성 ========= public Long createResume(ResumeCreateRequest request) { @@ -64,19 +60,6 @@ public ResumeView getMyResume(Long currentUserId, Long resumeId) { return toView(r); } - // ========= 조회(공고 작성자: 지원서 통해 접근) ========= - @Transactional(readOnly = true) - public ResumeView getResumeByApplicationForPostAuthor(Long currentUserId, Long applicationId) { - JobApplication app = jobApplicationRepository.findById(applicationId) - .orElseThrow(() -> new ApplicationException(ErrorCode.APPLICATION_NOT_FOUND)); - - // 권한 체크: 현재 유저 == 공고 작성자 - if (!app.getPost().getAuthor().getId().equals(currentUserId)) { - throw new ApplicationException(ErrorCode.FORBIDDEN); - } - return toView(app.getResume()); - } - // ========= 수정(본인, 부분 업데이트) ========= public ResumeView updateMyResume(Long currentUserId, Long resumeId, ResumeUpdateRequest req) { Resume r = resumeRepository.findById(resumeId)