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

πŸš€ 3단계 - μš”κΈˆ μ •μ±… μΆ”κ°€ #488

Open
wants to merge 15 commits into
base: moonstal1506
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
9706b1b
docs: 2단계 ν”Όλ“œλ°± λ‚΄μš© μΆ”κ°€
moonstal1506 Apr 1, 2024
135c69a
refactor: μΆœλ°œμ§€/도착지 νŒŒλΌλ―Έν„°λ‘œ 전달 및 PathFinder SubwayMap둜 μ—­ν•  이동
moonstal1506 Apr 1, 2024
813d9bb
refactor: Fare μ›μ‹œκ°’ 포μž₯ 객체둜 λ³€κ²½
moonstal1506 Apr 1, 2024
6c0b7e4
docs: μš”κΈˆ μ •μ±… μΆ”κ°€ μš”κ΅¬μ‚¬ν•­ μž‘μ„±
moonstal1506 Apr 1, 2024
aee66b7
test: 노선별 μΆ”κ°€ μš”κΈˆ μΈμˆ˜ν…ŒμŠ€νŠΈ μž‘μ„±
moonstal1506 Apr 1, 2024
9e84b32
test: 노선별 μΆ”κ°€ μš”κΈˆ λ‹¨μœ„ν…ŒμŠ€νŠΈ μž‘μ„±
moonstal1506 Apr 1, 2024
1f25188
feat: 노선별 μΆ”κ°€ μš”κΈˆ κ΅¬ν˜„
moonstal1506 Apr 1, 2024
8f9d1d4
test: 둜그인 μ‚¬μš©μž 연령별 μš”κΈˆ 계산 μΈμˆ˜ν…ŒμŠ€νŠΈ μž‘μ„±
moonstal1506 Apr 3, 2024
3795fda
test: 연령별 μš”κΈˆ 할인 λ‹¨μœ„ν…ŒμŠ€νŠΈ μž‘μ„±
moonstal1506 Apr 3, 2024
8abc39d
feat: 연령별 μš”κΈˆ 할인 둜직 μž‘μ„±
moonstal1506 Apr 3, 2024
4835fd4
feat: 토큰에 λ‚˜μ΄ μΆ”κ°€
moonstal1506 Apr 3, 2024
95311ce
feat: 둜그인 여뢀에 따라 LoginMember 생성 둜직 뢄리
moonstal1506 Apr 3, 2024
ffb7dc5
feat: 둜그인 μ‚¬μš©μž 연령별 μš”κΈˆ 계산 둜직 μž‘μ„±
moonstal1506 Apr 3, 2024
2ae14b7
refactor: Line μƒμ„±μž ν•˜λ‚˜λ‘œ λ³€κ²½
moonstal1506 Apr 15, 2024
a6ed904
refactor: Path에 Sections μΆ”κ°€
moonstal1506 Apr 17, 2024
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
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,27 @@
- [x] 인수 ν…ŒμŠ€νŠΈλ₯Ό μΆ©μ‘±ν•˜λŠ” κΈ°λŠ₯ κ΅¬ν˜„

### πŸš€ 2단계 - μš”κΈˆ 쑰회
- [x] 경둜 쑰회 결과에 μš”κΈˆ 정보λ₯Ό 포함
- [x] 경둜 쑰회 결과에 μš”κΈˆ 정보λ₯Ό 포함
- [x] findPathλ©”μ„œλ“œ, calculateFare λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•  λ•Œλ§ˆλ‹€ μΆœλ°œμ§€/도착지 νŒŒλΌλ―Έν„°λ‘œ 전달
- [x] PathFinder의 역할을 SubwayMap이 가져가도둝 λ³€κ²½
- [x] μš”κΈˆμ„ μ›μ‹œκ°’(int) λŒ€μ‹  μ‚¬μš©ν•˜λŠ” 클래슀둜 λ³€κ²½
- κΈ°λ³Έμš”κΈˆ 제곡
- μš”κΈˆ λ§μ…ˆ κΈ°λŠ₯ 제곡
- 0보닀 μž‘μ„ 수 μ—†μŒμ„ 보μž₯

### πŸš€ 3단계 - μš”κΈˆ μ •μ±… μΆ”κ°€
- [x] 노선별 μΆ”κ°€ μš”κΈˆ
- [x] μΆ”κ°€ μš”κΈˆμ΄ μžˆλŠ” 노선을 이용 ν•  경우 μΈ‘μ •λœ μš”κΈˆμ— μΆ”κ°€
````
ex) 900원 μΆ”κ°€ μš”κΈˆμ΄ μžˆλŠ” λ…Έμ„  8km 이용 μ‹œ 1,250원 -> 2,150원
ex) 900원 μΆ”κ°€ μš”κΈˆμ΄ μžˆλŠ” λ…Έμ„  12km 이용 μ‹œ 1,350원 -> 2,250원
````
- [x] 경둜 쀑 μΆ”κ°€μš”κΈˆμ΄ μžˆλŠ” 노선을 ν™˜μŠΉ ν•˜μ—¬ 이용 ν•  경우 κ°€μž₯ 높은 κΈˆμ•‘μ˜ μΆ”κ°€ μš”κΈˆλ§Œ 적용
````
ex) 0원, 500원, 900μ›μ˜ μΆ”κ°€ μš”κΈˆμ΄ μžˆλŠ” 노선듀을 κ²½μœ ν•˜μ—¬ 8km 이용 μ‹œ 1,250원 -> 2,150원
````
- [x] 둜그인 μ‚¬μš©μžμ˜ 경우 연령별 μš”κΈˆμœΌλ‘œ 계산
````
μ²­μ†Œλ…„(13μ„Έ 이상~19μ„Έ 미만): μš΄μž„μ—μ„œ 350원을 κ³΅μ œν•œ κΈˆμ•‘μ˜ 20%할인
어린이(6μ„Έ 이상~13μ„Έ 미만): μš΄μž„μ—μ„œ 350원을 κ³΅μ œν•œ κΈˆμ•‘μ˜ 50%할인
````
11 changes: 8 additions & 3 deletions src/main/java/nextstep/auth/application/JwtTokenProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@

@Component
public class JwtTokenProvider {

private static final String AGE_KEY = "age";

@Value("${security.jwt.token.secret-key}")
private String secretKey;
@Value("${security.jwt.token.expire-length}")
private long validityInMilliseconds;

public String createToken(String principal) {
public String createToken(String principal, int age) {
Claims claims = Jwts.claims().setSubject(principal);
claims.put(AGE_KEY, age);
Date now = new Date();
Date validity = new Date(now.getTime() + validityInMilliseconds);

Expand All @@ -26,8 +30,9 @@ public String createToken(String principal) {
.compact();
}

public String getPrincipal(String token) {
return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
public TokenInfo getPrincipal(String token) {
Claims claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody();
return new TokenInfo(claims.getSubject(), claims.get(AGE_KEY, Integer.class));
}

public boolean validateToken(String token) {
Expand Down
21 changes: 21 additions & 0 deletions src/main/java/nextstep/auth/application/TokenInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package nextstep.auth.application;

public class TokenInfo {

private String email;
private Integer age;

public TokenInfo(String email, Integer age) {
this.email = email;
this.age = age;
}

public String getEmail() {
return email;
}

public Integer getAge() {
return age;
}
}

4 changes: 2 additions & 2 deletions src/main/java/nextstep/auth/application/TokenService.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public TokenResponse createToken(String email, String password) {
throw new AuthenticationException();
}

String token = jwtTokenProvider.createToken(user.getEmail());
String token = jwtTokenProvider.createToken(user.getEmail(), user.getAge());

return new TokenResponse(token);
}
Expand All @@ -34,7 +34,7 @@ public TokenResponse createTokenByGithubLogin(GithubLoginRequest request) {
GithubProfileResponse profileResponse = githubClient.requestGithubProfile(accessToken);

UserDetails user = userDetailsService.findOrCreateMember(profileResponse.getEmail(), profileResponse.getAge());
String token = jwtTokenProvider.createToken(user.getEmail());
String token = jwtTokenProvider.createToken(user.getEmail(), user.getAge());

return new TokenResponse(token);
}
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/nextstep/auth/application/UserDetails.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ public interface UserDetails {
String getPassword();

String getEmail();

Integer getAge();
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthenticationPrincipal {
boolean required() default true;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@

import nextstep.auth.AuthenticationException;
import nextstep.auth.application.JwtTokenProvider;
import nextstep.auth.application.TokenInfo;
import nextstep.member.domain.LoginMember;
import org.springframework.core.MethodParameter;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import java.util.Optional;

public class AuthenticationPrincipalArgumentResolver implements HandlerMethodArgumentResolver {

public static final String AUTH_PREFIX = "Bearer ";
private static final String AUTHORIZATION = "Authorization";
private static final String BEARER = "bearer";
private static final String SPACE = " ";

private JwtTokenProvider jwtTokenProvider;

public AuthenticationPrincipalArgumentResolver(JwtTokenProvider jwtTokenProvider) {
Expand All @@ -29,13 +31,24 @@ public boolean supportsParameter(MethodParameter parameter) {
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
String authorization = webRequest.getHeader(AUTHORIZATION);
if (authorization == null || authorization.isEmpty() || !BEARER.equalsIgnoreCase(authorization.split(SPACE)[0])) {

if (StringUtils.hasLength(authorization) && authorization.startsWith(AUTH_PREFIX)) {
String token = authorization.substring(AUTH_PREFIX.length());
TokenInfo tokenInfo = jwtTokenProvider.getPrincipal(token);
return new LoginMember(tokenInfo.getEmail(), tokenInfo.getAge());
}

if (isAuthRequired(parameter)) {
throw new AuthenticationException();
}
String token = authorization.split(SPACE)[1];

String email = jwtTokenProvider.getPrincipal(token);
return new LoginMember(null, null);
}

return new LoginMember(email);
private boolean isAuthRequired(final MethodParameter parameter) {
final AuthenticationPrincipal annotation = parameter.getParameterAnnotation(AuthenticationPrincipal.class);
return Optional.ofNullable(annotation)
.map(AuthenticationPrincipal::required)
.orElse(true);
}
}
6 changes: 6 additions & 0 deletions src/main/java/nextstep/exception/GlobalExceptionHandler.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package nextstep.exception;

import nextstep.path.fare.NegativeNumberException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
Expand All @@ -12,4 +13,9 @@ public class GlobalExceptionHandler {
protected ResponseEntity<ExceptionResponse> handleApplication(SubwayException ex) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ExceptionResponse(ex.getMessage()));
}

@ExceptionHandler(NegativeNumberException.class)
protected ResponseEntity<ExceptionResponse> handleApplication(NegativeNumberException ex) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ExceptionResponse(ex.getMessage()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
import nextstep.member.domain.LoginMember;
import nextstep.member.domain.Member;
import nextstep.member.domain.MemberRepository;
import nextstep.path.Path;
import nextstep.path.PathFinder;
import nextstep.path.SubwayMap;
import nextstep.station.Station;
import nextstep.station.StationRepository;
import org.springframework.stereotype.Service;
Expand Down Expand Up @@ -40,7 +39,7 @@ public Long createFavorite(LoginMember loginMember, FavoriteRequest request) {
Station targetStation = stationRepository.findById(request.getTarget()).orElseThrow(() -> new SubwayException("μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” μ—­μž…λ‹ˆλ‹€."));
List<Line> lines = lineRepository.findAll();

PathFinder pathFinder = new PathFinder(lines);
SubwayMap pathFinder = new SubwayMap(lines);
pathFinder.isValidateRoute(sourceStation, targetStation);

Favorite favorite = new Favorite(sourceStation, targetStation, member);
Expand Down
16 changes: 15 additions & 1 deletion src/main/java/nextstep/line/Line.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,19 @@ public class Line {
@Column(length = 20, nullable = false)
private String color;

@Column(nullable = false)
private int extraFare;

@Embedded
private Sections sections = new Sections();

public Line() {
}

public Line(String name, String color, Section section) {
public Line(String name, String color, int extraFare, Section section) {
this.name = name;
this.color = color;
this.extraFare = extraFare;
sections.addSection(section);
}

Expand Down Expand Up @@ -57,6 +61,10 @@ public String getColor() {
return color;
}

public int getExtraFare() {
return extraFare;
}

public List<Section> getSections() {
return sections.getSections();
}
Expand All @@ -65,4 +73,10 @@ public List<Station> getOrderedStations() {
return sections.getOrderedStations();
}

@Override
public String toString() {
return "Line{" +
", name='" + name + '\'' +
'}';
}
}
5 changes: 5 additions & 0 deletions src/main/java/nextstep/line/LineRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public class LineRequest {
private Long downStationId;
private int distance;
private int duration;
private int extraFare;

public String getName() {
return name;
Expand All @@ -33,4 +34,8 @@ public int getDuration() {
return duration;
}

public int getExtraFare() {
return extraFare;
}

}
2 changes: 1 addition & 1 deletion src/main/java/nextstep/line/LineService.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public LineResponse saveLine(LineRequest lineRequest) {
.orElseThrow(EntityNotFoundException::new);

Section section = new Section(upStation, downStation, lineRequest.getDistance(), lineRequest.getDuration());
Line line = new Line(lineRequest.getName(), lineRequest.getColor(), section);
Line line = new Line(lineRequest.getName(), lineRequest.getColor(), lineRequest.getExtraFare(), section);
Line savedLine = lineRepository.save(line);

return new LineResponse(savedLine);
Expand Down
8 changes: 7 additions & 1 deletion src/main/java/nextstep/member/domain/LoginMember.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@

public class LoginMember {
private String email;
private Integer age;

public LoginMember(String email) {
public LoginMember(String email, Integer age) {
this.email = email;
this.age = age;
}

public String getEmail() {
return email;
}

public Integer getAge() {
return age;
}
}
1 change: 1 addition & 0 deletions src/main/java/nextstep/member/domain/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public String getPassword() {
return password;
}

@Override
public Integer getAge() {
return age;
}
Expand Down
17 changes: 10 additions & 7 deletions src/main/java/nextstep/path/Path.java
Original file line number Diff line number Diff line change
@@ -1,31 +1,34 @@
package nextstep.path;

import nextstep.line.Line;
import nextstep.station.Station;

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

public class Path {

private List<Station> stations;
private int distance;
private int duration;
private List<PathSection> pathSections;

public Path(List<Station> stations, int distance, int duration) {
public Path(List<Station> stations, List<PathSection> pathSections) {
this.stations = stations;
this.distance = distance;
this.duration = duration;
this.pathSections = pathSections;
}
Comment on lines 11 to 17
Copy link
Author

@moonstal1506 moonstal1506 Apr 17, 2024

Choose a reason for hiding this comment

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

stations λŒ€μ‹  sections(ꡬ간) 좔가에 λŒ€ν•œ ν”Όλ“œλ°±μ„ λ°›κ³ 

  1. μƒμ„±μžλ‘œ pathSections만 λ°›μ•„μ„œ 응닡값(PathResponse)에 ν•„μš”ν•œgetStations()λ₯Ό κ΅¬ν˜„ν•˜λŠ” 방법과
  2. SubwayMap의 findPathμ—μ„œ path.getVertexList()λ₯Ό ν•˜λ©΄ stationsλ₯Ό λ°”λ‘œ 얻을 수 μžˆλ‹€λŠ” μ μ—μ„œ stations와 pathSectionsλ₯Ό λ°›λŠ” 방법이 κ³ λ―Όλ˜μ—ˆμŠ΅λ‹ˆλ‹€!
public Path findPath(Station source, Station target, PathType pathType) {
    GraphPath<Station, PathSection> path = getGraphPath(source, target, pathType);
    return new Path(path.getVertexList(), path.getEdgeList());
}

1λ²ˆμ€ stations κ΅¬ν•˜λŠ” λ‘œμ§μ„ ν•œλ²ˆ 더 μž‘μ„±ν•΄μ•Ό ν•œλ‹€λŠ” 점,
2λ²ˆμ€ pathSectionλ‘œλ„ κ΅¬ν˜„ κ°€λŠ₯ν•œλ° stationsλ₯Ό 또 λ°›λŠ”λ‹€λŠ” μ μ—μ„œ
μ–΄λ–€ 방식을 κ³ λ₯΄λ©΄ μ’‹μ„κΉŒμš”πŸ€”


public List<Station> getStations() {
return stations;
}

public int getDistance() {
return distance;
return pathSections.stream().mapToInt(PathSection::getDistance).sum();
}

public int getDuration() {
return duration;
return pathSections.stream().mapToInt(PathSection::getDuration).sum();
}

public List<Line> getUsedLine() {
return pathSections.stream().map(PathSection::getLine).distinct().collect(Collectors.toList());
}
}
7 changes: 5 additions & 2 deletions src/main/java/nextstep/path/PathController.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package nextstep.path;

import nextstep.auth.ui.AuthenticationPrincipal;
import nextstep.member.domain.LoginMember;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
Expand All @@ -15,7 +17,8 @@ public PathController(PathService pathService) {
}

@GetMapping(value = "/paths")
public ResponseEntity<PathResponse> getPaths(@RequestParam Long source, @RequestParam Long target, @RequestParam PathType type) {
return ResponseEntity.ok().body(pathService.findPath(source, target, type));
public ResponseEntity<PathResponse> getPaths(@RequestParam Long source, @RequestParam Long target, @RequestParam PathType type,
@AuthenticationPrincipal(required = false) LoginMember loginMember) {
return ResponseEntity.ok().body(pathService.findPath(source, target, type, loginMember.getAge()));
}
}
Loading