diff --git a/src/main/java/gg/agit/konect/domain/club/controller/ClubRecruitmentApi.java b/src/main/java/gg/agit/konect/domain/club/controller/ClubRecruitmentApi.java index 6bff888a7..2a1a5c2fb 100644 --- a/src/main/java/gg/agit/konect/domain/club/controller/ClubRecruitmentApi.java +++ b/src/main/java/gg/agit/konect/domain/club/controller/ClubRecruitmentApi.java @@ -20,10 +20,10 @@ public interface ClubRecruitmentApi { @Operation(summary = "동아리 모집 정보를 조회한다.", description = """ 동아리의 모집 공고 상세 정보를 조회합니다. - + - status는 모집 기간에 따라 BEFORE(모집 전), ONGOING(모집 중), CLOSED(모집 마감)으로 반환됩니다. - 동아리 멤버이거나 지원 이력이 존재할 경우 isApplied는 true로 반환됩니다. - + ## 에러 - NOT_FOUND_CLUB (404): 동아리를 찾을 수 없습니다. - NOT_FOUND_USER (404): 유저를 찾을 수 없습니다. @@ -39,6 +39,7 @@ ResponseEntity getRecruitments( 요청 값을 기준으로 동아리 모집 공고를 저장합니다. - 모집 공고가 없으면 생성 - 모집 공고가 있으면 수정 + ## 에러 - INVALID_RECRUITMENT_DATE_NOT_ALLOWED (400): 상시 모집인 경우 모집 시작일과 마감일을 지정할 수 없습니다. - INVALID_RECRUITMENT_DATE_REQUIRED (400): 상시 모집이 아닐 경우 모집 시작일과 마감일이 필수입니다. diff --git a/src/main/java/gg/agit/konect/domain/club/dto/ClubFeeInfoReplaceRequest.java b/src/main/java/gg/agit/konect/domain/club/dto/ClubFeeInfoReplaceRequest.java index e30c9b018..91656adf8 100644 --- a/src/main/java/gg/agit/konect/domain/club/dto/ClubFeeInfoReplaceRequest.java +++ b/src/main/java/gg/agit/konect/domain/club/dto/ClubFeeInfoReplaceRequest.java @@ -1,9 +1,7 @@ package gg.agit.konect.domain.club.dto; import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED; -import java.time.LocalDate; - -import com.fasterxml.jackson.annotation.JsonFormat; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; @@ -27,8 +25,8 @@ public record ClubFeeInfoReplaceRequest( @Schema(description = "예금주", example = "BCSD", requiredMode = NOT_REQUIRED) String accountHolder, - @Schema(description = "납부 기한", example = "2025.12.31", requiredMode = NOT_REQUIRED) - @JsonFormat(pattern = "yyyy.MM.dd") - LocalDate deadLine + @NotNull(message = "회비 납부 필요 여부는 필수로 입력해야 합니다.") + @Schema(description = "회비 납부 필요 여부", example = "true", requiredMode = REQUIRED) + Boolean isFeeRequired ) { } diff --git a/src/main/java/gg/agit/konect/domain/club/dto/ClubFeeInfoResponse.java b/src/main/java/gg/agit/konect/domain/club/dto/ClubFeeInfoResponse.java index fba4d81d0..1a8231623 100644 --- a/src/main/java/gg/agit/konect/domain/club/dto/ClubFeeInfoResponse.java +++ b/src/main/java/gg/agit/konect/domain/club/dto/ClubFeeInfoResponse.java @@ -2,10 +2,6 @@ import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; -import java.time.LocalDate; - -import com.fasterxml.jackson.annotation.JsonFormat; - import gg.agit.konect.domain.club.model.Club; import io.swagger.v3.oas.annotations.media.Schema; @@ -22,9 +18,8 @@ public record ClubFeeInfoResponse( @Schema(description = "예금주", example = "BCSD", requiredMode = REQUIRED) String accountHolder, - @Schema(description = "납부 기한", example = "2025.12.31", requiredMode = REQUIRED) - @JsonFormat(pattern = "yyyy.MM.dd") - LocalDate deadLine + @Schema(description = "회비 납부 필요 여부", example = "true", requiredMode = REQUIRED) + Boolean isFeeRequired ) { public static ClubFeeInfoResponse from(Club club) { return new ClubFeeInfoResponse( @@ -32,7 +27,7 @@ public static ClubFeeInfoResponse from(Club club) { club.getFeeBank(), club.getFeeAccountNumber(), club.getFeeAccountHolder(), - club.getFeeDeadline() + club.getIsFeeRequired() ); } } diff --git a/src/main/java/gg/agit/konect/domain/club/dto/ClubRecruitmentResponse.java b/src/main/java/gg/agit/konect/domain/club/dto/ClubRecruitmentResponse.java index 6a3f68fc7..b93d43005 100644 --- a/src/main/java/gg/agit/konect/domain/club/dto/ClubRecruitmentResponse.java +++ b/src/main/java/gg/agit/konect/domain/club/dto/ClubRecruitmentResponse.java @@ -37,9 +37,6 @@ public record ClubRecruitmentResponse( @Schema(description = "모집 공고 이미지 리스트", requiredMode = REQUIRED) List images, - @Schema(description = "회비 납부 필요 여부", example = "true", requiredMode = REQUIRED) - Boolean isFeeRequired, - @Schema(description = "지원 여부", example = "false", requiredMode = REQUIRED) Boolean isApplied ) { @@ -63,7 +60,6 @@ public static ClubRecruitmentResponse of(ClubRecruitment recruitment, Boolean is recruitment.getImages().stream() .map(InnerRecruitmentImage::from) .toList(), - recruitment.getIsFeeRequired(), isApplied ); } diff --git a/src/main/java/gg/agit/konect/domain/club/dto/ClubRecruitmentUpsertRequest.java b/src/main/java/gg/agit/konect/domain/club/dto/ClubRecruitmentUpsertRequest.java index 558126857..c807a28f5 100644 --- a/src/main/java/gg/agit/konect/domain/club/dto/ClubRecruitmentUpsertRequest.java +++ b/src/main/java/gg/agit/konect/domain/club/dto/ClubRecruitmentUpsertRequest.java @@ -26,10 +26,6 @@ public record ClubRecruitmentUpsertRequest( @Schema(description = "상시 모집 여부", example = "false", requiredMode = REQUIRED) Boolean isAlwaysRecruiting, - @NotNull(message = "회비 납부 필요 여부는 필수 입력입니다.") - @Schema(description = "회비 납부 필요 여부", example = "true", requiredMode = REQUIRED) - Boolean isFeeRequired, - @NotEmpty(message = "모집 공고 내용은 필수 입력입니다.") @Schema(description = "모집 공고 내용", example = "BCSD 2025학년도 2학기 신입 부원 모집...", requiredMode = REQUIRED) String content, diff --git a/src/main/java/gg/agit/konect/domain/club/model/Club.java b/src/main/java/gg/agit/konect/domain/club/model/Club.java index 769d9d2b9..c93d2f45b 100644 --- a/src/main/java/gg/agit/konect/domain/club/model/Club.java +++ b/src/main/java/gg/agit/konect/domain/club/model/Club.java @@ -7,8 +7,6 @@ import static jakarta.persistence.GenerationType.IDENTITY; import static lombok.AccessLevel.PROTECTED; -import java.time.LocalDate; - import org.springframework.util.StringUtils; import gg.agit.konect.domain.club.dto.ClubCreateRequest; @@ -77,8 +75,8 @@ public class Club extends BaseEntity { @Column(name = "fee_account_holder", length = 100) private String feeAccountHolder; - @Column(name = "fee_deadline") - private LocalDate feeDeadline; + @Column(name = "is_fee_required", columnDefinition = "TINYINT(1)") + private Boolean isFeeRequired; @OneToOne(mappedBy = "club", fetch = LAZY, cascade = ALL, orphanRemoval = true) private ClubRecruitment clubRecruitment; @@ -97,7 +95,7 @@ private Club( String feeBank, String feeAccountNumber, String feeAccountHolder, - LocalDate feeDeadline, + Boolean isFeeRequired, ClubRecruitment clubRecruitment ) { this.id = id; @@ -112,7 +110,7 @@ private Club( this.feeBank = feeBank; this.feeAccountNumber = feeAccountNumber; this.feeAccountHolder = feeAccountHolder; - this.feeDeadline = feeDeadline; + this.isFeeRequired = isFeeRequired; this.clubRecruitment = clubRecruitment; } @@ -133,18 +131,18 @@ public void replaceFeeInfo( String feeBank, String feeAccountNumber, String feeAccountHolder, - LocalDate feeDeadline + Boolean isFeeRequired ) { - if (isFeeInfoEmpty(feeAmount, feeBank, feeAccountNumber, feeAccountHolder, feeDeadline)) { + if (isFeeInfoEmpty(feeAmount, feeBank, feeAccountNumber, feeAccountHolder)) { clearFeeInfo(); return; } - if (!isFeeInfoComplete(feeAmount, feeBank, feeAccountNumber, feeAccountHolder, feeDeadline)) { + if (!isFeeInfoComplete(feeAmount, feeBank, feeAccountNumber, feeAccountHolder)) { throw CustomException.of(INVALID_REQUEST_BODY); } - updateFeeInfo(feeAmount, feeBank, feeAccountNumber, feeAccountHolder, feeDeadline); + updateFeeInfo(feeAmount, feeBank, feeAccountNumber, feeAccountHolder, isFeeRequired); } public void updateInfo(String description, String imageUrl, String location, String introduce) { @@ -163,28 +161,24 @@ private boolean isFeeInfoEmpty( Integer feeAmount, String feeBank, String feeAccountNumber, - String feeAccountHolder, - LocalDate feeDeadline + String feeAccountHolder ) { return feeAmount == null && feeBank == null && feeAccountNumber == null - && feeAccountHolder == null - && feeDeadline == null; + && feeAccountHolder == null; } private boolean isFeeInfoComplete( Integer feeAmount, String feeBank, String feeAccountNumber, - String feeAccountHolder, - LocalDate feeDeadline + String feeAccountHolder ) { return feeAmount != null && StringUtils.hasText(feeBank) && StringUtils.hasText(feeAccountNumber) - && StringUtils.hasText(feeAccountHolder) - && feeDeadline != null; + && StringUtils.hasText(feeAccountHolder); } private void updateFeeInfo( @@ -192,13 +186,13 @@ private void updateFeeInfo( String feeBank, String feeAccountNumber, String feeAccountHolder, - LocalDate feeDeadline + Boolean isFeeRequired ) { this.feeAmount = feeAmount; this.feeBank = feeBank; this.feeAccountNumber = feeAccountNumber; this.feeAccountHolder = feeAccountHolder; - this.feeDeadline = feeDeadline; + this.isFeeRequired = isFeeRequired; } private void clearFeeInfo() { @@ -206,6 +200,6 @@ private void clearFeeInfo() { this.feeBank = null; this.feeAccountNumber = null; this.feeAccountHolder = null; - this.feeDeadline = null; + this.isFeeRequired = null; } } diff --git a/src/main/java/gg/agit/konect/domain/club/model/ClubRecruitment.java b/src/main/java/gg/agit/konect/domain/club/model/ClubRecruitment.java index 4244fb1ad..22e39283f 100644 --- a/src/main/java/gg/agit/konect/domain/club/model/ClubRecruitment.java +++ b/src/main/java/gg/agit/konect/domain/club/model/ClubRecruitment.java @@ -56,9 +56,6 @@ public class ClubRecruitment extends BaseEntity { @Column(name = "is_always_recruiting", columnDefinition = "TINYINT(1)") private Boolean isAlwaysRecruiting; - @Column(name = "is_fee_required", nullable = false, columnDefinition = "TINYINT(1)") - private Boolean isFeeRequired; - @OneToOne(fetch = LAZY) @JoinColumn(name = "club_id", nullable = false, updatable = false) private Club club; @@ -73,7 +70,6 @@ private ClubRecruitment( LocalDate endDate, String content, Boolean isAlwaysRecruiting, - Boolean isFeeRequired, Club club ) { this.id = id; @@ -81,7 +77,6 @@ private ClubRecruitment( this.endDate = endDate; this.content = content; this.isAlwaysRecruiting = isAlwaysRecruiting; - this.isFeeRequired = isFeeRequired; this.club = club; } @@ -89,7 +84,6 @@ public static ClubRecruitment of( LocalDate startDate, LocalDate endDate, Boolean isAlwaysRecruiting, - Boolean isFeeRequired, String content, Club club ) { @@ -106,7 +100,6 @@ public static ClubRecruitment of( .content(content) .club(club) .isAlwaysRecruiting(isAlwaysRecruiting) - .isFeeRequired(isFeeRequired) .build(); } @@ -136,7 +129,6 @@ public void update( LocalDate startDate, LocalDate endDate, Boolean isAlwaysRecruiting, - Boolean isFeeRequired, String content ) { if (isAlwaysRecruiting) { @@ -149,7 +141,6 @@ public void update( this.startDate = startDate; this.endDate = endDate; this.isAlwaysRecruiting = isAlwaysRecruiting; - this.isFeeRequired = isFeeRequired; this.content = content; } } diff --git a/src/main/java/gg/agit/konect/domain/club/service/ClubApplicationService.java b/src/main/java/gg/agit/konect/domain/club/service/ClubApplicationService.java index 3209c67d9..f21d26782 100644 --- a/src/main/java/gg/agit/konect/domain/club/service/ClubApplicationService.java +++ b/src/main/java/gg/agit/konect/domain/club/service/ClubApplicationService.java @@ -188,11 +188,7 @@ public ClubFeeInfoResponse applyClub(Integer clubId, Integer userId, ClubApplyRe } private void validateFeePaymentImage(Club club, String feePaymentImageUrl) { - ClubRecruitment recruitment = clubRecruitmentRepository.findByClubId(club.getId()) - .orElse(null); - - if (recruitment != null - && Boolean.TRUE.equals(recruitment.getIsFeeRequired()) + if (Boolean.TRUE.equals(club.getIsFeeRequired()) && !StringUtils.hasText(feePaymentImageUrl)) { throw CustomException.of(FEE_PAYMENT_IMAGE_REQUIRED); } @@ -341,7 +337,7 @@ public ClubFeeInfoResponse replaceFeeInfo(Integer clubId, Integer userId, ClubFe bankName, request.accountNumber(), request.accountHolder(), - request.deadLine() + request.isFeeRequired() ); return ClubFeeInfoResponse.from(club); diff --git a/src/main/java/gg/agit/konect/domain/club/service/ClubRecruitmentService.java b/src/main/java/gg/agit/konect/domain/club/service/ClubRecruitmentService.java index 4e5670404..4218fbf4b 100644 --- a/src/main/java/gg/agit/konect/domain/club/service/ClubRecruitmentService.java +++ b/src/main/java/gg/agit/konect/domain/club/service/ClubRecruitmentService.java @@ -52,7 +52,6 @@ public void upsertRecruitment(Integer clubId, Integer userId, ClubRecruitmentUps request.startDate(), request.endDate(), request.isAlwaysRecruiting(), - request.isFeeRequired(), request.content(), club )); @@ -62,14 +61,20 @@ public void upsertRecruitment(Integer clubId, Integer userId, ClubRecruitmentUps request.startDate(), request.endDate(), request.isAlwaysRecruiting(), - request.isFeeRequired(), request.content() ); clubRecruitment.getImages().clear(); } - List imageUrls = request.getImageUrls(); + addImages(clubRecruitment, request.getImageUrls()); + + if (clubRecruitment.getId() == null) { + clubRecruitmentRepository.save(clubRecruitment); + } + } + + private void addImages(ClubRecruitment clubRecruitment, List imageUrls) { for (int index = 0; index < imageUrls.size(); index++) { ClubRecruitmentImage image = ClubRecruitmentImage.of( imageUrls.get(index), @@ -78,9 +83,5 @@ public void upsertRecruitment(Integer clubId, Integer userId, ClubRecruitmentUps ); clubRecruitment.addImage(image); } - - if (clubRecruitment.getId() == null) { - clubRecruitmentRepository.save(clubRecruitment); - } } } diff --git a/src/main/resources/db/migration/V25__drop_club_fee_deadline_column.sql b/src/main/resources/db/migration/V25__drop_club_fee_deadline_column.sql new file mode 100644 index 000000000..5e6e9f534 --- /dev/null +++ b/src/main/resources/db/migration/V25__drop_club_fee_deadline_column.sql @@ -0,0 +1,13 @@ +-- 회비 납부 기한 컬럼 삭제 +ALTER TABLE club DROP COLUMN fee_deadline; + +-- 회비 납부 필요 여부를 club 테이블로 이동 +ALTER TABLE club ADD COLUMN is_fee_required TINYINT(1) NULL; + +-- 기존 데이터 마이그레이션 (club_recruitment -> club) +UPDATE club c + INNER JOIN club_recruitment cr ON c.id = cr.club_id +SET c.is_fee_required = cr.is_fee_required; + +-- club_recruitment에서 is_fee_required 컬럼 삭제 +ALTER TABLE club_recruitment DROP COLUMN is_fee_required; diff --git a/src/main/resources/db/migration/V27__add_fee_deadline b/src/main/resources/db/migration/V27__add_fee_deadline deleted file mode 100644 index c883b1873..000000000 --- a/src/main/resources/db/migration/V27__add_fee_deadline +++ /dev/null @@ -1,9 +0,0 @@ -ALTER TABLE club_recruitment ADD COLUMN is_fee_required TINYINT(1) NULL; - -UPDATE club_recruitment cr - INNER JOIN club c ON cr.club_id = c.id -SET cr.is_fee_required = c.is_fee_required; - -ALTER TABLE club DROP COLUMN is_fee_required; - -ALTER TABLE club ADD COLUMN fee_deadline DATE NULL;