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

Step3리뷰 요청드립니다. #501

Open
wants to merge 25 commits into
base: sang-eun
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
8ba6868
first commit
sang-eun Jul 7, 2022
2c883bb
[add] api 테스트 추가
sang-eun Jul 10, 2022
6e12f68
Merge branch 'sang-eun' into step1
sang-eun Jul 10, 2022
b4e2282
[add] 테스트 리스트
sang-eun Jul 10, 2022
5aed214
[add] create api 및 테스트 추가
sang-eun Jul 10, 2022
599824c
[modify] line 테스트 beforeAll 추가해서 자원공유하도록 수정
sang-eun Jul 10, 2022
325e5ab
[modify] step1 코드리뷰 반영
sang-eun Jul 12, 2022
2594684
[add] get api 테스트
sang-eun Jul 12, 2022
489e81a
[add] get lines api
sang-eun Jul 12, 2022
8beeb22
[add] get line api
sang-eun Jul 12, 2022
e094936
[add] update line api
sang-eun Jul 12, 2022
15ae19d
[add] delete line api
sang-eun Jul 12, 2022
3ecd092
[add] controllerAdvice 추가하려고 했으나 제대로 동작하지 않음. 캐치하고 204에러를 리턴해야 하는데, 계…
sang-eun Jul 12, 2022
3007872
Merge branch 'step1' of github.com:sang-eun/atdd-subway-map into step2
sang-eun Jul 12, 2022
47b5e09
[modify] 불필요한 부분 수정
sang-eun Jul 12, 2022
112e8c0
Merge remote-tracking branch 'upstream/sang-eun' into step2
sang-eun Jul 12, 2022
1d49dc3
코드리뷰 반영
sang-eun Jul 24, 2022
6181976
[add] 섹션 기반 코드 작성
sang-eun Jul 30, 2022
b992dd1
[add] 섹션 추가 api
sang-eun Jul 30, 2022
bcc0eb3
[modify] Line과 section 관계
sang-eun Jul 31, 2022
b5bb1d9
[add] 구간 삭제 테스트 추가
sang-eun Jul 31, 2022
4b11cea
[add] 구간 삭제 api 추가
sang-eun Jul 31, 2022
44a6707
[modify] section 저장 로직 수정
sang-eun Aug 1, 2022
b74b646
코드리뷰 반영
sang-eun Aug 6, 2022
33c993d
Merge branch 'sang-eun' of https://github.com/next-step/atdd-subway-m…
sang-eun Aug 6, 2022
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
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

// https://mvnrepository.com/artifact/org.projectlombok/lombok
compileOnly 'org.projectlombok:lombok:1.18.24'
annotationProcessor 'org.projectlombok:lombok:1.18.24'
implementation 'org.apache.commons:commons-text:1.9'

// log
implementation 'net.rakugakibox.spring.boot:logback-access-spring-boot-starter:2.7.1'

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package nextstep.subway.applicaion.common;

import com.sun.jdi.request.DuplicateRequestException;

import java.util.NoSuchElementException;

public class DuplicatedDownStationException extends DuplicateRequestException {

public DuplicatedDownStationException() {
super("하행역이 이미 존재하는 역입니다.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package nextstep.subway.applicaion.common;

import com.sun.jdi.request.DuplicateRequestException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.NoSuchElementException;

@RestControllerAdvice
public class ErrorController {

@ExceptionHandler(value = NoSuchElementException.class)
public ResponseEntity<ErrorResponse> noElementAdviser(NoSuchElementException e) {
return new ResponseEntity<>(new ErrorResponse(e.getMessage(), HttpStatus.NOT_FOUND), HttpStatus.NOT_FOUND);
}

@ExceptionHandler(value = {IllegalArgumentException.class, DuplicateRequestException.class})
public ResponseEntity<ErrorResponse> noElementAdviser(RuntimeException e) {
return new ResponseEntity<>(new ErrorResponse(e.getMessage(), HttpStatus.BAD_REQUEST), HttpStatus.BAD_REQUEST);
}
}
31 changes: 31 additions & 0 deletions src/main/java/nextstep/subway/applicaion/common/ErrorResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package nextstep.subway.applicaion.common;

import org.springframework.http.HttpStatus;

import java.time.LocalDateTime;

public class ErrorResponse {

private LocalDateTime timestamp = LocalDateTime.now();

private String message;

private HttpStatus status;

public LocalDateTime getTimestamp() {
return timestamp;
}

public String getMessage() {
return message;
}

public HttpStatus getStatus() {
return status;
}

public ErrorResponse(String message, HttpStatus status) {
this.message = message;
this.status = status;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package nextstep.subway.applicaion.common;

import java.util.NoSuchElementException;

public class LineNotFoundException extends NoSuchElementException {

public LineNotFoundException() {
super("해당 노선이 존재하지 않습니다.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package nextstep.subway.applicaion.common;

public class MinimumSectionException extends IllegalArgumentException {
public MinimumSectionException() {
super("해당 라인의 구간 갯수가 최소 갯수이므로 삭제가 불가능합니다.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package nextstep.subway.applicaion.common;

import java.util.NoSuchElementException;

public class SectionNotFoundException extends NoSuchElementException {

public SectionNotFoundException() {
super("해당 구간이 존재하지 않습니다.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package nextstep.subway.applicaion.common;

import java.util.NoSuchElementException;

public class StationNotFoundException extends NoSuchElementException {

public StationNotFoundException() {
super("해당 역이 존재하지 않습니다.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package nextstep.subway.applicaion.common;

public class UnappropriateStationException extends IllegalArgumentException {

public UnappropriateStationException() {
super("역이 해당 라인의 하행종점역이 아닙니다.");
}
}
70 changes: 70 additions & 0 deletions src/main/java/nextstep/subway/applicaion/line/LineService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package nextstep.subway.applicaion.line;

import nextstep.subway.applicaion.common.LineNotFoundException;
import nextstep.subway.applicaion.line.domain.Line;
import nextstep.subway.applicaion.line.domain.LineRepository;
import nextstep.subway.applicaion.line.dto.LineRequest;
import nextstep.subway.applicaion.line.dto.LineResponse;
import nextstep.subway.applicaion.line.dto.LineUpdateRequest;
import nextstep.subway.applicaion.section.SectionService;
import nextstep.subway.applicaion.station.StationService;
import nextstep.subway.applicaion.station.domain.Station;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

@Service
@Transactional(readOnly = true)
public class LineService {
private final LineRepository lineRepository;
private final StationService stationService;
private final SectionService sectionService;
public LineService(LineRepository lineRepository, StationService stationService, SectionService sectionService) {
this.lineRepository = lineRepository;
this.stationService = stationService;
this.sectionService = sectionService;
}
@Transactional
public LineResponse saveLine(LineRequest lineRequest) {
Station upStation = stationService.getStationThrowExceptionIfNotExists(lineRequest.getUpStationId());
Station downStation = stationService.getStationThrowExceptionIfNotExists(lineRequest.getDownStationId());

Line line = lineRepository.save(lineRequest.toLine());
line.getSections().initializeLine(line, upStation, downStation, lineRequest.getDistance());

return createLineResponse(line);
}

public List<LineResponse> findAllLines() {
List<Line> lines = lineRepository.findAll();

return lines.stream().map(this::createLineResponse).collect(Collectors.toList());
}

public LineResponse findLineById(Long id) {
Line line = lineRepository.findById(id).orElseThrow(LineNotFoundException::new);

return createLineResponse(line);
}

@Transactional
public void updateLineById(Long id, LineUpdateRequest lineRequest) {
Line line = lineRepository.findById(id).orElseThrow(LineNotFoundException::new);
line.updateLineInfo(lineRequest.getName(), lineRequest.getColor());
}

@Transactional
public void deleteLineById(Long id) {
lineRepository.deleteById(id);
}

private LineResponse createLineResponse(Line line) {
return new LineResponse(
line.getId(),
line.getName(),
line.getColor(),
line.getSections().stations().stream().map(stationService::createStationResponse).collect(Collectors.toList()));
}
}
30 changes: 30 additions & 0 deletions src/main/java/nextstep/subway/applicaion/line/domain/Line.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package nextstep.subway.applicaion.line.domain;

import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Getter
@Entity
@NoArgsConstructor
public class Line {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true)
private String name;
private String color;
@Embedded
private Sections sections = new Sections();

public Line(String name, String color) {
this.name = name;
this.color = color;
}

public void updateLineInfo(String name, String color) {
this.name = name;
this.color = color;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package nextstep.subway.applicaion.line.domain;

import org.springframework.data.jpa.repository.JpaRepository;

public interface LineRepository extends JpaRepository<Line, Long> {
}
101 changes: 101 additions & 0 deletions src/main/java/nextstep/subway/applicaion/line/domain/Sections.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package nextstep.subway.applicaion.line.domain;

import nextstep.subway.applicaion.common.DuplicatedDownStationException;
import nextstep.subway.applicaion.common.MinimumSectionException;
import nextstep.subway.applicaion.common.SectionNotFoundException;
import nextstep.subway.applicaion.common.UnappropriateStationException;
import nextstep.subway.applicaion.section.domain.Section;
import nextstep.subway.applicaion.station.domain.Station;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

@Embeddable
public class Sections {
private final Integer MINIMUM_SECTION_SIZE = 3;
@OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.PERSIST}, orphanRemoval = true)
@JoinColumn(name = "line_id")
private List<Section> sections = new ArrayList<>();

public void initializeLine(Line line, Station upStation, Station downStation, Integer distance) {
addFirstMark(line, upStation);
addLastMark(line, downStation);
this.add(line, upStation, downStation, distance);
}

public void addLast(Line line, Station upStation, Station downStation, Integer distance) {
this.add(line, upStation, downStation, distance);
addLastMark(line, downStation);
}

public void addFirst(Line line, Station upStation, Station downStation, Integer distance) {
this.add(line, upStation, downStation, distance);
addFirstMark(line, upStation);
}

public List<Station> stations() {
return sections.stream().map(it -> it.upStation).filter(Objects::nonNull).collect(Collectors.toList());
}

public void removeStation(Long stationId) {
if (sections.size() <= MINIMUM_SECTION_SIZE) {
throw new MinimumSectionException();
}

Section lastSectionMark = getLastSection().get();
if (!Objects.equals(lastSectionMark.upStation.getId(), stationId)) {
throw new UnappropriateStationException();
}
sections.remove(lastSectionMark);

Section lastSection = sections.stream().filter(it -> Objects.equals(it.downStation.getId(), stationId)).findFirst().get();
lastSection.downStation = null;
}

public void checkIsLastStation(Station upStation) {
if (!Objects.equals(getLastStation().getId(), upStation.getId())) {
throw new UnappropriateStationException();
}
}

public void checkIsNewStation(Station downStation) {
stations().forEach(it -> {
if (Objects.equals(it.getId(), downStation.getId())) {
throw new DuplicatedDownStationException();
}
});
}

private void addLastMark(Line line, Station downStation) {
getLastSection().ifPresent(it -> sections.remove(it));
Section lastSection = new Section(downStation, null, 0, line);
sections.add(lastSection);
}

private void addFirstMark(Line line, Station upStation) {
getFirstSection().ifPresent(it -> sections.remove(it));
Section firstSection = new Section(null, upStation, 0, line);
sang-eun marked this conversation as resolved.
Show resolved Hide resolved
sections.add(firstSection);
}

private Optional<Section> getLastSection() {
return sections.stream().filter(it -> it.downStation == null).findFirst();
}

private Optional<Section> getFirstSection() {
return sections.stream().filter(it -> it.upStation == null).findFirst();
}

private void add(Line line, Station upStation, Station downStation, Integer distance) {
Section section = new Section(upStation, downStation, distance, line);
sections.add(section);
}

private Station getLastStation() {
return getLastSection().orElseThrow(SectionNotFoundException::new).upStation;
}
}
43 changes: 43 additions & 0 deletions src/main/java/nextstep/subway/applicaion/line/dto/LineRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package nextstep.subway.applicaion.line.dto;

import nextstep.subway.applicaion.line.domain.Line;

public class LineRequest {
private String name;
private String color;
private Long upStationId;
private Long downStationId;
private Integer distance;

public Long getUpStationId() {
return upStationId;
}

public Long getDownStationId() {
return downStationId;
}

public String getName() {
return name;
}

public String getColor() {
return color;
}

public Integer getDistance() {
return distance;
}

public LineRequest(String name, String color, Long upStationId, Long downStationId, Integer distance) {
this.name = name;
this.color = color;
this.upStationId = upStationId;
this.downStationId = downStationId;
this.distance = distance;
}

public Line toLine() {
return new Line(name, color);
}
}
Loading