Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] 여행후기 기본 구현 완료 #39

Merged
merged 6 commits into from
Jan 8, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.haejwo.tripcometrue.domain.triprecord.controller;

import com.haejwo.tripcometrue.domain.triprecord.dto.request.TripRecordRequestDto;
import com.haejwo.tripcometrue.domain.triprecord.dto.response.TripRecordResponseDto;
import com.haejwo.tripcometrue.domain.triprecord.service.TripRecordService;
import com.haejwo.tripcometrue.global.util.ResponseDTO;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/v1/trip-record")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

URL에 도메인을 어떻게 표시할지는 추후에 논의해서 통일하면 좋을듯 합니다 :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

원래는 restful 기준에 따르면 소문자와 - _ 이 두개로 표현하는게 정석으로 알고 있습니다.

@RequiredArgsConstructor
public class TripRecordController {

private final TripRecordService tripRecordService;

@PostMapping
public ResponseEntity<ResponseDTO<TripRecordResponseDto>> tripRecordAdd(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

컨트롤러의 메소드에선 서비스 계층의 메소드와 다르게 동사를 뒤에 배치한 이유가 있나요?
일반적으로 메소드명은 동사가 앞에 나오는 것으로 이해하고 있어서요.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

구분을 위해서 그렇게 했습니다.
사람에 따라서는 컨트롤러와 서비스 단 구분을 위해서 함수명 단어를 아예 다르게 하시는 분도 있는 모양입니다.
제가 알기로는 이 부분은 팀내 약속과 개인의 취향의 영역이라고 알고있는데, 대표적인 컨벤션 같은게 있나요?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아하 그렇군요! 제가 알기로도 정해진 건 없다고 들었습니다! 메소드명까지 굳이 통일할 필요가 없다면 재량껏 사용해도 될듯 해요. 다른 분들은 어떻게 생각하시는지 궁금하네요 ㅎㅎ

@RequestBody TripRecordRequestDto requestDto
) {

TripRecordResponseDto responseDto = tripRecordService.addTripRecord(requestDto);
ResponseDTO<TripRecordResponseDto> responseBody = ResponseDTO.okWithData(responseDto);

return ResponseEntity
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

등록한 여행 후기 내용 전체를 다시 프론트로 돌려주는 이유가 있나요?
등록된 여행 후기 Id만 반환시키고, 프론트에서 해당 id를 통해 GET 요청을 하도록 하면 되지 않을까 궁금해서 여쭤봅니다.
프론트에서 POST url을 그대로 가지고 있으면 중복 등록이 될 수 있어서 GET으로 리다이렉트 하는 것처럼요!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이건 그냥 습관, 성향 차이로 알고 있습니다.

.status(responseBody.getCode())
.body(responseBody);
}

@GetMapping("/{tripRecordId}")
public ResponseEntity<ResponseDTO<TripRecordResponseDto>> tripRecordDetails(
@PathVariable Long tripRecordId
) {

TripRecordResponseDto responseDto = tripRecordService.findTripRecord(tripRecordId);
ResponseDTO<TripRecordResponseDto> responseBody = ResponseDTO.okWithData(responseDto);

return ResponseEntity
.status(responseBody.getCode())
.body(responseBody);
}

@PutMapping("/{tripRecordId}")
public ResponseEntity<ResponseDTO<TripRecordResponseDto>> tripRecordModify(
@PathVariable Long tripRecordId,
@RequestBody TripRecordRequestDto requestDto
) {

TripRecordResponseDto responseDto = tripRecordService.modifyTripRecord(tripRecordId, requestDto);
ResponseDTO<TripRecordResponseDto> responseBody = ResponseDTO.okWithData(responseDto);

return ResponseEntity
.status(responseBody.getCode())
.body(responseBody);

}

@DeleteMapping("/{tripRecordId}")
public ResponseEntity<ResponseDTO> tripRecordRemove(
@PathVariable Long tripRecordId
) {

tripRecordService.removeTripRecord(tripRecordId);
ResponseDTO responseBody = ResponseDTO.ok();

return ResponseEntity
.status(responseBody.getCode())
.body(responseBody);
}





}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.haejwo.tripcometrue.domain.triprecord.dto.request;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.haejwo.tripcometrue.domain.triprecord.entity.ExpenseType;
import com.haejwo.tripcometrue.domain.triprecord.entity.TripRecord;
import java.time.LocalDate;
import lombok.Builder;

public record TripRecordRequestDto(
String title,
String content,
ExpenseType expenseType,
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") LocalDate tripStartDay,
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") LocalDate tripEndDay,
String countries
) {

@Builder
public TripRecordRequestDto(String title, String content, ExpenseType expenseType,
LocalDate tripStartDay, LocalDate tripEndDay, String countries) {
this.title = title;
this.content = content;
this.expenseType = expenseType;
this.tripStartDay = tripStartDay;
this.tripEndDay = tripEndDay;
this.countries = countries;
}

public TripRecord toEntity() {
return TripRecord.builder()
.title(this.title)
.content(this.content)
.expenseType(this.expenseType)
.tripStartDay(this.tripStartDay)
.tripEndDay(this.tripEndDay)
.countries(this.countries)
.build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.haejwo.tripcometrue.domain.triprecord.dto.response;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.haejwo.tripcometrue.domain.triprecord.entity.ExpenseType;
import com.haejwo.tripcometrue.domain.triprecord.entity.TripRecord;
import java.time.LocalDate;
import lombok.Builder;

public record TripRecordResponseDto(
Long id,
String title,
String content,
Integer average_rating,
ExpenseType expenseType,
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") LocalDate tripStartDay,
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") LocalDate tripEndDay,
Integer totalDays,
String countries,
Integer viewCount

) {

@Builder
public TripRecordResponseDto(Long id, String title, String content, Integer average_rating,
ExpenseType expenseType, LocalDate tripStartDay, LocalDate tripEndDay, Integer totalDays,
String countries, Integer viewCount) {
this.id = id;
this.title = title;
this.content = content;
this.average_rating = average_rating;
this.expenseType = expenseType;
this.tripStartDay = tripStartDay;
this.tripEndDay = tripEndDay;
this.totalDays = totalDays;
this.countries = countries;
this.viewCount = viewCount;
}


public static TripRecordResponseDto fromEntity(TripRecord entity) {
return TripRecordResponseDto.builder()
.id(entity.getId())
.title(entity.getTitle())
.content(entity.getContent())
.average_rating(entity.getAverage_rating())
.expenseType(entity.getExpenseType())
.tripStartDay(entity.getTripStartDay())
.tripEndDay(entity.getTripEndDay())
.totalDays(entity.getTotalDays())
.countries(entity.getCountries())
.viewCount(entity.getViewCount())
.build();

}

}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

처음 ExpenseType을 봤을 때 지출 유형이라고 이해했어요. 지출 유형은 카드, 현금, 계좌이체 등을 먼저 떠올릴 수 있으니,
지출 범위라는 의미로 ExpenseRange가 어떤지 제안 드립니다 :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고려해보겠습니다.

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.haejwo.tripcometrue.domain.triprecord.entity;

import java.util.stream.Stream;

public enum ExpenseType {

BELOW_50(50),
BELOW_100(100),
BELOW_200(200),
BELOW_300(300),
ABOVE_300(Integer.MAX_VALUE);

private int max;

ExpenseType(int max) {
this.max = max;
}

public static ExpenseType findByMax(int max) {
return Stream.of(ExpenseType.values())
.filter(p -> p.max == max)
.findFirst()
.orElseThrow(); // TODO: ExpenseType 예외 추가
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package com.haejwo.tripcometrue.domain.triprecord.entity;

import com.haejwo.tripcometrue.domain.triprecord.dto.request.TripRecordRequestDto;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import jakarta.persistence.PrePersist;
import jakarta.persistence.PreUpdate;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.List;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class TripRecord {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "trip_record_id")
private Long id;

@Column(nullable = false)
private String title;
private String content;

private ExpenseType expenseType;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Enumerated 애노테이션이 빠진 것 같아요

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

추가하겠습니다.


private String countries;

private LocalDate tripStartDay;
private LocalDate tripEndDay;

private Integer totalDays;
private Integer average_rating;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

변수명에 카멜 케이스가 아닌 스네이크 케이스를 사용하신 이유가 있나요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이건 실수 같네요 수정하겠습니다.

private Integer viewCount;


@OneToMany(mappedBy = "tripRecord", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
private List<TripRecordSchedule> tripRecordSchedules;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

김영한님 말씀으론 관례적으로 바로 ArrayList를 만든다고 하네요.
private List<TripRecordSchedule> tripRecordSchedules = new ArrayList<>();

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네 적용하겠습니다


@Builder
public TripRecord(Long id, String title, String content, Integer average_rating,
ExpenseType expenseType, LocalDate tripStartDay, LocalDate tripEndDay, Integer totalDays,
String countries, Integer viewCount) {
this.id = id;
this.title = title;
this.content = content;
this.average_rating = average_rating;
this.expenseType = expenseType;
this.tripStartDay = tripStartDay;
this.tripEndDay = tripEndDay;
this.totalDays = totalDays;
this.countries = countries;
this.viewCount = viewCount;
}

// TODO: update 함수
public void update(TripRecordRequestDto requestDto) {
this.title = requestDto.title();
this.content = requestDto.content();
this.expenseType = requestDto.expenseType();
this.tripStartDay = requestDto.tripStartDay();
this.tripEndDay = requestDto.tripEndDay();
this.countries = requestDto.countries();
}

@PrePersist
public void prePersist() {
this.totalDays = calculateTotalDays();
}

@PreUpdate
public void preUpdate() {
this.totalDays = calculateTotalDays();
}

private int calculateTotalDays() {
if (this.tripStartDay == null || this.tripEndDay == null) {
return 0;
}
return (int) ChronoUnit.DAYS.between(this.tripStartDay, this.tripEndDay);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.haejwo.tripcometrue.domain.triprecord.entity;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class TripRecordSchedule {

@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "trip_record_schedule_id")
private Long id;

@Column(nullable = false)
private Integer dayNumber;

@Column(nullable = false)
private Integer ordering;

private String content;

@ManyToOne
@JoinColumn(name = "tripRecord_id")
private TripRecord tripRecord;

@Builder
public TripRecordSchedule(Long id, Integer dayNumber, Integer ordering, String content) {
this.id = id;
this.dayNumber = dayNumber;
this.ordering = ordering;
this.content = content;
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.haejwo.tripcometrue.domain.triprecord.repository;

import com.haejwo.tripcometrue.domain.triprecord.entity.TripRecord;
import org.springframework.data.jpa.repository.JpaRepository;

public interface TripRecordRepository extends JpaRepository<TripRecord, Long> {

}
Loading