From 53b665e821b336824f9ec58d0a630a8fd5f4f2ba Mon Sep 17 00:00:00 2001 From: yeoEun Date: Sun, 9 Nov 2025 20:02:40 +0900 Subject: [PATCH 1/2] fix/notification --- .../controller/JobApplicationController.java | 21 +++++----- .../dto/JobApplicationApplyRequest.java | 4 ++ .../service/JobApplicationService.java | 31 ++++++++++---- .../chainee/service/NotificationService.java | 40 ------------------- 4 files changed, 39 insertions(+), 57 deletions(-) create mode 100644 src/main/java/com/chaineeproject/chainee/dto/JobApplicationApplyRequest.java diff --git a/src/main/java/com/chaineeproject/chainee/controller/JobApplicationController.java b/src/main/java/com/chaineeproject/chainee/controller/JobApplicationController.java index 2a4afd2..729334d 100644 --- a/src/main/java/com/chaineeproject/chainee/controller/JobApplicationController.java +++ b/src/main/java/com/chaineeproject/chainee/controller/JobApplicationController.java @@ -1,13 +1,12 @@ -// src/main/java/com/chaineeproject/chainee/controller/JobApplicationController.java package com.chaineeproject.chainee.controller; -import com.chaineeproject.chainee.dto.JobApplicationRequest; +import com.chaineeproject.chainee.dto.JobApplicationApplyRequest; import com.chaineeproject.chainee.dto.JobApplicationResponse; +import com.chaineeproject.chainee.security.SecurityUtils; import com.chaineeproject.chainee.service.JobApplicationService; 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; @@ -23,17 +22,19 @@ public class JobApplicationController { private final JobApplicationService jobApplicationService; @PostMapping("/{postId}/apply") - @Operation(summary = "채용 공고 지원", description = "요청 사용자의 JWT uid를 지원자로 하여 지정한 공고에 지원합니다.") + @Operation(summary = "채용 공고 지원", + description = "요청 사용자의 JWT uid를 지원자로 하여 지정한 공고에 지원합니다.") public ResponseEntity applyToJob( - @AuthenticationPrincipal Jwt jwt, @Parameter(description = "지원할 채용 공고 ID", example = "1") @PathVariable Long postId, - @Valid @RequestBody JobApplicationRequest request + @AuthenticationPrincipal Jwt jwt, + @RequestBody JobApplicationApplyRequest request ) { - Long applicantId = com.chaineeproject.chainee.security.SecurityUtils.uidOrNull(jwt); - if (applicantId == null) return ResponseEntity.status(401).build(); - - jobApplicationService.applyToJob(postId, applicantId, request.resumeId()); + Long currentUserId = SecurityUtils.uidOrNull(jwt); + if (currentUserId == null) { + return ResponseEntity.status(401).body(new JobApplicationResponse(false, "UNAUTHORIZED")); + } + jobApplicationService.applyToJob(postId, currentUserId, request.resumeId()); return ResponseEntity.ok(new JobApplicationResponse(true, "APPLICATION_SUBMITTED")); } } diff --git a/src/main/java/com/chaineeproject/chainee/dto/JobApplicationApplyRequest.java b/src/main/java/com/chaineeproject/chainee/dto/JobApplicationApplyRequest.java new file mode 100644 index 0000000..59180e1 --- /dev/null +++ b/src/main/java/com/chaineeproject/chainee/dto/JobApplicationApplyRequest.java @@ -0,0 +1,4 @@ +package com.chaineeproject.chainee.dto; + +public class JobApplicationApplyRequest { +} diff --git a/src/main/java/com/chaineeproject/chainee/service/JobApplicationService.java b/src/main/java/com/chaineeproject/chainee/service/JobApplicationService.java index 4592da1..2a2dbce 100644 --- a/src/main/java/com/chaineeproject/chainee/service/JobApplicationService.java +++ b/src/main/java/com/chaineeproject/chainee/service/JobApplicationService.java @@ -1,14 +1,12 @@ -// src/main/java/com/chaineeproject/chainee/service/JobApplicationService.java package com.chaineeproject.chainee.service; import com.chaineeproject.chainee.entity.*; -import com.chaineeproject.chainee.event.JobApplicationCreatedEvent; +import com.chaineeproject.chainee.entity.enums.NotificationType; import com.chaineeproject.chainee.exception.ApplicationException; import com.chaineeproject.chainee.exception.ErrorCode; import com.chaineeproject.chainee.repository.*; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -23,8 +21,11 @@ public class JobApplicationService { private final JobPostRepository jobPostRepository; private final ResumeRepository resumeRepository; private final JobApplicationRepository jobApplicationRepository; - private final ApplicationEventPublisher eventPublisher; + private final NotificationRepository notificationRepository; // ✅ 알림 직접 저장 + /** + * JWT uid = applicantId + */ @Transactional public void applyToJob(Long postId, Long applicantId, Long resumeId) { User applicant = userRepository.findById(applicantId) @@ -49,11 +50,27 @@ public void applyToJob(Long postId, Long applicantId, Long resumeId) { application.setResume(resume); application.setStatus("pending"); application.setCreatedAt(LocalDateTime.now()); - jobApplicationRepository.save(application); + jobPostRepository.incrementApplicantCount(postId); - log.debug("Publishing JobApplicationCreatedEvent: {}", application.getId()); - eventPublisher.publishEvent(new JobApplicationCreatedEvent(application.getId())); + // ✅ 지원 저장 직후 바로 알림 생성 (이벤트/트랜잭션 훅 의존 X) + Notification noti = Notification.builder() + .recipient(post.getAuthor()) // 공고 작성자 + .actor(applicant) // 지원자 + .notificationType(NotificationType.JOB_APPLICATION_RECEIVED) + .title("새 지원이 도착했어요") + .message(String.format("%s님이 \"%s\" 공고에 지원했습니다.", + nz(applicant.getName(), "익명"), + nz(post.getTitle(), "공고"))) + .linkUrl("/jobs/" + postId + "/applications/" + application.getId()) + .build(); + notificationRepository.save(noti); + + log.debug("Application {} created; Notification {} saved.", application.getId(), noti.getId()); + } + + private static String nz(String s, String def) { + return (s == null || s.isBlank()) ? def : s; } } diff --git a/src/main/java/com/chaineeproject/chainee/service/NotificationService.java b/src/main/java/com/chaineeproject/chainee/service/NotificationService.java index f98b4fc..fef994a 100644 --- a/src/main/java/com/chaineeproject/chainee/service/NotificationService.java +++ b/src/main/java/com/chaineeproject/chainee/service/NotificationService.java @@ -1,60 +1,20 @@ -// 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.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.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; -import lombok.extern.slf4j.Slf4j; @Service @RequiredArgsConstructor -@Slf4j public class NotificationService { private final NotificationRepository notificationRepository; - private final JobApplicationRepository jobApplicationRepository; - - // 지원 발생 후 커밋이 완료된 다음 알림 생성 - @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) - public void onJobApplicationCreated(JobApplicationCreatedEvent event) { - 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 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); - }); - } - - private String nullSafe(String s, String def) { return (s == null || s.isBlank()) ? def : s; } // ===== 조회/읽음 처리 ===== @Transactional(readOnly = true) From 9dd0ee149d9276d52ef832f96c31cb30686c8316 Mon Sep 17 00:00:00 2001 From: yeoEun Date: Sun, 9 Nov 2025 20:09:21 +0900 Subject: [PATCH 2/2] fix/notification --- .../chainee/controller/JobApplicationController.java | 2 +- .../chainee/dto/JobApplicationApplyRequest.java | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/chaineeproject/chainee/controller/JobApplicationController.java b/src/main/java/com/chaineeproject/chainee/controller/JobApplicationController.java index 729334d..7b9ffca 100644 --- a/src/main/java/com/chaineeproject/chainee/controller/JobApplicationController.java +++ b/src/main/java/com/chaineeproject/chainee/controller/JobApplicationController.java @@ -34,7 +34,7 @@ public ResponseEntity applyToJob( if (currentUserId == null) { return ResponseEntity.status(401).body(new JobApplicationResponse(false, "UNAUTHORIZED")); } - jobApplicationService.applyToJob(postId, currentUserId, request.resumeId()); + jobApplicationService.applyToJob(postId, currentUserId, request.getResumeId()); return ResponseEntity.ok(new JobApplicationResponse(true, "APPLICATION_SUBMITTED")); } } diff --git a/src/main/java/com/chaineeproject/chainee/dto/JobApplicationApplyRequest.java b/src/main/java/com/chaineeproject/chainee/dto/JobApplicationApplyRequest.java index 59180e1..218c3b2 100644 --- a/src/main/java/com/chaineeproject/chainee/dto/JobApplicationApplyRequest.java +++ b/src/main/java/com/chaineeproject/chainee/dto/JobApplicationApplyRequest.java @@ -1,4 +1,16 @@ package com.chaineeproject.chainee.dto; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.AllArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Schema(description = "채용 공고 지원 요청 DTO(JWT uid 사용)") public class JobApplicationApplyRequest { + + @Schema(description = "지원자가 선택한 이력서 ID", example = "42", required = true) + private Long resumeId; }