diff --git a/backend/src/main/java/moadong/club/controller/ClubApplyController.java b/backend/src/main/java/moadong/club/controller/ClubApplyController.java index 5a8752653..c8c90e2ac 100644 --- a/backend/src/main/java/moadong/club/controller/ClubApplyController.java +++ b/backend/src/main/java/moadong/club/controller/ClubApplyController.java @@ -4,11 +4,10 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.AllArgsConstructor; -import moadong.club.entity.ClubApplication; +import moadong.club.payload.request.ClubApplicantEditRequest; import moadong.club.payload.request.ClubApplicationCreateRequest; import moadong.club.payload.request.ClubApplicationEditRequest; import moadong.club.payload.request.ClubApplyRequest; -import moadong.club.payload.response.ClubApplicationResponse; import moadong.club.service.ClubApplyService; import moadong.global.payload.Response; import moadong.user.annotation.CurrentUser; @@ -18,8 +17,6 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import java.util.List; - @RestController @RequestMapping("/api/club/{clubId}") @AllArgsConstructor @@ -73,4 +70,33 @@ public ResponseEntity getApplyInfo(@PathVariable String clubId, return Response.ok(clubApplyService.getClubApplyInfo(clubId, user)); } + @PutMapping("/apply/{appId}") + @Operation(summary = "지원서 변경", + description = "클럽 자원자의 지원서 정보를 수정합니다.
" + + "appId - 지원서 아이디" + ) + @PreAuthorize("isAuthenticated()") + @SecurityRequirement(name = "BearerAuth") + public ResponseEntity editApplicantDetail(@PathVariable String clubId, + @PathVariable String appId, + @RequestBody @Validated ClubApplicantEditRequest request, + @CurrentUser CustomUserDetails user) { + clubApplyService.editApplicantDetail(clubId, appId, request, user); + return Response.ok("success edit applicant"); + } + + @DeleteMapping("/apply/{appId}") + @Operation(summary = "지원서 삭제", + description = "클럽 자원자의 지원서를 삭제합니다.
" + + "appId - 지원서 아이디" + ) + @PreAuthorize("isAuthenticated()") + @SecurityRequirement(name = "BearerAuth") + public ResponseEntity removeApplicant(@PathVariable String clubId, + @PathVariable String appId, + @CurrentUser CustomUserDetails user) { + clubApplyService.deleteApplicant(clubId, appId, user); + return Response.ok("success delete applicant"); + } + } diff --git a/backend/src/main/java/moadong/club/entity/ClubApplication.java b/backend/src/main/java/moadong/club/entity/ClubApplication.java index 0b17f2153..7e94df822 100644 --- a/backend/src/main/java/moadong/club/entity/ClubApplication.java +++ b/backend/src/main/java/moadong/club/entity/ClubApplication.java @@ -30,9 +30,17 @@ public class ClubApplication { @Builder.Default ApplicationStatus status = ApplicationStatus.SUBMITTED; + @Builder.Default + private String memo = ""; + @Builder.Default private List answers = new ArrayList<>(); @Builder.Default LocalDateTime createdAt = ZonedDateTime.now(ZoneId.of("Asia/Seoul")).toLocalDateTime(); + + public void updateDetail(String memo, ApplicationStatus status) { + this.memo = memo; + this.status = status; + } } diff --git a/backend/src/main/java/moadong/club/payload/dto/ClubApplicantsResult.java b/backend/src/main/java/moadong/club/payload/dto/ClubApplicantsResult.java index e44ec0cac..2c8b60c6a 100644 --- a/backend/src/main/java/moadong/club/payload/dto/ClubApplicantsResult.java +++ b/backend/src/main/java/moadong/club/payload/dto/ClubApplicantsResult.java @@ -9,15 +9,17 @@ import moadong.global.exception.RestApiException; import moadong.global.util.AESCipher; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @Builder @Slf4j public record ClubApplicantsResult( - String questionId, + String id, ApplicationStatus status, - List answers + List answers, + LocalDateTime createdAt ) { public static ClubApplicantsResult of(ClubApplication application, AESCipher cipher) { List decryptedAnswers = new ArrayList<>(); @@ -35,9 +37,10 @@ public static ClubApplicantsResult of(ClubApplication application, AESCipher cip } return ClubApplicantsResult.builder() - .questionId(application.getQuestionId()) + .id(application.getId()) .status(application.getStatus()) .answers(decryptedAnswers) + .createdAt(application.getCreatedAt()) .build(); } } \ No newline at end of file diff --git a/backend/src/main/java/moadong/club/payload/request/ClubApplicantEditRequest.java b/backend/src/main/java/moadong/club/payload/request/ClubApplicantEditRequest.java new file mode 100644 index 000000000..7a143c781 --- /dev/null +++ b/backend/src/main/java/moadong/club/payload/request/ClubApplicantEditRequest.java @@ -0,0 +1,15 @@ +package moadong.club.payload.request; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import moadong.club.enums.ApplicationStatus; + +public record ClubApplicantEditRequest( + @NotNull + @Size(max = 500) + String memo, + + @NotNull + ApplicationStatus status +) { +} diff --git a/backend/src/main/java/moadong/club/repository/ClubApplicationRepository.java b/backend/src/main/java/moadong/club/repository/ClubApplicationRepository.java index f4b6f3f69..52ba38527 100644 --- a/backend/src/main/java/moadong/club/repository/ClubApplicationRepository.java +++ b/backend/src/main/java/moadong/club/repository/ClubApplicationRepository.java @@ -5,8 +5,11 @@ import org.springframework.data.mongodb.repository.Query; import java.util.List; +import java.util.Optional; public interface ClubApplicationRepository extends MongoRepository { @Query("{ 'questionId': ?0, 'status': { $exists: true, $ne: 'DRAFT' } }") List findAllByQuestionId(String questionId); + + Optional findByIdAndQuestionId(String id, String questionId); } \ No newline at end of file diff --git a/backend/src/main/java/moadong/club/service/ClubApplyService.java b/backend/src/main/java/moadong/club/service/ClubApplyService.java index 7834df050..2bef509df 100644 --- a/backend/src/main/java/moadong/club/service/ClubApplyService.java +++ b/backend/src/main/java/moadong/club/service/ClubApplyService.java @@ -1,10 +1,12 @@ package moadong.club.service; +import jakarta.transaction.Transactional; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import moadong.club.entity.*; import moadong.club.enums.ClubApplicationQuestionType; import moadong.club.payload.dto.ClubApplicantsResult; +import moadong.club.payload.request.ClubApplicantEditRequest; import moadong.club.payload.request.ClubApplicationCreateRequest; import moadong.club.payload.request.ClubApplicationEditRequest; import moadong.club.payload.request.ClubApplyRequest; @@ -123,6 +125,38 @@ public ClubApplyInfoResponse getClubApplyInfo(String clubId, CustomUserDetails u .build(); } + @Transactional + public void editApplicantDetail(String clubId, String appId, ClubApplicantEditRequest request, CustomUserDetails user) { + Club club = clubRepository.findById(clubId) + .orElseThrow(() -> new RestApiException(ErrorCode.CLUB_NOT_FOUND)); + + if (!user.getId().equals(club.getUserId())) { + throw new RestApiException(ErrorCode.USER_UNAUTHORIZED); + } + + ClubApplication application = clubApplicationRepository.findByIdAndQuestionId(appId, clubId) + .orElseThrow(() -> new RestApiException(ErrorCode.APPLICANT_NOT_FOUND)); + + application.updateDetail(request.memo(), request.status()); + + clubApplicationRepository.save(application); + } + + @Transactional + public void deleteApplicant(String clubId, String appId, CustomUserDetails user) { + Club club = clubRepository.findById(clubId) + .orElseThrow(() -> new RestApiException(ErrorCode.CLUB_NOT_FOUND)); + + if (!user.getId().equals(club.getUserId())) { + throw new RestApiException(ErrorCode.USER_UNAUTHORIZED); + } + + ClubApplication application = clubApplicationRepository.findByIdAndQuestionId(appId, clubId) + .orElseThrow(() -> new RestApiException(ErrorCode.APPLICANT_NOT_FOUND)); + + clubApplicationRepository.delete(application); + } + private void validateAnswers(List answers, ClubQuestion clubQuestion) { // 미리 질문과 응답 id 만들어두기 Map questionMap = clubQuestion.getQuestions().stream() diff --git a/backend/src/main/java/moadong/global/exception/ErrorCode.java b/backend/src/main/java/moadong/global/exception/ErrorCode.java index e0b6ae873..f32b7ed6b 100644 --- a/backend/src/main/java/moadong/global/exception/ErrorCode.java +++ b/backend/src/main/java/moadong/global/exception/ErrorCode.java @@ -39,7 +39,8 @@ public enum ErrorCode { QUESTION_NOT_FOUND(HttpStatus.NOT_FOUND, "800-4", "존재하지 않은 질문입니다."), REQUIRED_QUESTION_MISSING(HttpStatus.BAD_REQUEST, "800-5", "필수 응답 질문이 누락되었습니다."), - AES_CIPHER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "900-1", "암호화 중 오류가 발생했습니다.") + AES_CIPHER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "900-1", "암호화 중 오류가 발생했습니다."), + APPLICANT_NOT_FOUND(HttpStatus.NOT_FOUND, "900-2", "지원서가 존재하지 않습니다."), ; private final HttpStatus httpStatus;