Skip to content

Commit b2e268f

Browse files
authored
Merge pull request #13 from Pinit-Scheduler/feat/rest-api-버전-넘버링-적용
Feat/api 버전 넘버링 적용
2 parents c672e44 + a516607 commit b2e268f

6 files changed

Lines changed: 259 additions & 0 deletions

File tree

src/main/java/me/gg/pinit/pinittask/interfaces/web/MemberController.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.springframework.web.bind.annotation.GetMapping;
1212
import org.springframework.web.bind.annotation.RestController;
1313

14+
@Deprecated
1415
@RestController
1516
public class MemberController {
1617
private final MemberService memberService;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package me.gg.pinit.pinittask.interfaces.web;
2+
3+
import io.swagger.v3.oas.annotations.Operation;
4+
import io.swagger.v3.oas.annotations.media.Content;
5+
import io.swagger.v3.oas.annotations.media.Schema;
6+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
7+
import io.swagger.v3.oas.annotations.responses.ApiResponses;
8+
import io.swagger.v3.oas.annotations.tags.Tag;
9+
import me.gg.pinit.pinittask.application.member.service.MemberService;
10+
import me.gg.pinit.pinittask.interfaces.utils.MemberId;
11+
import org.springframework.http.ResponseEntity;
12+
import org.springframework.web.bind.annotation.GetMapping;
13+
import org.springframework.web.bind.annotation.RequestMapping;
14+
import org.springframework.web.bind.annotation.RestController;
15+
16+
@RestController
17+
@RequestMapping("/v0")
18+
@Tag(name = "Member", description = "회원 관련 정보 API")
19+
public class MemberControllerV0 {
20+
private final MemberService memberService;
21+
22+
public MemberControllerV0(MemberService memberService) {
23+
this.memberService = memberService;
24+
}
25+
26+
@GetMapping("/now")
27+
@Operation(summary = "현재 진행 중인 일정 ID 조회", description = "사용자의 현재 진행 중인 일정 ID를 조회합니다.")
28+
public ResponseEntity<Long> getNowInProgressScheduleId(@MemberId Long memberId) {
29+
Long scheduleId = memberService.getNowInProgressScheduleId(memberId);
30+
return ResponseEntity.ok(scheduleId);
31+
}
32+
33+
@GetMapping("/zone-offset")
34+
@Operation(summary = "사용자 시간대 조회", description = "사용자의 시간대를 조회합니다.")
35+
@ApiResponses({
36+
@ApiResponse(responseCode = "200", description = "사용자 시간대 조회 성공", content = @Content(schema = @Schema(implementation = String.class, example = "+09:00"))),
37+
})
38+
public ResponseEntity<String> getMemberZoneOffset(@MemberId Long memberId) {
39+
String zoneOffset = memberService.findZoneOffsetOfMember(memberId).toString();
40+
return ResponseEntity.ok(zoneOffset);
41+
}
42+
}

src/main/java/me/gg/pinit/pinittask/interfaces/web/ScheduleController.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.time.ZoneId;
2727
import java.util.List;
2828

29+
@Deprecated
2930
@RestController
3031
@RequestMapping("/schedules")
3132
@RequiredArgsConstructor
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
package me.gg.pinit.pinittask.interfaces.web;
2+
3+
import io.swagger.v3.oas.annotations.Operation;
4+
import io.swagger.v3.oas.annotations.media.Content;
5+
import io.swagger.v3.oas.annotations.media.Schema;
6+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
7+
import io.swagger.v3.oas.annotations.responses.ApiResponses;
8+
import io.swagger.v3.oas.annotations.tags.Tag;
9+
import jakarta.validation.Valid;
10+
import lombok.RequiredArgsConstructor;
11+
import me.gg.pinit.pinittask.application.datetime.DateTimeUtils;
12+
import me.gg.pinit.pinittask.application.schedule.service.ScheduleAdjustmentService;
13+
import me.gg.pinit.pinittask.application.schedule.service.ScheduleService;
14+
import me.gg.pinit.pinittask.application.schedule.service.ScheduleStateChangeService;
15+
import me.gg.pinit.pinittask.domain.schedule.model.Schedule;
16+
import me.gg.pinit.pinittask.interfaces.dto.ScheduleRequest;
17+
import me.gg.pinit.pinittask.interfaces.dto.ScheduleResponse;
18+
import me.gg.pinit.pinittask.interfaces.exception.ErrorResponse;
19+
import me.gg.pinit.pinittask.interfaces.utils.MemberId;
20+
import org.springframework.format.annotation.DateTimeFormat;
21+
import org.springframework.http.HttpStatus;
22+
import org.springframework.http.ResponseEntity;
23+
import org.springframework.web.bind.annotation.*;
24+
25+
import java.time.LocalDateTime;
26+
import java.time.ZoneId;
27+
import java.util.List;
28+
29+
@RestController
30+
@RequestMapping("/v0/schedules")
31+
@RequiredArgsConstructor
32+
@Tag(name = "Schedule", description = "일정 관리 API")
33+
@ApiResponses({
34+
@ApiResponse(responseCode = "400", description = "잘못된 요청", content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
35+
@ApiResponse(responseCode = "404", description = "대상을 찾을 수 없습니다.", content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
36+
@ApiResponse(responseCode = "409", description = "현재 상태와 충돌했습니다.", content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
37+
@ApiResponse(responseCode = "500", description = "서버 내부 오류", content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
38+
})
39+
public class ScheduleControllerV0 {
40+
private final DateTimeUtils dateTimeUtils;
41+
private final ScheduleService scheduleService;
42+
private final ScheduleAdjustmentService scheduleAdjustmentService;
43+
private final ScheduleStateChangeService scheduleStateChangeService;
44+
45+
@PostMapping
46+
@Operation(summary = "일정 생성", description = "새 일정과 의존 관계를 등록합니다.")
47+
@ApiResponses({
48+
@ApiResponse(responseCode = "201", description = "일정이 성공적으로 생성되었습니다."),
49+
@ApiResponse(responseCode = "400", description = "요청 값 검증에 실패했습니다.", content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
50+
})
51+
public ResponseEntity<ScheduleResponse> createSchedule(@MemberId Long memberId,
52+
@Valid @RequestBody ScheduleRequest request) {
53+
Schedule saved = scheduleAdjustmentService.createSchedule(memberId, request.toCommand(null, memberId, dateTimeUtils));
54+
return ResponseEntity.status(HttpStatus.CREATED).body(ScheduleResponse.from(saved));
55+
}
56+
57+
@PatchMapping("/{scheduleId}")
58+
@Operation(summary = "일정 수정", description = "일정 본문과 의존 관계를 함께 수정합니다.")
59+
@ApiResponses({
60+
@ApiResponse(responseCode = "200", description = "일정이 수정되었습니다."),
61+
@ApiResponse(responseCode = "400", description = "요청 값 검증에 실패했습니다.", content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
62+
})
63+
public ResponseEntity<ScheduleResponse> updateSchedule(@MemberId Long memberId,
64+
@PathVariable Long scheduleId,
65+
@Valid @RequestBody ScheduleRequest request) {
66+
Schedule updated = scheduleAdjustmentService.adjustSchedule(memberId, request.toCommand(scheduleId, memberId, dateTimeUtils));
67+
return ResponseEntity.ok(ScheduleResponse.from(updated));
68+
}
69+
70+
@GetMapping
71+
@Operation(summary = "일정 목록 조회", description = "지정한 날짜의 일정을 조회합니다.")
72+
@ApiResponses({
73+
@ApiResponse(responseCode = "200", description = "목록 조회에 성공했습니다."),
74+
@ApiResponse(responseCode = "400", description = "날짜 형식이 올바르지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
75+
})
76+
public List<ScheduleResponse> getSchedules(@MemberId Long memberId,
77+
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime date) {
78+
return scheduleService.getScheduleList(memberId, date.toLocalDate()).stream()
79+
.map(ScheduleResponse::from)
80+
.toList();
81+
}
82+
83+
@GetMapping("/{scheduleId}")
84+
@Operation(summary = "일정 단건 조회", description = "특정 일정의 상세 정보를 조회합니다.")
85+
@ApiResponses({
86+
@ApiResponse(responseCode = "200", description = "단건 조회에 성공했습니다."),
87+
@ApiResponse(responseCode = "404", description = "일정을 찾을 수 없습니다.", content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
88+
})
89+
public ScheduleResponse getSchedule(@MemberId Long memberId, @PathVariable Long scheduleId) {
90+
Schedule schedule = scheduleService.getSchedule(memberId, scheduleId);
91+
return ScheduleResponse.from(schedule);
92+
}
93+
94+
@GetMapping("/week")
95+
@Operation(summary = "주간 일정 조회", description = "해당 주의 일정을 조회합니다.")
96+
@ApiResponses({
97+
@ApiResponse(responseCode = "200", description = "주간 일정 조회에 성공했습니다."),
98+
@ApiResponse(responseCode = "400", description = "잘못된 요청입니다.", content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
99+
})
100+
public List<ScheduleResponse> getWeeklySchedules(@MemberId Long memberId,
101+
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime time,
102+
@RequestParam ZoneId zoneId) {
103+
return scheduleService.getScheduleListForWeek(memberId, dateTimeUtils.toZonedDateTime(time, zoneId)).stream()
104+
.map(ScheduleResponse::from)
105+
.toList();
106+
}
107+
108+
@PostMapping("/{scheduleId}/start")
109+
@Operation(summary = "일정 시작", description = "일정을 진행 중 상태로 변경합니다.")
110+
@ApiResponses({
111+
@ApiResponse(responseCode = "204", description = "일정이 시작되었습니다."),
112+
@ApiResponse(responseCode = "409", description = "잘못된 상태 전환입니다.", content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
113+
})
114+
public ResponseEntity<Void> startSchedule(@MemberId Long memberId, @PathVariable Long scheduleId,
115+
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime time,
116+
@RequestParam ZoneId zoneId) {
117+
scheduleStateChangeService.startSchedule(memberId, scheduleId, dateTimeUtils.toZonedDateTime(time, zoneId));
118+
return ResponseEntity.noContent().build();
119+
}
120+
121+
@PostMapping("/{scheduleId}/complete")
122+
@Operation(summary = "일정 완료", description = "진행 중인 일정을 완료 처리합니다.")
123+
@ApiResponses({
124+
@ApiResponse(responseCode = "204", description = "일정이 완료되었습니다."),
125+
@ApiResponse(responseCode = "409", description = "잘못된 상태 전환입니다.", content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
126+
})
127+
public ResponseEntity<Void> completeSchedule(@MemberId Long memberId, @PathVariable Long scheduleId,
128+
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime time,
129+
@RequestParam ZoneId zoneId) {
130+
scheduleStateChangeService.completeSchedule(memberId, scheduleId, dateTimeUtils.toZonedDateTime(time, zoneId));
131+
return ResponseEntity.noContent().build();
132+
}
133+
134+
@PostMapping("/{scheduleId}/suspend")
135+
@Operation(summary = "일정 일시중지", description = "진행 중인 일정을 일시중지합니다.")
136+
@ApiResponses({
137+
@ApiResponse(responseCode = "204", description = "일정이 일시중지되었습니다."),
138+
@ApiResponse(responseCode = "409", description = "잘못된 상태 전환입니다.", content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
139+
})
140+
public ResponseEntity<Void> suspendSchedule(@MemberId Long memberId, @PathVariable Long scheduleId,
141+
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime time,
142+
@RequestParam ZoneId zoneId) {
143+
scheduleStateChangeService.suspendSchedule(memberId, scheduleId, dateTimeUtils.toZonedDateTime(time, zoneId));
144+
return ResponseEntity.noContent().build();
145+
}
146+
147+
@PostMapping("/{scheduleId}/cancel")
148+
@Operation(summary = "일정 취소", description = "예정되었거나 진행 중인 일정을 취소합니다.")
149+
@ApiResponses({
150+
@ApiResponse(responseCode = "204", description = "일정이 취소되었습니다."),
151+
@ApiResponse(responseCode = "409", description = "잘못된 상태 전환입니다.", content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
152+
})
153+
public ResponseEntity<Void> cancelSchedule(@MemberId Long memberId, @PathVariable Long scheduleId) {
154+
scheduleStateChangeService.cancelSchedule(memberId, scheduleId);
155+
return ResponseEntity.noContent().build();
156+
}
157+
158+
@DeleteMapping("/{scheduleId}")
159+
@Operation(summary = "일정 삭제", description = "지정한 일정을 삭제합니다.")
160+
@ApiResponses({
161+
@ApiResponse(responseCode = "204", description = "일정이 삭제되었습니다."),
162+
@ApiResponse(responseCode = "409", description = "잘못된 상태 전환입니다.", content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
163+
})
164+
public ResponseEntity<Void> deleteSchedule(@MemberId Long memberId, @PathVariable Long scheduleId) {
165+
scheduleService.deleteSchedule(memberId, scheduleId);
166+
return ResponseEntity.noContent().build();
167+
}
168+
}

src/main/java/me/gg/pinit/pinittask/interfaces/web/StatisticsController.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.time.LocalDateTime;
2020
import java.time.ZoneId;
2121

22+
@Deprecated
2223
@RestController
2324
@RequestMapping("/statistics")
2425
@Tag(name = "Statistics", description = "통계 조회 API")
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package me.gg.pinit.pinittask.interfaces.web;
2+
3+
import io.swagger.v3.oas.annotations.Operation;
4+
import io.swagger.v3.oas.annotations.media.Content;
5+
import io.swagger.v3.oas.annotations.media.Schema;
6+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
7+
import io.swagger.v3.oas.annotations.tags.Tag;
8+
import me.gg.pinit.pinittask.application.datetime.DateTimeUtils;
9+
import me.gg.pinit.pinittask.application.statistics.service.StatisticsService;
10+
import me.gg.pinit.pinittask.interfaces.dto.StatisticsResponse;
11+
import me.gg.pinit.pinittask.interfaces.exception.ErrorResponse;
12+
import me.gg.pinit.pinittask.interfaces.utils.MemberId;
13+
import org.springframework.format.annotation.DateTimeFormat;
14+
import org.springframework.web.bind.annotation.GetMapping;
15+
import org.springframework.web.bind.annotation.RequestMapping;
16+
import org.springframework.web.bind.annotation.RequestParam;
17+
import org.springframework.web.bind.annotation.RestController;
18+
19+
import java.time.LocalDateTime;
20+
import java.time.ZoneId;
21+
22+
@RestController
23+
@RequestMapping("/v0/statistics")
24+
@Tag(name = "Statistics", description = "통계 조회 API")
25+
public class StatisticsControllerV0 {
26+
private final StatisticsService statisticsService;
27+
private final DateTimeUtils dateTimeUtils;
28+
29+
public StatisticsControllerV0(StatisticsService statisticsService, DateTimeUtils dateTimeUtils) {
30+
this.statisticsService = statisticsService;
31+
this.dateTimeUtils = dateTimeUtils;
32+
}
33+
34+
@GetMapping
35+
@Operation(summary = "사용자 통계 조회", description = "주어진 시점 기준 주간 통계를 조회합니다.")
36+
@ApiResponse(responseCode = "200", description = "통계 조회 성공")
37+
@ApiResponse(responseCode = "400", description = "잘못된 요청", content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
38+
@ApiResponse(responseCode = "404", description = "통계를 찾을 수 없습니다.", content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
39+
public StatisticsResponse getStatistics(
40+
@MemberId Long memberId,
41+
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime time,
42+
@RequestParam ZoneId zoneId
43+
) {
44+
return StatisticsResponse.from(statisticsService.getStatistics(memberId, dateTimeUtils.toZonedDateTime(time, zoneId)));
45+
}
46+
}

0 commit comments

Comments
 (0)