Skip to content

Commit 067e190

Browse files
authored
Merge pull request #281 from prgrms-web-devcourse-final-project/feat/#255
[Plan] 교통 경로 상세 정보를 받기 위해 JSON 형태로 저장
2 parents 475d017 + da3db4a commit 067e190

7 files changed

Lines changed: 199 additions & 3 deletions

File tree

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package com.back.web7_9_codecrete_be.domain.plans.dto;
2+
3+
import com.fasterxml.jackson.annotation.JsonInclude;
4+
import io.swagger.v3.oas.annotations.media.Schema;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Builder;
7+
import lombok.Getter;
8+
import lombok.NoArgsConstructor;
9+
10+
import java.util.List;
11+
12+
/**
13+
* 교통 경로 정보 DTO
14+
* 프론트엔드에서 제공하는 교통 경로 상세 정보를 담는 클래스
15+
*/
16+
@Getter
17+
@Builder
18+
@NoArgsConstructor
19+
@AllArgsConstructor
20+
@JsonInclude(JsonInclude.Include.NON_NULL)
21+
@Schema(description = "교통 경로 정보")
22+
public class TransportRoute {
23+
24+
@Schema(description = "총 소요 시간 (초)", example = "3600")
25+
private Integer totalTime;
26+
27+
@Schema(description = "총 거리 (m)", example = "5000")
28+
private Integer totalDistance;
29+
30+
@Schema(description = "총 도보 시간 (초) - 선택적", example = "600")
31+
private Integer totalWalkTime;
32+
33+
@Schema(description = "총 도보 거리 (m) - 선택적", example = "500")
34+
private Integer totalWalkDistance;
35+
36+
@Schema(description = "환승 횟수 - 선택적", example = "2")
37+
private Integer transferCount;
38+
39+
@Schema(description = "도보, 대중교통 구간 배열")
40+
private List<Leg> leg;
41+
42+
@Schema(description = "요금 정보")
43+
private Fare fare;
44+
45+
@Getter
46+
@Builder
47+
@NoArgsConstructor
48+
@AllArgsConstructor
49+
@JsonInclude(JsonInclude.Include.NON_NULL)
50+
@Schema(description = "이동 구간 정보")
51+
public static class Leg {
52+
@Schema(description = "이동 수단", example = "SUBWAY", allowableValues = {"WALK", "BUS", "SUBWAY", "EXPRESSBUS", "TRAIN", "AIRPLANE", "FERRY"})
53+
private String mode;
54+
55+
@Schema(description = "구간 시작점")
56+
private Location start;
57+
58+
@Schema(description = "구간 도착점")
59+
private Location end;
60+
61+
@Schema(description = "구간 소요 시간 (초)", example = "1800")
62+
private Integer sectionTime;
63+
64+
@Schema(description = "노선명 (예: \"간선:472\", \"지하철 2호선\") - 선택적", example = "지하철 2호선")
65+
private String route;
66+
67+
@Schema(description = "노선 색상 (Hex 코드) - 선택적", example = "#00A84D")
68+
private String routeColor;
69+
70+
@Schema(description = "노선 타입 - 선택적", example = "1")
71+
private Integer type;
72+
73+
@Schema(description = "경유 정류장/역 개수 - 선택적", example = "5")
74+
private Integer passStopCount;
75+
76+
@Schema(description = "경로 선형 (지도 그리기용) - 선택적")
77+
private PassShape passShape;
78+
}
79+
80+
@Getter
81+
@Builder
82+
@NoArgsConstructor
83+
@AllArgsConstructor
84+
@JsonInclude(JsonInclude.Include.NON_NULL)
85+
@Schema(description = "위치 정보 (위도/경도)")
86+
public static class Location {
87+
@Schema(description = "위치 이름", example = "강남역")
88+
private String name;
89+
90+
@Schema(description = "경도", example = "127.0276")
91+
private Double lon;
92+
93+
@Schema(description = "위도", example = "37.4979")
94+
private Double lat;
95+
}
96+
97+
@Getter
98+
@Builder
99+
@NoArgsConstructor
100+
@AllArgsConstructor
101+
@JsonInclude(JsonInclude.Include.NON_NULL)
102+
@Schema(description = "경로 선형 정보")
103+
public static class PassShape {
104+
@Schema(description = "경로 선형 (지도 그리기용)", example = "LINESTRING(127.0276 37.4979, 127.0286 37.4989)")
105+
private String linestring;
106+
}
107+
108+
@Getter
109+
@Builder
110+
@NoArgsConstructor
111+
@AllArgsConstructor
112+
@JsonInclude(JsonInclude.Include.NON_NULL)
113+
@Schema(description = "요금 정보")
114+
public static class Fare {
115+
@Schema(description = "총 요금 (원) - 도보는 0원, 대중교통은 합산금액, 자동차는 택시 + 톨게이트비", example = "1500")
116+
private Integer totalFare;
117+
118+
@Schema(description = "택시 요금 (원) - 선택적", example = "10000")
119+
private Integer taxi;
120+
121+
@Schema(description = "톨게이트 요금 (원) - 선택적", example = "2500")
122+
private Integer toll;
123+
}
124+
}
125+

src/main/java/com/back/web7_9_codecrete_be/domain/plans/dto/request/ScheduleAddRequest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.back.web7_9_codecrete_be.domain.plans.dto.request;
22

3+
import com.back.web7_9_codecrete_be.domain.plans.dto.TransportRoute;
34
import com.back.web7_9_codecrete_be.domain.plans.entity.Schedule;
45
import jakarta.validation.constraints.AssertTrue;
56
import jakarta.validation.constraints.Max;
@@ -74,6 +75,9 @@ public class ScheduleAddRequest {
7475

7576
private Schedule.TransportType transportType;
7677

78+
// 교통 경로 상세 정보 (TRANSPORT 타입일 때 선택적)
79+
private TransportRoute transportRoute;
80+
7781
/**
7882
* 위도와 경도는 함께 제공되어야 함 (둘 다 null이거나 둘 다 값이 있어야 함)
7983
* 단, TRANSPORT 타입일 때는 locationLat/Lon을 사용하지 않음 (endPlaceLat/Lon 사용)

src/main/java/com/back/web7_9_codecrete_be/domain/plans/dto/request/ScheduleUpdateRequest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.back.web7_9_codecrete_be.domain.plans.dto.request;
22

3+
import com.back.web7_9_codecrete_be.domain.plans.dto.TransportRoute;
34
import com.back.web7_9_codecrete_be.domain.plans.entity.Schedule;
45
import jakarta.validation.constraints.AssertTrue;
56
import jakarta.validation.constraints.Max;
@@ -65,6 +66,9 @@ public class ScheduleUpdateRequest {
6566

6667
private Schedule.TransportType transportType;
6768

69+
// 교통 경로 상세 정보 (TRANSPORT 타입일 때 선택적)
70+
private TransportRoute transportRoute;
71+
6872
/**
6973
* 위도와 경도는 함께 제공되어야 함 (둘 다 null이거나 둘 다 값이 있어야 함)
7074
* 단, TRANSPORT 타입일 때는 locationLat/Lon을 사용하지 않음 (endPlaceLat/Lon 사용)

src/main/java/com/back/web7_9_codecrete_be/domain/plans/dto/response/PlanDetailResponse.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.back.web7_9_codecrete_be.domain.plans.dto.response;
22

3+
import com.back.web7_9_codecrete_be.domain.plans.dto.TransportRoute;
34
import com.back.web7_9_codecrete_be.domain.plans.entity.PlanParticipant;
45
import com.back.web7_9_codecrete_be.domain.plans.entity.Schedule;
56
import io.swagger.v3.oas.annotations.media.Schema;
@@ -86,6 +87,8 @@ public static class ScheduleInfo {
8687
private Integer distance;
8788
@Schema(description = "교통 수단 타입 (TRANSPORT 타입일 때)", example = "SUBWAY")
8889
private Schedule.TransportType transportType;
90+
@Schema(description = "교통 경로 상세 정보 (TRANSPORT 타입일 때)", example = "{\"totalTime\":3600,\"totalDistance\":5000}")
91+
private TransportRoute transportRoute;
8992
@Schema(description = "메인 이벤트(콘서트) 여부", example = "true")
9093
private Boolean isMainEvent;
9194
// 아래 공연 정보들은 isMainEvent가 true인 경우 필요

src/main/java/com/back/web7_9_codecrete_be/domain/plans/dto/response/ScheduleResponse.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.back.web7_9_codecrete_be.domain.plans.dto.response;
22

3+
import com.back.web7_9_codecrete_be.domain.plans.dto.TransportRoute;
34
import com.back.web7_9_codecrete_be.domain.plans.entity.Schedule;
45
import lombok.Builder;
56
import lombok.Getter;
@@ -27,6 +28,7 @@ public class ScheduleResponse {
2728
private Double endPlaceLon;
2829
private Integer distance;
2930
private Schedule.TransportType transportType;
31+
private TransportRoute transportRoute;
3032
private Boolean isMainEvent;
3133
// 공연 정보 (isMainEvent가 true인 경우)
3234
private Long concertId;

src/main/java/com/back/web7_9_codecrete_be/domain/plans/entity/Schedule.java

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package com.back.web7_9_codecrete_be.domain.plans.entity;
22

3+
import com.back.web7_9_codecrete_be.domain.plans.dto.TransportRoute;
4+
import com.fasterxml.jackson.core.JsonProcessingException;
5+
import com.fasterxml.jackson.databind.ObjectMapper;
36
import jakarta.persistence.*;
47
import lombok.*;
58
import org.hibernate.annotations.CreationTimestamp;
@@ -74,6 +77,10 @@ public class Schedule {
7477
@Column(name = "transport_type")
7578
private TransportType transportType;
7679

80+
// 교통 경로 상세 정보 (JSON 형태로 저장)
81+
@Column(name = "transport_route", columnDefinition = "TEXT")
82+
private String transportRoute;
83+
7784
@CreationTimestamp
7885
@Column(name = "created_date", nullable = false, updatable = false)
7986
private LocalDateTime createdDate;
@@ -100,7 +107,7 @@ public Schedule(Plan plan, ScheduleType scheduleType, String title,
100107
Double startPlaceLat, Double startPlaceLon,
101108
Double endPlaceLat, Double endPlaceLon,
102109
Integer distance, TransportType transportType,
103-
Boolean isMainEvent) {
110+
String transportRoute, Boolean isMainEvent) {
104111
this.plan = plan;
105112
this.scheduleType = scheduleType;
106113
this.title = title;
@@ -117,6 +124,7 @@ public Schedule(Plan plan, ScheduleType scheduleType, String title,
117124
this.endPlaceLon = endPlaceLon;
118125
this.distance = distance;
119126
this.transportType = transportType;
127+
this.transportRoute = transportRoute;
120128
this.isMainEvent = isMainEvent != null ? isMainEvent : false;
121129
}
122130

@@ -126,7 +134,8 @@ public void update(ScheduleType scheduleType, String title,
126134
Integer estimatedCost, String details,
127135
Double startPlaceLat, Double startPlaceLon,
128136
Double endPlaceLat, Double endPlaceLon,
129-
Integer distance, TransportType transportType) {
137+
Integer distance, TransportType transportType,
138+
String transportRoute) {
130139
this.scheduleType = scheduleType;
131140
this.title = title;
132141
this.startAt = startAt;
@@ -142,6 +151,38 @@ public void update(ScheduleType scheduleType, String title,
142151
this.endPlaceLon = endPlaceLon;
143152
this.distance = distance;
144153
this.transportType = transportType;
154+
this.transportRoute = transportRoute;
155+
}
156+
157+
/**
158+
* TransportRoute 객체를 JSON 문자열로 변환하여 저장
159+
*/
160+
public void setTransportRoute(TransportRoute transportRoute) {
161+
if (transportRoute == null) {
162+
this.transportRoute = null;
163+
return;
164+
}
165+
try {
166+
ObjectMapper objectMapper = new ObjectMapper();
167+
this.transportRoute = objectMapper.writeValueAsString(transportRoute);
168+
} catch (JsonProcessingException e) {
169+
throw new RuntimeException("Failed to serialize transportRoute", e);
170+
}
171+
}
172+
173+
/**
174+
* JSON 문자열을 TransportRoute 객체로 변환하여 반환
175+
*/
176+
public TransportRoute getTransportRouteAsObject() {
177+
if (transportRoute == null || transportRoute.isEmpty()) {
178+
return null;
179+
}
180+
try {
181+
ObjectMapper objectMapper = new ObjectMapper();
182+
return objectMapper.readValue(transportRoute, TransportRoute.class);
183+
} catch (JsonProcessingException e) {
184+
throw new RuntimeException("Failed to deserialize transportRoute", e);
185+
}
145186
}
146187

147188
public enum ScheduleType {

src/main/java/com/back/web7_9_codecrete_be/domain/plans/service/PlanService.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ private PlanDetailResponse buildPlanDetailResponse(Plan plan) {
163163
.endPlaceLon(item.getEndPlaceLon())
164164
.distance(item.getDistance())
165165
.transportType(item.getTransportType())
166+
.transportRoute(item.getTransportRouteAsObject())
166167
.isMainEvent(item.getIsMainEvent())
167168
.createdDate(item.getCreatedDate())
168169
.modifiedDate(item.getModifiedDate());
@@ -293,6 +294,11 @@ public ScheduleResponse addSchedule(Long planId, User user, ScheduleAddRequest r
293294
.transportType(request.getTransportType())
294295
.build();
295296

297+
// transportRoute 설정 (JSON 문자열로 변환)
298+
if (request.getTransportRoute() != null) {
299+
schedule.setTransportRoute(request.getTransportRoute());
300+
}
301+
296302
plan.addSchedule(schedule);
297303
// cascade 설정으로 인해 plan 저장 시 schedule도 함께 저장됨
298304
planRepository.save(plan);
@@ -661,6 +667,15 @@ private void validateScheduleUpdate(ScheduleUpdateRequest request, Schedule sche
661667
*/
662668
private void updateScheduleFields(Schedule schedule, ScheduleUpdateRequest request,
663669
Schedule.ScheduleType newScheduleType) {
670+
// transportRoute는 별도로 처리 (null이 아닌 경우에만 업데이트)
671+
String transportRouteJson = null;
672+
if (request.getTransportRoute() != null) {
673+
schedule.setTransportRoute(request.getTransportRoute());
674+
transportRouteJson = schedule.getTransportRoute(); // 변환된 JSON 문자열 가져오기
675+
} else {
676+
transportRouteJson = schedule.getTransportRoute(); // 기존 값 유지
677+
}
678+
664679
schedule.update(
665680
newScheduleType,
666681
request.getTitle() != null ? request.getTitle() : schedule.getTitle(),
@@ -676,7 +691,8 @@ private void updateScheduleFields(Schedule schedule, ScheduleUpdateRequest reque
676691
request.getEndPlaceLat() != null ? request.getEndPlaceLat() : schedule.getEndPlaceLat(),
677692
request.getEndPlaceLon() != null ? request.getEndPlaceLon() : schedule.getEndPlaceLon(),
678693
request.getDistance() != null ? request.getDistance() : schedule.getDistance(),
679-
request.getTransportType() != null ? request.getTransportType() : schedule.getTransportType()
694+
request.getTransportType() != null ? request.getTransportType() : schedule.getTransportType(),
695+
transportRouteJson
680696
);
681697
}
682698

@@ -787,6 +803,7 @@ private ScheduleResponse toScheduleResponse(Schedule schedule) {
787803
.endPlaceLon(schedule.getEndPlaceLon())
788804
.distance(schedule.getDistance())
789805
.transportType(schedule.getTransportType())
806+
.transportRoute(schedule.getTransportRouteAsObject())
790807
.isMainEvent(schedule.getIsMainEvent())
791808
.createdDate(schedule.getCreatedDate())
792809
.modifiedDate(schedule.getModifiedDate());

0 commit comments

Comments
 (0)