Skip to content

Commit

Permalink
feat: 시험장 관리 기능 추가 (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
gengminy committed Dec 5, 2023
1 parent a3424eb commit eb653d7
Show file tree
Hide file tree
Showing 10 changed files with 290 additions and 0 deletions.
7 changes: 7 additions & 0 deletions src/main/java/com/testcar/car/domains/track/Track.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,11 @@ public Track(String name, String location, String description, Double length) {
this.description = description;
this.length = length;
}

public void update(Track track) {
this.name = track.getName();
this.location = track.getLocation();
this.description = track.getDescription();
this.length = track.getLength();
}
}
68 changes: 68 additions & 0 deletions src/main/java/com/testcar/car/domains/track/TrackController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.testcar.car.domains.track;


import com.testcar.car.common.annotation.RoleAllowed;
import com.testcar.car.domains.member.Role;
import com.testcar.car.domains.track.model.RegisterTrackRequest;
import com.testcar.car.domains.track.model.TrackResponse;
import com.testcar.car.domains.track.model.vo.TrackFilterCondition;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.Valid;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/tracks")
@RequiredArgsConstructor
public class TrackController {
private final TrackService trackService;

@GetMapping
@RoleAllowed(role = Role.USER)
@Operation(summary = "[시험장 관리] 시험장 조회", description = "시험장을 조건에 맞게 조회합니다.")
public List<TrackResponse> getTracksByCondition(TrackFilterCondition condition) {
final List<Track> tracks = trackService.findAllByCondition(condition);
return tracks.stream().map(TrackResponse::from).toList();
}

@GetMapping("/{trackId}")
@RoleAllowed(role = Role.USER)
@Operation(summary = "[시험장 관리] 시험장 상세 정보", description = "시험장 상세 정보를 가져옵니다.")
public TrackResponse getTrackById(@PathVariable Long trackId) {
final Track track = trackService.findById(trackId);
return TrackResponse.from(track);
}

@PostMapping("/register")
@RoleAllowed(role = Role.ADMIN)
@Operation(summary = "[시험장 관리] 시험장 등록", description = "(관리자) 새로운 시험장을 등록합니다.")
public TrackResponse register(@Valid @RequestBody RegisterTrackRequest request) {
final Track track = trackService.register(request);
return TrackResponse.from(track);
}

@PatchMapping("/{trackId}")
@RoleAllowed(role = Role.ADMIN)
@Operation(summary = "[시험장 관리] 시험장 정보 수정", description = "(관리자) 시험장 정보를 수정합니다.")
public TrackResponse update(
@PathVariable Long trackId, @Valid @RequestBody RegisterTrackRequest request) {
final Track track = trackService.updateById(trackId, request);
return TrackResponse.from(track);
}

@DeleteMapping("/{trackId}")
@RoleAllowed(role = Role.ADMIN)
@Operation(summary = "[시험장 관리] 시험장 삭제", description = "(관리자) 시험장을 삭제합니다.")
public TrackResponse delete(@PathVariable Long trackId) {
final Track track = trackService.deleteById(trackId);
return TrackResponse.from(track);
}
}
66 changes: 66 additions & 0 deletions src/main/java/com/testcar/car/domains/track/TrackService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.testcar.car.domains.track;


import com.testcar.car.common.exception.BadRequestException;
import com.testcar.car.common.exception.NotFoundException;
import com.testcar.car.domains.track.exception.ErrorCode;
import com.testcar.car.domains.track.model.RegisterTrackRequest;
import com.testcar.car.domains.track.model.vo.TrackFilterCondition;
import com.testcar.car.domains.track.repository.TrackRepository;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
@RequiredArgsConstructor
public class TrackService {
private final TrackRepository trackRepository;

/** 시험장을 id로 조회합니다. */
public Track findById(Long id) {
return trackRepository
.findByIdAndDeletedFalse(id)
.orElseThrow(() -> new NotFoundException(ErrorCode.TRACK_NOT_FOUND));
}

/** 시험장을 id로 조회합니다. */
public List<Track> findAllByCondition(TrackFilterCondition condition) {
return trackRepository.findAllByCondition(condition);
}

/** 새로운 시험장을 등록합니다. */
public Track register(RegisterTrackRequest request) {
final Track car = createEntity(request);
return trackRepository.save(car);
}

/** 시험장 정보를 업데이트 합니다. */
public Track updateById(Long trackId, RegisterTrackRequest request) {
Track track = this.findById(trackId);
final Track updateTrack = this.createEntity(request);
track.update(updateTrack);
return trackRepository.save(track);
}

/** 시험장을 삭제 처리 합니다. (soft delete) */
public Track deleteById(Long trackId) {
final Track car = this.findById(trackId);
car.delete();
return trackRepository.save(car);
}

/** 영속되지 않은 시험장 엔티티를 생성합니다. */
private Track createEntity(RegisterTrackRequest request) {
validateNameNotDuplicated(request.getName());
return Track.builder().name(request.getName()).location(request.getLocation()).build();
}

/** 시험장명 중복을 검사합니다. */
private void validateNameNotDuplicated(String name) {
if (trackRepository.existsByNameAndDeletedFalse(name)) {
throw new BadRequestException(ErrorCode.DUPLICATED_TRACK_NAME);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.testcar.car.domains.track.exception;


import com.testcar.car.common.exception.BaseErrorCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public enum ErrorCode implements BaseErrorCode {
TRACK_NOT_FOUND("TRK001", "해당 시험장을 찾을 수 없습니다."),
DUPLICATED_TRACK_NAME("TRK002", "중복된 시험장명입니다."),
;
private final String code;
private final String message;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.testcar.car.domains.track.model;


import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import org.hibernate.validator.constraints.Length;

@Getter
public class RegisterTrackRequest {
@NotBlank
@Length(max = 20)
@Schema(description = "시험장명", example = "서산주행시험장")
private String name;

@NotBlank
@Schema(description = "위치", example = "충청남도 서산시 부석면")
private String location;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.testcar.car.domains.track.model;


import com.testcar.car.domains.track.Track;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class TrackResponse {
@Schema(description = "시험장 ID", example = "1")
private Long id;

@Schema(description = "시험명", example = "서산주행시험장")
private String name;

@Schema(description = "위치", example = "충청남도 서산시 부석면")
private String location;

@Schema(description = "경도", example = "132.123123")
private Double longitude;

@Schema(description = "위도", example = "37.123123")
private Double latitude;

@Schema(description = "시험장 특성", example = "평지")
private String description;

public static TrackResponse from(Track track) {
return TrackResponse.builder()
.id(track.getId())
.name(track.getName())
.location(track.getLocation())
.longitude(track.getLongitude())
.latitude(track.getLatitude())
.description(track.getDescription())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.testcar.car.domains.track.model.vo;


import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class TrackFilterCondition {
@Schema(description = "시험장명", example = "null")
private String name;

@Schema(description = "위치", example = "null")
private String location;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.testcar.car.domains.track.repository;


import com.testcar.car.domains.track.Track;
import com.testcar.car.domains.track.model.vo.TrackFilterCondition;
import java.util.List;

public interface TrackCustomRepository {
List<Track> findAllByCondition(TrackFilterCondition condition);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.testcar.car.domains.track.repository;

import static com.testcar.car.domains.track.QTrack.track;

import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.testcar.car.common.entity.BaseQueryDslRepository;
import com.testcar.car.domains.track.Track;
import com.testcar.car.domains.track.model.vo.TrackFilterCondition;
import java.util.List;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class TrackCustomRepositoryImpl implements TrackCustomRepository, BaseQueryDslRepository {
private final JPAQueryFactory jpaQueryFactory;

@Override
public List<Track> findAllByCondition(TrackFilterCondition condition) {
return jpaQueryFactory
.selectFrom(track)
.where(
notDeleted(track),
trackNameContainsOrNull(condition.getName()),
locationContainsOrNull(condition.getLocation()))
.orderBy(track.name.asc())
.fetch();
}

private BooleanExpression trackNameContainsOrNull(String name) {
return (name == null) ? null : track.name.contains(name);
}

private BooleanExpression locationContainsOrNull(String location) {
return (location == null) ? null : track.location.contains(location);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.testcar.car.domains.track.repository;


import com.testcar.car.domains.track.Track;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface TrackRepository extends JpaRepository<Track, Long>, TrackCustomRepository {
Optional<Track> findByIdAndDeletedFalse(Long id);

boolean existsByNameAndDeletedFalse(String name);
}

0 comments on commit eb653d7

Please sign in to comment.