Skip to content

Commit 9466c4b

Browse files
authored
Merge pull request #839 from Clubber2024/#828-feat-모집글-캘린더-연동-기능-구현
feat : 모집글 캘린더 연동 기능, 캘린더 달력 조회 api 구현
2 parents ab96359 + 4f2e7b7 commit 9466c4b

48 files changed

Lines changed: 887 additions & 482 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.clubber.ClubberServer.domain.calendar.controller;
2+
3+
import com.clubber.ClubberServer.domain.calendar.dto.GetCalendarInListResponse;
4+
import com.clubber.ClubberServer.domain.calendar.service.CalendarService;
5+
import com.clubber.ClubberServer.global.config.swagger.DisableSwaggerSecurity;
6+
import io.swagger.v3.oas.annotations.tags.Tag;
7+
import lombok.RequiredArgsConstructor;
8+
import org.springframework.web.bind.annotation.GetMapping;
9+
import org.springframework.web.bind.annotation.RequestMapping;
10+
import org.springframework.web.bind.annotation.RequestParam;
11+
import org.springframework.web.bind.annotation.RestController;
12+
13+
@RestController
14+
@RequiredArgsConstructor
15+
@RequestMapping("/api/v1/calendars")
16+
@Tag(name = "[캘린더 - 모집 일정 API]")
17+
public class CalendarController {
18+
19+
private final CalendarService calendarService;
20+
21+
@GetMapping("")
22+
@DisableSwaggerSecurity
23+
public GetCalendarInListResponse getCalendarList(@RequestParam int year,
24+
@RequestParam int month) {
25+
return calendarService.getCalendarList(year, month);
26+
}
27+
28+
}
Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,53 @@
11
package com.clubber.ClubberServer.domain.calendar.dto;
22

33
import com.clubber.ClubberServer.domain.calendar.entity.Calendar;
4+
import com.clubber.ClubberServer.domain.club.domain.Club;
45
import com.clubber.ClubberServer.domain.recruit.domain.Recruit;
56
import com.clubber.ClubberServer.domain.recruit.domain.RecruitType;
67
import com.clubber.ClubberServer.domain.user.domain.AccountRole;
78
import com.fasterxml.jackson.annotation.JsonFormat;
89
import io.swagger.v3.oas.annotations.media.Schema;
910
import jakarta.validation.constraints.NotBlank;
10-
import lombok.Builder;
11-
1211
import java.time.LocalDateTime;
12+
import lombok.Builder;
1313

1414
@Builder
1515
public record CreateCalendarRequest(
16-
@NotBlank(message = "제목을 입력해주세요")
17-
String title,
18-
@NotBlank(message = "모집 종류를 입력해주세요")
19-
RecruitType recruitType,
20-
@Schema(description = "기간 시작일", example = "2025-08-30 00:00", type = "string")
21-
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm", timezone = "Asia/Seoul")
22-
@NotBlank(message = "시작 일자를 입력해주세요")
23-
LocalDateTime startAt,
24-
@Schema(description = "기간 마감일", example = "2025-09-02 23:59", type = "string")
25-
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm", timezone = "Asia/Seoul")
26-
@NotBlank(message = "마감 일정을 입력해주세요")
27-
LocalDateTime endAt,
28-
String url
16+
@NotBlank(message = "제목을 입력해주세요")
17+
String title,
18+
@NotBlank(message = "모집 종류를 입력해주세요")
19+
RecruitType recruitType,
20+
@Schema(description = "기간 시작일", example = "2025-08-30 00:00", type = "string")
21+
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm", timezone = "Asia/Seoul")
22+
@NotBlank(message = "시작 일자를 입력해주세요")
23+
LocalDateTime startAt,
24+
@Schema(description = "기간 마감일", example = "2025-09-02 23:59", type = "string")
25+
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm", timezone = "Asia/Seoul")
26+
@NotBlank(message = "마감 일정을 입력해주세요")
27+
LocalDateTime endAt,
28+
String url
29+
2930
) {
30-
public Calendar toEntity() {
31+
32+
public Calendar toEntity(Club club) {
3133
return Calendar.builder()
32-
.title(title)
33-
.recruitType(recruitType)
34-
.startAt(startAt)
35-
.endAt(endAt)
36-
.url(url)
37-
.writerRole(AccountRole.ADMIN)
38-
.build();
34+
.title(title)
35+
.recruitType(recruitType)
36+
.startAt(startAt)
37+
.endAt(endAt)
38+
.url(url)
39+
.writerRole(AccountRole.ADMIN)
40+
.club(club)
41+
.build();
3942
}
4043

4144
public static CreateCalendarRequest from(Recruit recruit, String recruitUrl) {
4245
return CreateCalendarRequest.builder()
43-
.title(recruit.getTitle())
44-
.recruitType(recruit.getRecruitType())
45-
.startAt(recruit.getStartAt())
46-
.endAt(recruit.getEndAt())
47-
.url(recruitUrl)
48-
.build();
46+
.title(recruit.getTitle())
47+
.recruitType(recruit.getRecruitType())
48+
.startAt(recruit.getStartAt())
49+
.endAt(recruit.getEndAt())
50+
.url(recruitUrl)
51+
.build();
4952
}
5053
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.clubber.ClubberServer.domain.calendar.dto;
2+
3+
import com.clubber.ClubberServer.domain.recruit.domain.RecruitType;
4+
import io.swagger.v3.oas.annotations.media.Schema;
5+
import lombok.Getter;
6+
7+
@Getter
8+
public class GetAlwaysCalendarResponse {
9+
10+
@Schema(description = "동아리 id", example = "1")
11+
private final Long clubId;
12+
13+
@Schema(description = "동아리명", example = "클러버")
14+
private final String clubName;
15+
16+
@Schema(description = "모집 유형", example = "수시모집")
17+
private final String recruitType;
18+
19+
@Schema(description = "모집글 개수", example = "2")
20+
private final Long calendarNum;
21+
22+
public GetAlwaysCalendarResponse(Long clubId, String clubName, Long calendarNum) {
23+
this.clubId = clubId;
24+
this.clubName = clubName;
25+
this.recruitType = RecruitType.ALWAYS.getTitle();
26+
this.calendarNum = calendarNum;
27+
}
28+
29+
}

src/main/java/com/clubber/ClubberServer/domain/recruit/dto/recruitCalendar/GetCalendarInListResponse.java renamed to src/main/java/com/clubber/ClubberServer/domain/calendar/dto/GetCalendarInListResponse.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.clubber.ClubberServer.domain.recruit.dto.recruitCalendar;
1+
package com.clubber.ClubberServer.domain.calendar.dto;
22

33
import io.swagger.v3.oas.annotations.media.Schema;
44
import java.util.List;
@@ -18,17 +18,21 @@ public class GetCalendarInListResponse {
1818
@Schema(description = "모집 월", example = "2")
1919
private final int month;
2020

21-
@Schema(description = "모집글 목록")
22-
private final List<GetCalendarResponse> recruitList;
21+
@Schema(description = "정규,추가 모집 캘린더 목록")
22+
private final List<GetNonAlwaysCalendarResponse> nonAlwaysCalendars;
23+
24+
@Schema(description = "상시 모집 캘린더 목록")
25+
private final List<GetAlwaysCalendarResponse> alwaysCalendars;
2326

2427
public static GetCalendarInListResponse of(int year, int month,
25-
List<GetCalendarResponse> recruitList) {
28+
List<GetNonAlwaysCalendarResponse> nonAlwaysCalendars,
29+
List<GetAlwaysCalendarResponse> alwaysCalendars) {
2630
return GetCalendarInListResponse.builder()
2731
.year(year)
2832
.month(month)
29-
.recruitList(recruitList)
33+
.nonAlwaysCalendars(nonAlwaysCalendars)
34+
.alwaysCalendars(alwaysCalendars)
3035
.build();
31-
3236
}
3337

3438
}

src/main/java/com/clubber/ClubberServer/domain/recruit/dto/recruitCalendar/GetCalendarResponse.java renamed to src/main/java/com/clubber/ClubberServer/domain/calendar/dto/GetNonAlwaysCalendarResponse.java

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
package com.clubber.ClubberServer.domain.recruit.dto.recruitCalendar;
1+
package com.clubber.ClubberServer.domain.calendar.dto;
22

3-
import com.clubber.ClubberServer.domain.recruit.domain.Recruit;
3+
import com.clubber.ClubberServer.domain.calendar.entity.Calendar;
44
import com.fasterxml.jackson.annotation.JsonFormat;
55
import io.swagger.v3.oas.annotations.media.Schema;
66
import java.time.LocalDateTime;
@@ -12,19 +12,16 @@
1212
@Getter
1313
@Builder(access = AccessLevel.PRIVATE)
1414
@AllArgsConstructor(access = AccessLevel.PRIVATE)
15-
public class GetCalendarResponse {
15+
public class GetNonAlwaysCalendarResponse {
1616

1717
@Schema(description = "동아리 id", example = "1")
1818
private final Long clubId;
1919

2020
@Schema(description = "동아리명", example = "클러버")
2121
private final String clubName;
2222

23-
@Schema(description = "에브리타임 url", example = "https://www.everytime.com")
24-
private final String everytimeUrl;
25-
26-
@Schema(description = "모집시기", example = "ALWAYS")
27-
private final String semester;
23+
@Schema(description = "모집 유형", example = "정규모집")
24+
private final String recruitType;
2825

2926
@Schema(description = "모집 시작 일자", example = "2025-02-05 00:00:00")
3027
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
@@ -34,15 +31,14 @@ public class GetCalendarResponse {
3431
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
3532
private final LocalDateTime endAt;
3633

37-
public static GetCalendarResponse of(Recruit recruit) {
38-
return GetCalendarResponse.builder()
39-
.clubId(recruit.getClub().getId())
40-
.clubName(recruit.getClub().getName())
41-
.everytimeUrl(recruit.getEverytimeUrl())
42-
.semester(recruit.getSemester().name())
43-
.startAt(recruit.getStartAt())
44-
.endAt(recruit.getEndAt())
34+
public static GetNonAlwaysCalendarResponse from(Calendar calendar) {
35+
return GetNonAlwaysCalendarResponse.builder()
36+
.clubId(calendar.getClub().getId())
37+
.clubName(calendar.getClub().getName())
38+
.recruitType(calendar.getRecruitType().getTitle())
39+
.startAt(calendar.getStartAt())
40+
.endAt(calendar.getEndAt())
4541
.build();
4642
}
4743

48-
}
44+
}

src/main/java/com/clubber/ClubberServer/domain/calendar/entity/Calendar.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.clubber.ClubberServer.domain.calendar.entity;
22

33
import com.clubber.ClubberServer.domain.club.domain.Club;
4+
import com.clubber.ClubberServer.domain.common.BaseEntity;
45
import com.clubber.ClubberServer.domain.recruit.domain.RecruitType;
56
import com.clubber.ClubberServer.domain.user.domain.AccountRole;
67
import jakarta.persistence.*;
@@ -17,7 +18,8 @@
1718
@Getter
1819
@Entity
1920
@NoArgsConstructor(access = AccessLevel.PROTECTED)
20-
public class Calendar {
21+
public class Calendar extends BaseEntity {
22+
2123
@Id
2224
@GeneratedValue(strategy = GenerationType.IDENTITY)
2325
private Long id;
@@ -41,36 +43,49 @@ public class Calendar {
4143
@Enumerated(EnumType.STRING)
4244
private AccountRole writerRole;
4345

44-
@ManyToOne
46+
47+
@ManyToOne(fetch = FetchType.LAZY)
4548
@JoinColumn(name = "club_id")
4649
private Club club;
4750

4851
private boolean isDeleted = false;
4952

5053
@Builder
51-
public Calendar(Long id, String title, RecruitType recruitType, String url, LocalDateTime startAt, LocalDateTime endAt, AccountRole writerRole, boolean isDeleted) {
54+
public Calendar(Long id, String title, RecruitType recruitType, String url,
55+
LocalDateTime startAt, LocalDateTime endAt, AccountRole writerRole, Club club,
56+
boolean isDeleted) {
5257
this.id = id;
5358
this.title = title;
5459
this.recruitType = recruitType;
5560
this.url = url;
5661
this.startAt = startAt;
5762
this.endAt = endAt;
5863
this.writerRole = writerRole;
64+
this.club = club;
5965
this.isDeleted = isDeleted;
6066
}
6167

6268
public void delete() {
6369
isDeleted = true;
6470
}
6571

66-
public void update(String title, RecruitType recruitType, LocalDateTime startAt, LocalDateTime endAt, String url) {
72+
public void update(String title, RecruitType recruitType, LocalDateTime startAt,
73+
LocalDateTime endAt, String url) {
6774
this.title = title;
6875
this.recruitType = recruitType;
6976
this.startAt = startAt;
7077
this.endAt = endAt;
7178
this.url = url;
7279
}
7380

81+
public void update(String title, RecruitType recruitType, LocalDateTime startAt,
82+
LocalDateTime endAt) {
83+
this.title = title;
84+
this.recruitType = recruitType;
85+
this.startAt = startAt;
86+
this.endAt = endAt;
87+
}
88+
7489
public String getStatus() {
7590
CalendarStatus status = CalendarStatus.getStatus(startAt, endAt, recruitType);
7691
return status.getTitle();

src/main/java/com/clubber/ClubberServer/domain/calendar/exception/CalendarErrorCode.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@
77

88
@AllArgsConstructor
99
public enum CalendarErrorCode implements BaseErrorCode {
10+
CALENDAR_MONTH_INVALID(HttpStatus.BAD_REQUEST.value(), "CALENDAR_400_1", "유효하지 않은 월입니다."),
11+
CALENDAR_POST_UNAUTHORIZED(HttpStatus.FORBIDDEN.value(), "CALENDAR_403_1", "캘린더 작성 권한이 없습니다."),
1012
CALENDAR_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "CALENDAR_404_1", "존재하지 않는 캘린더입니다");
1113

14+
1215
private final Integer status;
1316
private final String code;
1417
private final String reason;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.clubber.ClubberServer.domain.calendar.exception;
2+
3+
import com.clubber.ClubberServer.global.exception.BaseException;
4+
5+
public class CalendarInvalidMonthException extends BaseException {
6+
7+
public static final BaseException EXCEPTION = new CalendarInvalidMonthException();
8+
private CalendarInvalidMonthException() {
9+
super(CalendarErrorCode.CALENDAR_MONTH_INVALID);
10+
}
11+
12+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.clubber.ClubberServer.domain.calendar.exception;
2+
3+
import com.clubber.ClubberServer.global.exception.BaseException;
4+
5+
public class CalendarPostUnauthorizedException extends BaseException {
6+
7+
public static final BaseException EXCEPTION = new CalendarPostUnauthorizedException();
8+
9+
private CalendarPostUnauthorizedException() {
10+
super(CalendarErrorCode.CALENDAR_POST_UNAUTHORIZED);
11+
}
12+
13+
}

src/main/java/com/clubber/ClubberServer/domain/calendar/implement/CalendarAppender.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import com.clubber.ClubberServer.domain.calendar.dto.UpdateCalendarRequest;
44
import com.clubber.ClubberServer.domain.calendar.entity.Calendar;
55
import com.clubber.ClubberServer.domain.calendar.repository.CalendarRepository;
6+
import com.clubber.ClubberServer.domain.recruit.domain.RecruitType;
7+
import java.time.LocalDateTime;
68
import lombok.RequiredArgsConstructor;
79
import org.springframework.stereotype.Component;
810
import org.springframework.transaction.annotation.Transactional;
@@ -20,11 +22,18 @@ public Calendar append(Calendar calendar) {
2022

2123
public void update(Calendar calendar, UpdateCalendarRequest request) {
2224
calendar.update(
23-
request.title(),
24-
request.recruitType(),
25-
request.startAt(),
26-
request.endAt(),
27-
request.url()
25+
request.title(),
26+
request.recruitType(),
27+
request.startAt(),
28+
request.endAt(),
29+
request.url()
30+
);
31+
}
32+
33+
public void update(Calendar calendar, String title, RecruitType recruitType,
34+
LocalDateTime startAt, LocalDateTime endAt) {
35+
calendar.update(
36+
title, recruitType, startAt, endAt
2837
);
2938
}
3039

0 commit comments

Comments
 (0)