Skip to content

Commit 7fcf0fe

Browse files
authored
Merge pull request #64 from prgrms-web-devcourse-final-project/feature/EA3-80-study-optimal-time-vote-api
[EA3-80] feature: 스터디 최적 시간 투표 관련 엔티티 및 스웨거 작성
2 parents 17581d7 + 197277c commit 7fcf0fe

15 files changed

+535
-0
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package grep.neogul_coder.domain.timevote.controller;
2+
3+
import grep.neogul_coder.domain.timevote.dto.request.*;
4+
import grep.neogul_coder.domain.timevote.dto.response.*;
5+
import grep.neogul_coder.global.response.ApiResponse;
6+
import jakarta.validation.Valid;
7+
import java.util.List;
8+
import org.springframework.web.bind.annotation.*;
9+
10+
@RestController
11+
@RequestMapping("/api/studies/{studyId}/time-vote")
12+
public class TimeVoteController implements TimeVoteSpecification {
13+
14+
@PostMapping("/periods")
15+
public ApiResponse<TimeVotePeriodResponse> createPeriod(
16+
@PathVariable("studyId") Long studyId,
17+
@RequestBody @Valid TimeVotePeriodCreateRequest request
18+
) {
19+
return ApiResponse.success(new TimeVotePeriodResponse());
20+
}
21+
22+
@GetMapping("/periods/{periodId}/stats")
23+
public ApiResponse<List<TimeVoteStatResponse>> getVoteStats(
24+
@PathVariable("studyId") Long studyId,
25+
@PathVariable("periodId") Long periodId
26+
) {
27+
return ApiResponse.success(List.of(new TimeVoteStatResponse()));
28+
}
29+
30+
@PostMapping("/single")
31+
public ApiResponse<TimeVoteResponse> submitVote(
32+
@PathVariable("studyId") Long studyId,
33+
@RequestBody @Valid TimeVoteCreateRequest request
34+
) {
35+
return ApiResponse.success(new TimeVoteResponse());
36+
}
37+
38+
@PostMapping("/bulk")
39+
public ApiResponse<List<TimeVoteResponse>> submitVotes(
40+
@PathVariable("studyId") Long studyId,
41+
@RequestBody @Valid TimeVoteBulkCreateRequest request
42+
) {
43+
return ApiResponse.success(List.of(new TimeVoteResponse()));
44+
}
45+
46+
@PutMapping("/single")
47+
public ApiResponse<TimeVoteResponse> updateVote(
48+
@PathVariable("studyId") Long studyId,
49+
@RequestBody @Valid TimeVoteUpdateRequest request
50+
) {
51+
return ApiResponse.success(new TimeVoteResponse());
52+
}
53+
54+
@PutMapping("/bulk")
55+
public ApiResponse<List<TimeVoteResponse>> updateVotes(
56+
@PathVariable("studyId") Long studyId,
57+
@RequestBody @Valid TimeVoteBulkUpdateRequest request
58+
) {
59+
return ApiResponse.success(List.of(new TimeVoteResponse()));
60+
}
61+
62+
63+
@DeleteMapping("/single")
64+
public ApiResponse<Void> deleteVotes(
65+
@PathVariable("studyId") Long studyId,
66+
@RequestBody @Valid TimeVoteDeleteRequest request
67+
) {
68+
return ApiResponse.noContent();
69+
}
70+
71+
@DeleteMapping("/bulk")
72+
public ApiResponse<Void> deleteMultipleVotes(
73+
@PathVariable("studyId") Long studyId,
74+
@RequestBody @Valid TimeVoteBulkDeleteRequest request
75+
) {
76+
return ApiResponse.noContent();
77+
}
78+
79+
@DeleteMapping("/all")
80+
public ApiResponse<Void> deleteAllVotes(
81+
@PathVariable("studyId") Long studyId,
82+
@RequestParam("periodId") Long periodId,
83+
@RequestParam("studyMemberId") Long studyMemberId
84+
) {
85+
return ApiResponse.noContent();
86+
}
87+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package grep.neogul_coder.domain.timevote.controller;
2+
3+
import grep.neogul_coder.domain.timevote.dto.request.*;
4+
import grep.neogul_coder.domain.timevote.dto.response.*;
5+
import grep.neogul_coder.global.response.ApiResponse;
6+
import io.swagger.v3.oas.annotations.Operation;
7+
import io.swagger.v3.oas.annotations.Parameter;
8+
import io.swagger.v3.oas.annotations.tags.Tag;
9+
import jakarta.validation.Valid;
10+
import java.util.List;
11+
import org.springframework.web.bind.annotation.RequestBody;
12+
13+
@Tag(name = "Time-Vote", description = "스터디 모임 최적 시간 조율 API")
14+
public interface TimeVoteSpecification {
15+
16+
@Operation(summary = "최적 시간 투표 기간 생성", description = "팀장이 가능한 시간 요청을 생성합니다.")
17+
ApiResponse<TimeVotePeriodResponse> createPeriod(
18+
@Parameter(description = "스터디 ID", example = "1") Long studyId,
19+
@RequestBody @Valid TimeVotePeriodCreateRequest request
20+
);
21+
22+
@Operation(summary = "사용자 특정 가능 시간대 제출", description = "스터디 멤버가 단일 가능 시간을 제출합니다.")
23+
ApiResponse<TimeVoteResponse> submitVote(
24+
@Parameter(description = "스터디 ID", example = "1") Long studyId,
25+
@RequestBody @Valid TimeVoteCreateRequest request
26+
);
27+
28+
@Operation(summary = "사용자 여러 가능 시간대 제출", description = "스터디 멤버가 여러 가능 시간을 제출합니다.")
29+
ApiResponse<List<TimeVoteResponse>> submitVotes(
30+
@Parameter(description = "스터디 ID", example = "1") Long studyId,
31+
@RequestBody @Valid TimeVoteBulkCreateRequest request
32+
);
33+
34+
@Operation(summary = "사용자 특정 시간대 수정", description = "사용자가 기존 제출한 시간 중 하나를 수정합니다.")
35+
ApiResponse<TimeVoteResponse> updateVote(
36+
@Parameter(description = "스터디 ID") Long studyId,
37+
@RequestBody @Valid TimeVoteUpdateRequest request
38+
);
39+
40+
@Operation(summary = "사용자 여러 시간대 수정", description = "사용자가 제출한 여러 시간대를 수정합니다.")
41+
ApiResponse<List<TimeVoteResponse>> updateVotes(
42+
@Parameter(description = "스터디 ID") Long studyId,
43+
@RequestBody @Valid TimeVoteBulkUpdateRequest request
44+
);
45+
46+
@Operation(summary = "사용자 특정 시간대 삭제", description = "사용자가 특정 시간대만 삭제합니다.")
47+
ApiResponse<Void> deleteVotes(
48+
@Parameter(description = "스터디 ID") Long studyId,
49+
@RequestBody @Valid TimeVoteDeleteRequest request
50+
);
51+
52+
@Operation(summary = "사용자 여러 시간대 삭제", description = "사용자가 선택한 여러 시간대를 삭제합니다.")
53+
ApiResponse<Void> deleteMultipleVotes(
54+
@Parameter(description = "스터디 ID") Long studyId,
55+
@RequestBody @Valid TimeVoteBulkDeleteRequest request
56+
);
57+
58+
@Operation(summary = "사용자 전체 시간 삭제", description = "사용자가 제출한 시간 전체를 삭제합니다.")
59+
ApiResponse<Void> deleteAllVotes(
60+
@Parameter(description = "스터디 ID") Long studyId,
61+
@Parameter(description = "투표 기간 ID") Long periodId,
62+
@Parameter(description = "스터디 멤버 ID") Long studyMemberId
63+
);
64+
65+
@Operation(summary = "투표 통계 조회", description = "특정 투표 기간의 시간대별 통계 정보를 조회합니다.")
66+
ApiResponse<List<TimeVoteStatResponse>> getVoteStats(
67+
@Parameter(description = "스터디 ID", example = "1") Long studyId,
68+
@Parameter(description = "투표 기간 ID", example = "5") Long periodId
69+
);
70+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package grep.neogul_coder.domain.timevote.dto.request;
2+
3+
import io.swagger.v3.oas.annotations.media.ArraySchema;
4+
import io.swagger.v3.oas.annotations.media.Schema;
5+
import jakarta.validation.Valid;
6+
import jakarta.validation.constraints.NotEmpty;
7+
import jakarta.validation.constraints.NotNull;
8+
import java.time.LocalDateTime;
9+
import java.util.List;
10+
import lombok.Getter;
11+
12+
@Getter
13+
@Schema(description = "스터디 모임 일정 조율 - 여러 가능 시간 제출 요청 DTO")
14+
public class TimeVoteBulkCreateRequest {
15+
16+
@NotNull
17+
@Schema(description = "기간 ID", example = "5")
18+
private Long periodId;
19+
20+
@NotNull
21+
@Schema(description = "스터디 멤버 ID", example = "12")
22+
private Long studyMemberId;
23+
24+
@Valid
25+
@NotEmpty
26+
@ArraySchema(arraySchema = @Schema(description = "제출할 가능 시간 목록",
27+
example = "[{\"startTime\":\"2025-07-16T10:00:00\",\"endTime\":\"2025-07-16T12:00:00\"}, {\"startTime\":\"2025-07-17T13:00:00\",\"endTime\":\"2025-07-17T15:00:00\"}]"))
28+
private List<AvailableTimeDto> availableTimes;
29+
30+
@Getter
31+
@Schema(description = "가능 시간 블록 DTO")
32+
public static class AvailableTimeDto {
33+
34+
@NotNull
35+
@Schema(description = "가능 시작 시간", example = "2025-07-16T10:00:00")
36+
private LocalDateTime startTime;
37+
38+
@NotNull
39+
@Schema(description = "가능 종료 시간", example = "2025-07-16T12:00:00")
40+
private LocalDateTime endTime;
41+
}
42+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package grep.neogul_coder.domain.timevote.dto.request;
2+
3+
import io.swagger.v3.oas.annotations.media.ArraySchema;
4+
import io.swagger.v3.oas.annotations.media.Schema;
5+
import jakarta.validation.Valid;
6+
import jakarta.validation.constraints.NotEmpty;
7+
import java.time.LocalDateTime;
8+
import java.util.List;
9+
import lombok.Getter;
10+
11+
@Getter
12+
@Schema(description = "스터디 모임 일정 조율 - 여러 시간 삭제 요청 DTO")
13+
public class TimeVoteBulkDeleteRequest {
14+
15+
@Schema(description = "스터디 멤버 ID", example = "12")
16+
private Long studyMemberId;
17+
18+
@Schema(description = "투표 기간 ID", example = "5")
19+
private Long periodId;
20+
21+
@Valid
22+
@NotEmpty
23+
@ArraySchema(arraySchema = @Schema(description = "삭제할 시간대 목록",
24+
example = "[{\"startTime\":\"2025-07-16T10:00:00\",\"endTime\":\"2025-07-16T12:00:00\"}," +
25+
"{\"startTime\":\"2025-07-17T13:00:00\",\"endTime\":\"2025-07-17T15:00:00\"}]"))
26+
private List<TimeRange> timeRanges;
27+
28+
@Getter
29+
@Schema(description = "시간 범위")
30+
public static class TimeRange {
31+
@Schema(description = "시작 시간", example = "2025-07-16T10:00:00")
32+
private LocalDateTime startTime;
33+
34+
@Schema(description = "종료 시간", example = "2025-07-16T13:00:00")
35+
private LocalDateTime endTime;
36+
}
37+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package grep.neogul_coder.domain.timevote.dto.request;
2+
3+
import io.swagger.v3.oas.annotations.media.ArraySchema;
4+
import io.swagger.v3.oas.annotations.media.Schema;
5+
import jakarta.validation.Valid;
6+
import jakarta.validation.constraints.NotEmpty;
7+
import jakarta.validation.constraints.NotNull;
8+
import java.time.LocalDateTime;
9+
import java.util.List;
10+
import lombok.Getter;
11+
12+
@Getter
13+
@Schema(description = "스터디 모임 일정 조율 - 여러 가능 시간 수정 요청 DTO")
14+
public class TimeVoteBulkUpdateRequest {
15+
16+
@Schema(description = "투표 기간 ID", example = "5")
17+
private Long periodId;
18+
19+
@Schema(description = "스터디 멤버 ID", example = "12")
20+
private Long studyMemberId;
21+
22+
@Valid
23+
@NotEmpty
24+
@ArraySchema(arraySchema = @Schema(description = "수정할 가능 시간 목록",
25+
example = "[{\"voteId\":100,\"startTime\":\"2025-07-16T10:00:00\",\"endTime\":\"2025-07-16T12:00:00\"}, {\"voteId\":101,\"startTime\":\"2025-07-17T13:00:00\",\"endTime\":\"2025-07-17T15:00:00\"}]"))
26+
private List<VoteUpdateBlock> voteBlocks;
27+
28+
@Getter
29+
@Schema(description = "수정할 시간 블록")
30+
public static class VoteUpdateBlock {
31+
@NotNull
32+
@Schema(description = "투표 ID", example = "100")
33+
private Long voteId;
34+
35+
@NotNull
36+
@Schema(description = "시작 시간", example = "2025-07-16T16:00:00")
37+
private LocalDateTime startTime;
38+
39+
@NotNull
40+
@Schema(description = "종료 시간", example = "2025-07-16T19:00:00")
41+
private LocalDateTime endTime;
42+
}
43+
}
44+
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package grep.neogul_coder.domain.timevote.dto.request;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import jakarta.validation.constraints.NotNull;
5+
import java.time.LocalDateTime;
6+
import lombok.Getter;
7+
8+
@Getter
9+
@Schema(description = "스터디 모임 일정 조율 - 단일 가능 시간 제출 요청 DTO")
10+
public class TimeVoteCreateRequest {
11+
12+
@NotNull
13+
@Schema(description = "기간 ID", example = "5")
14+
private Long periodId;
15+
16+
@NotNull
17+
@Schema(description = "스터디 멤버 ID", example = "12")
18+
private Long studyMemberId;
19+
20+
@NotNull
21+
@Schema(description = "시작 시간", example = "2025-07-16T10:00:00")
22+
private LocalDateTime startTime;
23+
24+
@NotNull
25+
@Schema(description = "종료 시간", example = "2025-07-16T13:00:00")
26+
private LocalDateTime endTime;
27+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package grep.neogul_coder.domain.timevote.dto.request;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import java.time.LocalDateTime;
5+
import lombok.Getter;
6+
7+
@Getter
8+
@Schema(description = "스터디 모임 일정 조율 - 단일 시간 삭제 요청 DTO")
9+
public class TimeVoteDeleteRequest {
10+
11+
@Schema(description = "투표 기간 ID", example = "5")
12+
private Long periodId;
13+
14+
@Schema(description = "스터디 멤버 ID", example = "12")
15+
private Long studyMemberId;
16+
17+
@Schema(description = "삭제할 시작 시간", example = "2025-07-16T10:00:00")
18+
private LocalDateTime startTime;
19+
20+
@Schema(description = "삭제할 종료 시간", example = "2025-07-16T13:00:00")
21+
private LocalDateTime endTime;
22+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package grep.neogul_coder.domain.timevote.dto.request;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import jakarta.validation.constraints.NotNull;
5+
import java.time.LocalDateTime;
6+
import lombok.Getter;
7+
8+
@Getter
9+
@Schema(description = "스터디 모임 일정 조율 - 팀장이 가능 시간 요청을 생성할 때 사용하는 요청 DTO")
10+
public class TimeVotePeriodCreateRequest {
11+
12+
@NotNull
13+
@Schema(description = "스터디 ID", example = "1")
14+
private Long studyId;
15+
16+
@NotNull
17+
@Schema(description = "시작일", example = "2025-07-13T00:00:00")
18+
private LocalDateTime startDate;
19+
20+
@NotNull
21+
@Schema(description = "종료일", example = "2025-07-20T23:59:59")
22+
private LocalDateTime endDate;
23+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package grep.neogul_coder.domain.timevote.dto.request;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import jakarta.validation.constraints.NotNull;
5+
import java.time.LocalDateTime;
6+
import lombok.Getter;
7+
8+
@Getter
9+
@Schema(description = "스터디 모임 일정 조율 - 단일 가능 시간 수정 요청 DTO")
10+
public class TimeVoteUpdateRequest {
11+
12+
@NotNull
13+
@Schema(description = "투표 ID", example = "100")
14+
private Long voteId;
15+
16+
@NotNull
17+
@Schema(description = "변경할 시작 시간", example = "2025-07-16T15:00:00")
18+
private LocalDateTime startTime;
19+
20+
@NotNull
21+
@Schema(description = "변경할 종료 시간", example = "2025-07-16T17:00:00")
22+
private LocalDateTime endTime;
23+
24+
}

0 commit comments

Comments
 (0)