Skip to content

Commit 32d155e

Browse files
authored
Merge pull request #44 from hackathon-soa/feat/#42
Feat/#42
2 parents aad4f5d + 4d2bf7c commit 32d155e

7 files changed

Lines changed: 175 additions & 15 deletions

File tree

src/main/java/hackathon/soa/common/apiPayload/code/status/ErrorStatus.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ public enum ErrorStatus implements BaseErrorCode {
2727
MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "MEMBER4001", "존재하지 않는 사용자입니다."),
2828

2929
// 코스 관련
30-
NOT_FOUND_COURSE(HttpStatus.NOT_FOUND, "H001", "코스를 찾을 수 없습니다."),
31-
NOT_FOUND_SEGMENT(HttpStatus.NOT_FOUND, "H002", "세그먼트를 찾을 수 없습니다."),
32-
30+
NOT_FOUND_COURSE(HttpStatus.NOT_FOUND, "COURSE4001", "코스를 찾을 수 없습니다."),
31+
NOT_FOUND_SEGMENT(HttpStatus.NOT_FOUND, "COURSE4002", "세그먼트를 찾을 수 없습니다."),
32+
UNAUTHORIZED_COURSE_ACCESS(HttpStatus.FORBIDDEN, "COURSE4003", "해당 코스에 접근할 권한이 없습니다."),
3333

3434

3535
// 세그먼트 관련

src/main/java/hackathon/soa/domain/segment/SegmentController.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,17 @@ public ApiResponse<SegmentResponseDTO.CourseDetailResponseDTO> getCourseDetail(
3333
SegmentResponseDTO.CourseDetailResponseDTO result = segmentService.getCourseDetail(courseId, memberId);
3434
return ApiResponse.onSuccess(result);
3535
}
36+
37+
@GetMapping("/my/{courseId}")
38+
@Operation(
39+
summary = "나의 특정 코스 상세 조회",
40+
description = "코스 ID로 특정 코스의 세그먼트들의 정보를 조회합니다. (내꺼)"
41+
)
42+
public ApiResponse<SegmentResponseDTO.MyCourseDetailResponseDTO> getMyCourseDetail(
43+
@PathVariable Long courseId,
44+
@Parameter(hidden = true) @JwtUser Long memberId
45+
) {
46+
SegmentResponseDTO.MyCourseDetailResponseDTO result = segmentService.getMyCourseDetail(courseId, memberId);
47+
return ApiResponse.onSuccess(result);
48+
}
3649
}

src/main/java/hackathon/soa/domain/segment/SegmentConverter.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,46 @@ public static SegmentResponseDTO.MoveSegmentDTO toMoveSegmentDTO(MoveSegment mov
6464
.movementDistanceKm(BigDecimal.valueOf(moveSegment.getMovementDistanceKm()))
6565
.build();
6666
}
67+
68+
69+
public static SegmentResponseDTO.MyCourseDetailResponseDTO toMyCourseDetailResponseDTO(
70+
Long courseId,
71+
List<SegmentResponseDTO.MySegmentDetailDTO> segments) {
72+
return SegmentResponseDTO.MyCourseDetailResponseDTO.builder()
73+
.courseId(courseId)
74+
.segments(segments)
75+
.build();
76+
}
77+
78+
public static SegmentResponseDTO.MySegmentDetailDTO toMySegmentDetailDTO(
79+
CourseSegment courseSegment,
80+
StaySegment staySegment,
81+
MoveSegment moveSegment,
82+
String mateStatus) {
83+
84+
// 시간 포맷터 정의
85+
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("MM/dd(E)", Locale.KOREAN);
86+
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm");
87+
88+
String segmentType = (staySegment != null) ? "장소" : "이동";
89+
90+
return SegmentResponseDTO.MySegmentDetailDTO.builder()
91+
.segmentOrder(courseSegment.getSegmentOrder())
92+
.segmentType(segmentType)
93+
.date(courseSegment.getStartTime().format(dateFormatter))
94+
.startTime(courseSegment.getStartTime().format(timeFormatter))
95+
.endTime(courseSegment.getEndTime().format(timeFormatter))
96+
.staySegment(staySegment != null ? toMyStaySegmentDTO(staySegment, mateStatus) : null)
97+
.moveSegment(moveSegment != null ? toMoveSegmentDTO(moveSegment) : null)
98+
.build();
99+
}
100+
101+
public static SegmentResponseDTO.MyStaySegmentDTO toMyStaySegmentDTO(StaySegment staySegment, String mateStatus) {
102+
return SegmentResponseDTO.MyStaySegmentDTO.builder()
103+
.segmentId(staySegment.getSegmentId())
104+
.locationName(staySegment.getLocationName())
105+
.locationAddress(staySegment.getLocationAddress())
106+
.mateStatus(mateStatus)
107+
.build();
108+
}
67109
}

src/main/java/hackathon/soa/domain/segment/SegmentService.java

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@
1919
import org.springframework.stereotype.Service;
2020
import org.springframework.transaction.annotation.Transactional;
2121

22-
import java.time.format.DateTimeFormatter;
2322
import java.util.List;
24-
import java.util.Locale;
2523
import java.util.Optional;
2624
import java.util.stream.Collectors;
2725

@@ -68,21 +66,19 @@ private SegmentResponseDTO.SegmentDetailDTO buildSegmentDetail(CourseSegment cou
6866
Long segmentId = courseSegment.getId();
6967

7068
try {
71-
// 장소 세그먼트 먼저 확인 (@MapsId로 PK가 동일함)
69+
// 장소 세그먼트 먼저 확인
7270
Optional<StaySegment> staySegment = staySegmentRepository.findById(segmentId);
7371
if (staySegment.isPresent()) {
7472
// 현재 사용자가 이 장소 세그먼트에 참여했는지 확인
7573
boolean isParticipated = staySegment.get().getSegmentParticipations().stream()
7674
.anyMatch(participation -> participation.getMember().getId().equals(memberId));
7775

78-
// SegmentConverter를 사용하여 장소 세그먼트 DTO 생성
7976
return SegmentConverter.toSegmentDetailDTO(courseSegment, staySegment.get(), null, isParticipated);
8077
}
8178

82-
// 이동 세그먼트 확인 (@MapsId로 PK가 동일함)
79+
// 이동 세그먼트 확인
8380
Optional<MoveSegment> moveSegment = moveSegmentRepository.findById(segmentId);
8481
if (moveSegment.isPresent()) {
85-
// SegmentConverter를 사용하여 이동 세그먼트 DTO 생성
8682
return SegmentConverter.toSegmentDetailDTO(courseSegment, null, moveSegment.get(), false);
8783
}
8884

@@ -106,4 +102,75 @@ public SearchResponseDTO.SearchCourseResponseDTO getCourseById(Long memberId, Lo
106102

107103
return SearchConverter.toSearchCourseResponseDTO(course, liked);
108104
}
105+
106+
107+
public SegmentResponseDTO.MyCourseDetailResponseDTO getMyCourseDetail(Long courseId, Long memberId) {
108+
// 1. 코스 조회 및 작성자 확인
109+
Course course = courseRepository.findById(courseId)
110+
.orElseThrow(() -> new CourseHandler(ErrorStatus.NOT_FOUND_COURSE));
111+
112+
// 내 코스가 아니면 예외 발생
113+
if (!course.getMember().getId().equals(memberId)) {
114+
throw new CourseHandler(ErrorStatus.UNAUTHORIZED_COURSE_ACCESS);
115+
}
116+
117+
// 2. 코스 세그먼트들을 순서대로 조회
118+
List<CourseSegment> courseSegments = courseSegmentRepository.findByCourse_IdOrderBySegmentOrder(courseId);
119+
120+
if (courseSegments.isEmpty()) {
121+
throw new CourseHandler(ErrorStatus.NOT_FOUND_COURSE);
122+
}
123+
124+
// 3. 각 세그먼트 정보를 조회하여 DTO 생성
125+
List<SegmentResponseDTO.MySegmentDetailDTO> segmentDetails = courseSegments.stream()
126+
.map(this::buildMySegmentDetail)
127+
.collect(Collectors.toList());
128+
129+
// 4. SegmentConverter를 사용하여 최종 응답 DTO 생성
130+
return SegmentConverter.toMyCourseDetailResponseDTO(courseId, segmentDetails);
131+
}
132+
133+
private SegmentResponseDTO.MySegmentDetailDTO buildMySegmentDetail(CourseSegment courseSegment) {
134+
Long segmentId = courseSegment.getId();
135+
136+
try {
137+
// 장소 세그먼트 먼저 확인
138+
Optional<StaySegment> staySegment = staySegmentRepository.findById(segmentId);
139+
if (staySegment.isPresent()) {
140+
// 동행 메이트 상태 확인
141+
String mateStatus = determineMateStatus(staySegment.get());
142+
143+
return SegmentConverter.toMySegmentDetailDTO(courseSegment, staySegment.get(), null, mateStatus);
144+
}
145+
146+
// 이동 세그먼트 확인
147+
Optional<MoveSegment> moveSegment = moveSegmentRepository.findById(segmentId);
148+
if (moveSegment.isPresent()) {
149+
return SegmentConverter.toMySegmentDetailDTO(courseSegment, null, moveSegment.get(), null);
150+
}
151+
152+
throw new SegmentHandler(ErrorStatus.SEGMENT_NOT_FOUND);
153+
154+
} catch (Exception e) {
155+
log.error("Error while building my segment detail for segmentId: {}", segmentId, e);
156+
throw new SegmentHandler(ErrorStatus.SEGMENT_DESERIALIZATION_ERROR);
157+
}
158+
}
159+
160+
private String determineMateStatus(StaySegment staySegment) {
161+
List<SegmentParticipation> participations = staySegment.getSegmentParticipations();
162+
163+
if (participations.isEmpty()) {
164+
return "모집 중";
165+
}
166+
167+
boolean hasApproved = participations.stream()
168+
.anyMatch(participation -> participation.getStatus() == SegmentParticipationStatus.APPROVED);
169+
170+
if (hasApproved) {
171+
return "신청확정";
172+
}
173+
174+
return "신청확인";
175+
}
109176
}

src/main/java/hackathon/soa/domain/segment/dto/SegmentResponseDTO.java

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package hackathon.soa.domain.segment.dto;
22

33
import hackathon.soa.domain.search.dto.SearchResponseDTO;
4+
import hackathon.soa.entity.CourseSegment;
45
import hackathon.soa.entity.MoveSegment;
56
import hackathon.soa.entity.MovementType;
67
import hackathon.soa.entity.StaySegment;
@@ -10,7 +11,11 @@
1011
import lombok.NoArgsConstructor;
1112

1213
import java.math.BigDecimal;
14+
import java.time.format.DateTimeFormatter;
1315
import java.util.List;
16+
import java.util.Locale;
17+
18+
import static hackathon.soa.domain.segment.SegmentConverter.toMoveSegmentDTO;
1419

1520
public class SegmentResponseDTO {
1621

@@ -32,9 +37,9 @@ public static class CourseDetailResponseDTO {
3237
public static class SegmentDetailDTO {
3338
private Integer segmentOrder;
3439
private String segmentType; // "장소" or "이동"
35-
private String date; // "07/05(토)" 형식
36-
private String startTime; // "09:00" 형식
37-
private String endTime; // "09:30" 형식
40+
private String date; // "07/05(토)"
41+
private String startTime; // "09:00"
42+
private String endTime; // "09:30"
3843
private StaySegmentDTO staySegment;
3944
private MoveSegmentDTO moveSegment;
4045
}
@@ -76,4 +81,39 @@ public static MoveSegmentDTO from(MoveSegment moveSegment) {
7681
.build();
7782
}
7883
}
84+
85+
86+
@Builder
87+
@Getter
88+
@NoArgsConstructor
89+
@AllArgsConstructor
90+
public static class MyCourseDetailResponseDTO {
91+
private Long courseId;
92+
private List<MySegmentDetailDTO> segments;
93+
}
94+
95+
@Builder
96+
@Getter
97+
@NoArgsConstructor
98+
@AllArgsConstructor
99+
public static class MySegmentDetailDTO {
100+
private Integer segmentOrder;
101+
private String segmentType; // "장소" or "이동"
102+
private String date; // "07/05(토)"
103+
private String startTime; // "09:00"
104+
private String endTime; // "09:30"
105+
private MyStaySegmentDTO staySegment;
106+
private MoveSegmentDTO moveSegment;
107+
}
108+
109+
@Builder
110+
@Getter
111+
@NoArgsConstructor
112+
@AllArgsConstructor
113+
public static class MyStaySegmentDTO {
114+
private Long segmentId;
115+
private String locationName;
116+
private String locationAddress;
117+
private String mateStatus; // "모집 중", "신청확인", "신청확정"
118+
}
79119
}

src/main/java/hackathon/soa/domain/segment/repository/CourseSegmentRepository.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import org.springframework.data.jpa.repository.JpaRepository;
66

77
import java.util.List;
8-
//통일
8+
99
public interface CourseSegmentRepository extends JpaRepository<CourseSegment, Long> {
1010
List<CourseSegment> findByCourse_IdOrderBySegmentOrder(Long courseId);
1111
List<CourseSegment> findAllByCourse(Course course);

src/main/java/hackathon/soa/domain/segment/repository/StaySegmentRepository.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package hackathon.soa.domain.segment.repository;
22

3-
import hackathon.soa.entity.Course;
43
import hackathon.soa.entity.StaySegment;
54
import org.springframework.data.jpa.repository.JpaRepository;
65

7-
import java.util.List;
86
import java.util.Optional;
97

108
public interface StaySegmentRepository extends JpaRepository<StaySegment, Long> {

0 commit comments

Comments
 (0)