Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
3448c92
fix: 로그인 ID 하이픈을 이모지로 오인하는 검증 수정 (#2383)
taejinn Sep 2, 2026
276ec50
fix: DIRECT 채팅방 생성을 동시성 안전한 get-or-create로 수정 (#2384)
taejinn Sep 2, 2026
170c414
fix: 개인 채팅 CTA와 DIRECT 생성 조건을 동일 정책으로 정렬 (#2385)
taejinn Sep 2, 2026
c3b41de
fix: TEAM 채팅방 인원 상한을 실제 멤버 기준으로 계산 (#2386)
taejinn Sep 2, 2026
20dabeb
fix: make team recruitment notification actions idempotent (#2387)
taejinn Sep 2, 2026
b78fa4a
fix: 팀원 모집 조회 응답의 d_day 계산 계약을 공통화 (#2388)
taejinn Sep 2, 2026
199149c
fix: 팀원 모집글 연속 생성 방지 (#2389)
taejinn Sep 2, 2026
2004995
fix: 팀원 모집 마감 배치 실패를 모집 단위로 격리 (#2394)
taejinn Sep 2, 2026
0bb7977
fix: enforce upload URL request metadata (#2395)
taejinn Sep 2, 2026
e447c44
fix: OpenAPI 스키마 dialect와 참조 정합성 보장 (#2404)
taejinn Sep 2, 2026
e77f271
fix: OpenAPI 응답 status와 no-content 정규화 (#2390)
taejinn Sep 2, 2026
cefcda7
docs: 팀원 모집 Java Swagger와 공개 API 계약 동기화 (#2391)
taejinn Sep 2, 2026
814d457
test: add team recruitment chat HTTP acceptance tests (#2392)
taejinn Sep 2, 2026
f19c254
test: 팀원 모집 지원 동시성 검증 (#2393)
taejinn Sep 2, 2026
67422e6
test: add team recruitment notification HTTP acceptance tests (#2405)
taejinn Sep 2, 2026
3175139
test: ROLE_BASED 지원 여정 검증 (#2406)
taejinn Sep 2, 2026
fda3ad8
feat: 팀원 모집 채팅방 목록 조회 API 구현 (#2408)
taejinn Sep 3, 2026
488cb6a
fix: 닉네임 미설정 사용자의 채팅방 조회 및 메시지 전송 버그 수정
dnjswldnd-3513 Sep 8, 2026
b1f9ce6
test: 닉네임 미설정 사용자의 채팅방 조회 및 메시지 전송 단위 테스트 추가
dnjswldnd-3513 Sep 8, 2026
e24308c
merge: resolve conflict with origin/develop
dnjswldnd-3513 Sep 8, 2026
b710fff
Merge pull request #2413 from BCSDLab/fix/2411-nickname-null-fallback
dnjswldnd-3513 Sep 8, 2026
8222a7f
fix: 팀원 모집 지원 마감일의 활동 시작일 제한 제거
insik03 Sep 8, 2026
ee04a63
Merge pull request #2415 from BCSDLab/fix/team-recruitment-deadline-d…
insik03 Sep 8, 2026
1987595
fix: 채팅방 목록 조회 시 닉네임 미설정 상대방 nickname null 응답 수정
dnjswldnd-3513 Sep 9, 2026
50514cb
fix: 채팅 메시지 알림 outbox 직렬화 실패 시 KOIN 표준 예외 미사용 수정
dnjswldnd-3513 Sep 9, 2026
2aa4cf6
Merge pull request #2419 from BCSDLab/fix/2417-chat-outbox-illegal-st…
dnjswldnd-3513 Sep 9, 2026
fa5fe33
Merge branch 'develop' into fix/2416-chat-room-counterpart-nickname-null
dnjswldnd-3513 Sep 9, 2026
68a8c3d
Merge pull request #2418 from BCSDLab/fix/2416-chat-room-counterpart-…
dnjswldnd-3513 Sep 9, 2026
c3a242d
fix: 팀원 모집 마감 시 채팅방 읽기 전용 전환 제거
dnjswldnd-3513 Sep 9, 2026
509471a
test: 마감 시 채팅방 save 미호출 검증 추가 및 테스트 이름 정리
dnjswldnd-3513 Sep 9, 2026
c011d61
test: 마감 처리 후 사용되지 않는 chatRoomRepository stub 제거
dnjswldnd-3513 Sep 9, 2026
3e97e28
test: 수동 마감 후 채팅방 ACTIVE 유지에 따른 DIRECT 생성 가능 테스트 수정
dnjswldnd-3513 Sep 9, 2026
3d14db3
Merge pull request #2421 from BCSDLab/fix/2420-team-recruitment-chat-…
dnjswldnd-3513 Sep 9, 2026
8d556c1
fix: 주문 이력이 있는 회원 탈퇴 시 외래 키 오류 수정 (#2423)
taejinn Sep 11, 2026
d343aca
fix: 셔틀 잔여 시간 정렬 중 null 도착 시각 NPE 방지 (#2410)
taejinn Sep 11, 2026
b02935d
refactor: TeamRecruitmentChatRoom READ_ONLY 상태 완전 제거
dnjswldnd-3513 Sep 12, 2026
a6495a8
refactor: TeamRecruitmentChatRoom READ_ONLY 상태 완전 제거
dnjswldnd-3513 Sep 12, 2026
ba9b0e7
fix: getStatus() 불필요한 stub 및 status DTO 필드 assertion 제거
dnjswldnd-3513 Sep 12, 2026
c6cd7cf
Merge pull request #2427 from BCSDLab/refactor/2426-remove-chat-room-…
dnjswldnd-3513 Sep 12, 2026
989f1de
fix: 팀원 모집 프로필 활동 설명 글자수 제한을 500자에서 1000자로 확장
insik03 Sep 12, 2026
c03ea01
Merge pull request #2429 from BCSDLab/fix/2428-activity-description-l…
insik03 Sep 12, 2026
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
Expand Up @@ -72,6 +72,7 @@ public List<BusRemainTime> getShuttleBusRemainTimes(BusType busType, BusStation
.filter(route -> route.isRunning(clock))
.filter(route -> route.isCorrectRoute(depart, arrival, clock))
.map(route -> route.getRemainTime(depart))
.filter(remainTime -> remainTime.getBusArrivalTime() != null)
.distinct()
.sorted()
.toList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
import static in.koreatech.koin.global.code.ApiResponseCode.NOT_FOUND_USER;
import static in.koreatech.koin.global.code.ApiResponseCode.NO_CONTENT;
import static in.koreatech.koin.global.code.ApiResponseCode.OK;
import static in.koreatech.koin.global.code.ApiResponseCode.REQUEST_TOO_FAST;
import static in.koreatech.koin.global.code.ApiResponseCode.TEAM_RECRUITMENT_CLOSED;
import static in.koreatech.koin.global.code.ApiResponseCode.TEAM_RECRUITMENT_FORBIDDEN;
import static in.koreatech.koin.global.code.ApiResponseCode.TEAM_RECRUITMENT_INVALID_DEADLINE_DATE;
import static in.koreatech.koin.global.code.ApiResponseCode.TEAM_RECRUITMENT_INVALID_ROLE_COMPOSITION;
import static in.koreatech.koin.global.code.ApiResponseCode.TEAM_RECRUITMENT_MAX_PARTICIPANTS_BELOW_ACCEPTED;
import static in.koreatech.koin.global.code.ApiResponseCode.TEAM_RECRUITMENT_NOT_FOUND;
Expand Down Expand Up @@ -89,23 +89,27 @@ ResponseEntity<RecruitmentListResponse> getRecruitments(

@ApiResponseCodes({
CREATED,
TEAM_RECRUITMENT_INVALID_DEADLINE_DATE,
TEAM_RECRUITMENT_INVALID_ROLE_COMPOSITION,
INVALID_START_DATE_AFTER_END_DATE,
INVALID_REQUEST_BODY,
NOT_FOUND_USER,
UNAUTHORIZED_USER,
FORBIDDEN_USER_TYPE,
REQUEST_TOO_FAST,
})
@Operation(summary = "모집글 작성", description = """
### 모집글 작성
- `recruitment_type=ROLE_BASED`은 `roles`를 1~5개 보내며 전체 정원은 역할 정원의 합으로 계산됩니다.
- 전체 정원은 10명을 넘을 수 없습니다. 역할별 정원의 합도 같은 상한을 지켜야 합니다.
- `recruitment_type=ROLE_BASED`은 `roles`를 1~5개 보내며 작성자를 제외한 지원자 모집 정원은
역할별 지원자 모집 정원의 합으로 계산됩니다.
- 작성자를 제외한 지원자 모집 정원은 10명을 넘을 수 없습니다.
역할별 지원자 모집 정원의 합도 같은 상한을 지켜야 합니다.
- 역할명은 앞뒤 공백을 제거해 저장하며, 대소문자와 악센트만 다른 이름도 중복으로 봅니다.
- `recruitment_type=GENERAL`은 `max_participants`를 보내고 `roles`는 빈 배열로 보내셔야 합니다.
- 지원 마감일은 활동 시작일 이하, 활동 시작일은 활동 종료일 이하여야 합니다.
- `recruitment_type=GENERAL`은 작성자를 제외한 지원자 모집 정원인 `max_participants`를 보내고
`roles`는 빈 배열로 보내셔야 합니다.
- 활동 시작일은 활동 종료일 이하여야 합니다. 지원 마감일은 활동 기간과의 순서 제한이 없습니다.
- 모집글과 TEAM 채팅방을 같은 트랜잭션에서 생성하고 작성자를 최초 채팅방 멤버로 추가합니다.
- 별도의 팀 채팅방 생성 API는 없습니다.
- 같은 사용자가 동일한 요청을 300ms 안에 반복하면 두 번째 요청은 `409 REQUEST_TOO_FAST`를 반환합니다.
- 생성된 모집글 id만 반환합니다.
""")
@PostMapping
Expand All @@ -123,7 +127,8 @@ ResponseEntity<IdResponse> createRecruitment(
- 비로그인 조회가 가능합니다.
- 로그인한 경우 `is_author`, `can_apply`, `apply_block_reason`, `application`,
`can_manage_applicants`, 팀 채팅방 정보를 함께 반환합니다.
- `d_day`는 지원 마감일 기준이며 마감일이 지나면 null입니다.
- `d_day`는 KST 기준 현재 날짜와 지원 마감일의 차이입니다.
- 유효 모집 상태가 `RECRUITING`이고 마감일이 오늘 또는 미래일 때만 반환하며, `CLOSED`/`DELETED`, null 마감일 또는 마감일 경과 시 `null`입니다.
- 삭제된 모집글은 404를 반환합니다.
""")
@GetMapping("/{recruitmentId}")
Expand All @@ -141,7 +146,6 @@ ResponseEntity<RecruitmentDetail> getRecruitment(
TEAM_RECRUITMENT_ROLE_UPDATE_NOT_ALLOWED,
TEAM_RECRUITMENT_MAX_PARTICIPANTS_BELOW_ACCEPTED,
TEAM_RECRUITMENT_TYPE_CHANGE_NOT_ALLOWED,
TEAM_RECRUITMENT_INVALID_DEADLINE_DATE,
TEAM_RECRUITMENT_INVALID_ROLE_COMPOSITION,
INVALID_START_DATE_AFTER_END_DATE,
INVALID_REQUEST_BODY,
Expand All @@ -152,11 +156,13 @@ ResponseEntity<RecruitmentDetail> getRecruitment(
### 모집글 수정
- 작성자만 수정할 수 있습니다.
- 기존 역할은 `id`를 반드시 보내고 새 역할은 `id`를 생략하셔야 합니다.
- 지원자가 있는 역할은 삭제, 이름 변경, 정원 축소를 할 수 없습니다.
- 지원자가 있는 역할은 삭제, 이름 변경, 지원자 정원 축소를 할 수 없습니다.
- 기존 역할의 표시 순서는 유지되며, 새 역할은 기존 역할 뒤에 요청 배열 순서대로 추가됩니다.
- 전체 정원은 10명을 넘을 수 없습니다.
- `recruitment_type=GENERAL`의 `max_participants`는 작성자를 제외한 지원자 모집 정원입니다.
- `recruitment_type=ROLE_BASED`의 지원자 모집 정원은 역할별 지원자 모집 정원의 합으로 계산됩니다.
- 작성자를 제외한 지원자 모집 정원은 10명을 넘을 수 없습니다.
- 역할명은 앞뒤 공백을 제거해 저장하며, 대소문자와 악센트만 다른 이름도 중복으로 봅니다.
- 정원을 이미 승인된 인원과 같게 줄이면 그 자리에서 마감됩니다.
- 지원자 정원을 이미 승인된 인원과 같게 줄이면 그 자리에서 마감됩니다.
- 마감된 모집글은 수정할 수 없습니다.
""")
@PutMapping("/{recruitmentId}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ public record ApplicantDetail(
@Schema(description = "현재 사용자가 승인/거절할 수 있는지", example = "true", requiredMode = REQUIRED)
Boolean canDecide,

@Schema(description = "개인 채팅을 시작할 수 있는지", example = "false", requiredMode = REQUIRED)
@Schema(
description = "기존 DIRECT 방이 있거나, 마감일이 지나지 않은 RECRUITING 상태이거나, "
+ "정원 충족으로 마감되어 ACTIVE TEAM 방이 있을 때 개인 채팅을 시작할 수 있는지",
example = "false",
requiredMode = REQUIRED
)
Boolean canOpenDirectChat
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ public record ApplicantRecruitment(
@JsonFormat(pattern = "yyyy-MM-dd")
LocalDate deadlineDate,

@Schema(description = "D-day", example = "8", nullable = true, requiredMode = REQUIRED)
@Schema(description = "D-day. 모집 상태가 RECRUITING이고 마감일이 오늘 또는 미래일 때만 반환되며, CLOSED/DELETED, null 마감일 또는 마감일 경과 시 null입니다.",
example = "8", nullable = true, requiredMode = REQUIRED)
Integer dDay,

@Schema(description = "모집 상태", example = "RECRUITING", requiredMode = REQUIRED)
Expand All @@ -50,10 +51,10 @@ public record ApplicantRecruitment(
@Schema(description = "모집 유형", example = "ROLE_BASED", requiredMode = REQUIRED)
TeamRecruitmentType recruitmentType,

@Schema(description = "승인된 전체 지원자 수", example = "2", requiredMode = REQUIRED)
@Schema(description = "작성자를 제외한 승인된 전체 지원자 수", example = "2", requiredMode = REQUIRED)
Integer currentParticipants,

@Schema(description = "전체 모집 정원", example = "5", requiredMode = REQUIRED)
@Schema(description = "작성자를 제외한 전체 모집 정원", example = "5", requiredMode = REQUIRED)
Integer maxParticipants,

@Schema(description = "역할 목록", requiredMode = REQUIRED)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@ public record ApplicantSummary(
@Schema(description = "지원 상태", example = "PENDING", requiredMode = REQUIRED)
TeamRecruitmentApplicationStatus status,

@Schema(description = "개인 채팅 시작 가능 여부", example = "false", requiredMode = REQUIRED)
@Schema(
description = "기존 DIRECT 방이 있거나, 마감일이 지나지 않은 RECRUITING 상태이거나, "
+ "정원 충족으로 마감되어 ACTIVE TEAM 방이 있을 때 개인 채팅 시작 가능 여부",
example = "false",
requiredMode = REQUIRED
)
Boolean canOpenDirectChat
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import io.swagger.v3.oas.annotations.media.Schema;

@JsonNaming(SnakeCaseStrategy.class)
@Schema(description = "지원자가 선택한 역할")
public record ApplicationRole(
@Schema(description = "역할 ID", example = "1", requiredMode = REQUIRED)
Integer id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public record CreateRecruitmentRequest(
@NotNull
LocalDate activityEndDate,

@Schema(description = "지원 마감일. 활동 시작일 이하여야 합니다.", example = "2026-09-03", requiredMode = REQUIRED)
@Schema(description = "지원 마감일", example = "2026-09-03", requiredMode = REQUIRED)
@JsonFormat(pattern = "yyyy-MM-dd")
@NotNull
LocalDate deadlineDate,
Expand All @@ -57,12 +57,13 @@ public record CreateRecruitmentRequest(
@NotNull
TeamRecruitmentType recruitmentType,

@Schema(description = "전체 모집 정원. GENERAL 모집만 사용하며 ROLE_BASED 모집은 역할 정원의 합으로 계산됩니다.",
@Schema(description = "작성자를 제외한 지원자 모집 정원. GENERAL 모집만 사용하며 "
+ "ROLE_BASED 모집은 역할별 지원자 모집 정원의 합으로 계산됩니다.",
example = "5", nullable = true, requiredMode = NOT_REQUIRED)
Integer maxParticipants,

@Schema(description = "역할 목록. ROLE_BASED 모집은 1~5개이며 역할별 정원의 합은 최대 10명입니다. "
+ "GENERAL 모집은 빈 배열입니다.", requiredMode = REQUIRED)
@Schema(description = "역할 목록. ROLE_BASED 모집은 1~5개이며 역할별 지원자 모집 정원의 합은 최대 10명입니다. "
+ "작성자는 지원자 모집 정원에 포함되지 않습니다. GENERAL 모집은 빈 배열입니다.", requiredMode = REQUIRED)
@NotNull
List<@NotNull @Valid RoleInput> roles,

Expand All @@ -81,7 +82,7 @@ public record CreateRecruitmentRequest(
String qualification
) {
public CreateRecruitmentRequest {
RecruitmentRequestValidator.validatePeriod(activityStartDate, activityEndDate, deadlineDate);
RecruitmentRequestValidator.validateActivityPeriod(activityStartDate, activityEndDate);
RecruitmentRequestValidator.validateRoleComposition(recruitmentType, roles, maxParticipants);
if (RecruitmentRequestValidator.isCompleteRoleList(roles, RoleInput::isComplete)) {
RecruitmentRequestValidator.validateDistinctRoleNames(roles.stream().map(RoleInput::name).toList());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ public record CreatedRecruitment(
@JsonFormat(pattern = "yyyy-MM-dd")
LocalDate deadlineDate,

@Schema(description = "D-day, 모집 마감 후 null", example = "8", nullable = true, requiredMode = REQUIRED)
@Schema(description = "D-day. 모집 상태가 RECRUITING이고 마감일이 오늘 또는 미래일 때만 반환되며, CLOSED/DELETED, null 마감일 또는 마감일 경과 시 null입니다.",
example = "8", nullable = true, requiredMode = REQUIRED)
Integer dDay,

@Schema(description = "모집 상태", example = "RECRUITING", requiredMode = REQUIRED)
Expand All @@ -50,10 +51,10 @@ public record CreatedRecruitment(
@Schema(description = "모집 유형", example = "ROLE_BASED", requiredMode = REQUIRED)
TeamRecruitmentType recruitmentType,

@Schema(description = "승인된 전체 지원자 수", example = "2", requiredMode = REQUIRED)
@Schema(description = "작성자를 제외한 승인된 전체 지원자 수", example = "2", requiredMode = REQUIRED)
Integer currentParticipants,

@Schema(description = "전체 모집 정원", example = "5", requiredMode = REQUIRED)
@Schema(description = "작성자를 제외한 전체 모집 정원", example = "5", requiredMode = REQUIRED)
Integer maxParticipants,

@Schema(description = "역할 목록", requiredMode = REQUIRED)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public record ProfileActivityInput(

@Schema(description = "활동 설명", example = "기획 담당", requiredMode = REQUIRED)
@NotBlank
@Size(min = 1, max = 500)
@Size(min = 1, max = 1000)
String description
) {
public ProfileActivityInput {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ public record RecruitmentCard(
@JsonFormat(pattern = "yyyy-MM-dd")
LocalDate deadlineDate,

@Schema(description = "D-day, 모집 마감 후 null", example = "8", nullable = true, requiredMode = REQUIRED)
@Schema(description = "D-day. 모집 상태가 RECRUITING이고 마감일이 오늘 또는 미래일 때만 반환되며, CLOSED/DELETED, null 마감일 또는 마감일 경과 시 null입니다.",
example = "8", nullable = true, requiredMode = REQUIRED)
Integer dDay,

@Schema(description = "모집 상태", example = "RECRUITING", requiredMode = REQUIRED)
Expand All @@ -50,10 +51,10 @@ public record RecruitmentCard(
@Schema(description = "모집 유형", example = "ROLE_BASED", requiredMode = REQUIRED)
TeamRecruitmentType recruitmentType,

@Schema(description = "승인된 전체 지원자 수", example = "2", requiredMode = REQUIRED)
@Schema(description = "작성자를 제외한 승인된 전체 지원자 수", example = "2", requiredMode = REQUIRED)
Integer currentParticipants,

@Schema(description = "전체 모집 정원", example = "5", requiredMode = REQUIRED)
@Schema(description = "작성자를 제외한 전체 모집 정원", example = "5", requiredMode = REQUIRED)
Integer maxParticipants,

@Schema(description = "역할 목록", requiredMode = REQUIRED)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;

import in.koreatech.koin.domain.team.recruitment.enums.TeamRecruitmentStatus;
import in.koreatech.koin.domain.team.recruitment.model.TeamRecruitment;
import in.koreatech.koin.domain.team.recruitment.model.TeamRecruitmentRole;

Expand All @@ -24,7 +25,7 @@ public static RecruitmentCard of(TeamRecruitment recruitment, LocalDate today) {
recruitment.getActivityStartDate(),
recruitment.getActivityEndDate(),
recruitment.getDeadlineDate(),
dDayOf(recruitment.getDeadlineDate(), today),
dDayOf(recruitment.getStatus(), recruitment.getDeadlineDate(), today),
recruitment.getStatus(),
recruitment.getRecruitmentType(),
recruitment.getCurrentParticipants(),
Expand All @@ -47,10 +48,17 @@ private static boolean isRecruitmentClosed(TeamRecruitment recruitment, LocalDat
}

/**
* 지원 마감일이 지나면 null 이다.
* 모집 상태가 RECRUITING이고 지원 마감일이 지나지 않았을 때만 D-day를 반환한다.
*/
public static Integer dDayOf(LocalDate deadlineDate, LocalDate today) {
if (deadlineDate == null || today == null || today.isAfter(deadlineDate)) {
public static Integer dDayOf(
TeamRecruitmentStatus status,
LocalDate deadlineDate,
LocalDate today
) {
if (status != TeamRecruitmentStatus.RECRUITING
|| deadlineDate == null
|| today == null
|| today.isAfter(deadlineDate)) {
return null;
}
return (int) ChronoUnit.DAYS.between(today, deadlineDate);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ public record RecruitmentDetail(
@JsonFormat(pattern = "yyyy-MM-dd")
LocalDate deadlineDate,

@Schema(description = "D-day, 모집 마감 후 null", example = "8", nullable = true, requiredMode = REQUIRED)
@Schema(description = "D-day. 모집 상태가 RECRUITING이고 마감일이 오늘 또는 미래일 때만 반환되며, CLOSED/DELETED, null 마감일 또는 마감일 경과 시 null입니다.",
example = "8", nullable = true, requiredMode = REQUIRED)
Integer dDay,

@Schema(description = "모집 상태", example = "RECRUITING", requiredMode = REQUIRED)
Expand All @@ -53,10 +54,10 @@ public record RecruitmentDetail(
@Schema(description = "모집 유형", example = "ROLE_BASED", requiredMode = REQUIRED)
TeamRecruitmentType recruitmentType,

@Schema(description = "승인된 전체 지원자 수", example = "2", requiredMode = REQUIRED)
@Schema(description = "작성자를 제외한 승인된 전체 지원자 수", example = "2", requiredMode = REQUIRED)
Integer currentParticipants,

@Schema(description = "전체 모집 정원", example = "5", requiredMode = REQUIRED)
@Schema(description = "작성자를 제외한 전체 모집 정원", example = "5", requiredMode = REQUIRED)
Integer maxParticipants,

@Schema(description = "역할 목록", requiredMode = REQUIRED)
Expand Down Expand Up @@ -84,7 +85,9 @@ public record RecruitmentDetail(
@Schema(description = "지원 가능 여부", example = "true", requiredMode = REQUIRED)
Boolean canApply,

@Schema(description = "지원 불가 사유. 지원 가능하면 null입니다.", nullable = true, requiredMode = REQUIRED)
@Schema(description = "지원 불가 사유. 지원 가능하면 null입니다. 여러 사유가 겹치면 "
+ "LOGIN_REQUIRED > OWN_RECRUITMENT > ALREADY_APPLIED > RECRUITMENT_CLOSED > DEADLINE_PASSED > "
+ "ROLE_CLOSED > PROFILE_REQUIRED 순으로 반환합니다.", nullable = true, requiredMode = REQUIRED)
TeamRecruitmentApplyBlockReason applyBlockReason,

@Schema(description = "내 지원 정보. 미지원자는 null입니다.", nullable = true, requiredMode = REQUIRED)
Expand All @@ -101,6 +104,7 @@ public record RecruitmentDetail(
Integer teamChatRoomId
) {
@JsonNaming(SnakeCaseStrategy.class)
@Schema(description = "현재 사용자의 지원 정보")
public record AppliedApplication(
@Schema(description = "지원서 ID", example = "51", requiredMode = REQUIRED)
Integer applicationId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import static in.koreatech.koin.global.code.ApiResponseCode.INVALID_REQUEST_BODY;
import static in.koreatech.koin.global.code.ApiResponseCode.INVALID_START_DATE_AFTER_END_DATE;
import static in.koreatech.koin.global.code.ApiResponseCode.TEAM_RECRUITMENT_INVALID_DEADLINE_DATE;
import static in.koreatech.koin.global.code.ApiResponseCode.TEAM_RECRUITMENT_INVALID_ROLE_COMPOSITION;

import java.text.Normalizer;
Expand Down Expand Up @@ -30,16 +29,16 @@ final class RecruitmentRequestValidator {
private RecruitmentRequestValidator() {
}

static void validatePeriod(LocalDate activityStartDate, LocalDate activityEndDate, LocalDate deadlineDate) {
if (activityStartDate == null) {
/**
* 지원 마감일은 활동 기간과 무관하게 받는다. 활동 시작 이후까지 지원을 받는 모집글이 있어 순서를 제한하지 않는다.
*/
static void validateActivityPeriod(LocalDate activityStartDate, LocalDate activityEndDate) {
if (activityStartDate == null || activityEndDate == null) {
return;
}
if (activityEndDate != null && activityEndDate.isBefore(activityStartDate)) {
if (activityEndDate.isBefore(activityStartDate)) {
throw CustomException.of(INVALID_START_DATE_AFTER_END_DATE);
}
if (deadlineDate != null && deadlineDate.isAfter(activityStartDate)) {
throw CustomException.of(TEAM_RECRUITMENT_INVALID_DEADLINE_DATE);
}
}

/**
Expand Down
Loading
Loading