Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion src/main/java/com/livable/server/entity/ParkingLog.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class ParkingLog extends BaseTimeEntity {
private Long id;

@JoinColumn(nullable = false, unique = true)
@ManyToOne(fetch = FetchType.LAZY)
@OneToOne(fetch = FetchType.LAZY)
private Visitor visitor;

@Column(nullable = false)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.livable.server.invitation.domain;

public interface InvitationValidationGuideMessage {

String INVALID_CAR_NUMBER = "차량 번호를 올바르게 입력해 주세요.";
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;

@RequiredArgsConstructor
@RestController
@RequestMapping("/api/visitation")
Expand All @@ -32,4 +34,15 @@ public ResponseEntity<ApiResponse.Success<Object>> validateQrCode(@RequestBody V

return ApiResponse.success(HttpStatus.OK);
}

@PostMapping("/parking")
public ResponseEntity<ApiResponse.Success<Object>> registerParking(
@RequestBody @Valid VisitationRequest.RegisterParkingDto registerParkingDto
) {
Long visitorId = 1L;
visitationFacadeService.registerParking(visitorId, registerParkingDto.getCarNumber());

return ApiResponse.success(HttpStatus.CREATED);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ public enum VisitationErrorCode implements ErrorCode {
IO(HttpStatus.INTERNAL_SERVER_ERROR, "I/O를 진행하는 과정에서 알 수 없는 에러가 발생했습니다."),
INVALID_QR_PERIOD(HttpStatus.BAD_REQUEST, "QR을 생성할 수 없는 시간입니다."),
INVALID_PERIOD(HttpStatus.BAD_REQUEST, "시작 및 종료시간이 올바르지 않습니다."),
NOT_FOUND(HttpStatus.BAD_REQUEST, "존재하지 않는 정보입니다.");
NOT_FOUND(HttpStatus.BAD_REQUEST, "존재하지 않는 정보입니다."),
ALREADY_REGISTER_PARKING(HttpStatus.BAD_REQUEST, "이미 주차 등록을 완료하였습니다.");


private final HttpStatus httpStatus;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
package com.livable.server.visitation.dto;

import com.livable.server.invitation.domain.InvitationValidationGuideMessage;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.validation.constraints.Pattern;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class VisitationRequest {

@Getter
public static class ValidateQrDto {
private String qr;
}

@Getter
public static class RegisterParkingDto {
@Pattern(regexp = "^\\d{2,3}[가-힣]{1}\\d{4}$", message = InvitationValidationGuideMessage.INVALID_CAR_NUMBER)
private String carNumber;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.livable.server.visitation.repository;

import com.livable.server.entity.ParkingLog;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.Optional;

public interface ParkingLogRepository extends JpaRepository<ParkingLog, Long> {

@Query("select p from ParkingLog p" +
" where p.visitor.id = :visitorId")
Optional<ParkingLog> findParkingLogByVisitorId(@Param("visitorId") Long visitorId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.livable.server.visitation.service;

import com.livable.server.entity.ParkingLog;
import com.livable.server.entity.Visitor;
import com.livable.server.visitation.repository.ParkingLogRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.Optional;

@RequiredArgsConstructor
@Service
public class ParkingLogService {

private final ParkingLogRepository parkingLogRepository;

public Optional<ParkingLog> findParkingLogByVisitorId(Long visitorId) {
return parkingLogRepository.findParkingLogByVisitorId(visitorId);
}

public void registerParkingLog(Visitor visitor, String carNumber) {
ParkingLog parkingLog = ParkingLog.builder()
.carNumber(carNumber)
.visitor(visitor)
.build();

parkingLogRepository.save(parkingLog);
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
package com.livable.server.visitation.service;

import com.livable.server.core.exception.GlobalRuntimeException;
import com.livable.server.entity.ParkingLog;
import com.livable.server.entity.Visitor;
import com.livable.server.invitation.service.InvitationService;
import com.livable.server.visitation.domain.VisitationErrorCode;
import com.livable.server.visitation.dto.VisitationResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Optional;

@RequiredArgsConstructor
@Service
@Transactional(readOnly = true)
public class VisitationFacadeService {

private final VisitationService visitationService;
private final InvitationService invitationService;
private final VisitorService visitorService;
private final ParkingLogService parkingLogService;

public String createQrCode(Long visitorId) {
VisitationResponse.InvitationTimeDto invitationTime = invitationService.findInvitationTime(visitorId);
Expand All @@ -23,4 +31,17 @@ public String createQrCode(Long visitorId) {
public void validateQrCode(String qr) {
visitationService.validateQrCode(qr);
}

@Transactional
public void registerParking(Long visitorId, String carNumber) {
validateDuplicationRegister(visitorId);
Visitor visitor = visitorService.findById(visitorId);
parkingLogService.registerParkingLog(visitor, carNumber);
}

private void validateDuplicationRegister(Long visitorId) {
if (parkingLogService.findParkingLogByVisitorId(visitorId).isPresent()) {
throw new GlobalRuntimeException(VisitationErrorCode.ALREADY_REGISTER_PARKING);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.livable.server.visitation.service;

import com.livable.server.core.exception.GlobalRuntimeException;
import com.livable.server.entity.Visitor;
import com.livable.server.visitation.domain.VisitationErrorCode;
import com.livable.server.visitation.repository.VisitorRepository;
import lombok.RequiredArgsConstructor;
Expand All @@ -18,4 +19,9 @@ public Long findInvitationId(Long visitorId) {
.getInvitation()
.getId();
}

public Visitor findById(Long visitorId) {
return visitorRepository.findById(visitorId)
.orElseThrow(() -> new GlobalRuntimeException(VisitationErrorCode.NOT_FOUND));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.livable.server.core.exception.GlobalRuntimeException;
import com.livable.server.visitation.domain.VisitationErrorCode;
import com.livable.server.visitation.dto.VisitationRequest;
import com.livable.server.visitation.mock.MockRegisterParkingDto;
import com.livable.server.visitation.mock.ValidateQrCodeSuccessMockRequest;
import com.livable.server.visitation.service.VisitationFacadeService;
import org.junit.jupiter.api.DisplayName;
Expand Down Expand Up @@ -103,4 +104,30 @@ void validateQrCodeFail() throws Exception {

then(visitationFacadeService).should(times(1)).validateQrCode(anyString());
}

@DisplayName("[POST][/api/visitation/parking] - 차량 등록 성공")
@Test
void registerParking() throws Exception {
// given
String carNumber = "12가1234";
MockRegisterParkingDto mockRegisterParkingDto = new MockRegisterParkingDto(carNumber);
Long visitorId = 1L;


willDoNothing().given(visitationFacadeService).registerParking(visitorId, mockRegisterParkingDto.getCarNumber());

// when
ResultActions resultActions = mockMvc.perform(
post("/api/visitation/parking")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(mockRegisterParkingDto))
);

// then
resultActions.andExpect(status().isCreated());

then(visitationFacadeService)
.should(times(1))
.registerParking(visitorId, mockRegisterParkingDto.getCarNumber());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.livable.server.visitation.dto;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

import java.util.Set;

import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
import static org.junit.jupiter.api.Assertions.*;

class VisitationRequestTest {

private static ValidatorFactory factory;
private static Validator validator;

private static ObjectMapper objectMapper = new ObjectMapper();

@BeforeAll
public static void init() {
factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
}

@DisplayName("VisitationRequest.RegisterParkingDto carNumber 정규식 성공 테스트")
@CsvSource({"12가1234", "12흫3456", "123핡0000"})
@ParameterizedTest(name = "[{index}] 차량번호: {0}")
void carNumberPatternSuccessTest(String carNumber) throws JsonProcessingException {
String requestDto = "{\n" +
" \"carNumber\": \"" + carNumber + "\"\n" +
" }";

VisitationRequest.RegisterParkingDto registerParkingDto =
objectMapper.readValue(requestDto, VisitationRequest.RegisterParkingDto.class);

Set<ConstraintViolation<VisitationRequest.RegisterParkingDto>> validate = validator.validate(registerParkingDto);

assertThat(validate.size()).isEqualTo(0);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.livable.server.visitation.mock;

import com.livable.server.visitation.dto.VisitationRequest;

public class MockRegisterParkingDto extends VisitationRequest.RegisterParkingDto {

private final String carNumber;

public MockRegisterParkingDto(String carNumber) {
this.carNumber = carNumber;
}

@Override
public String getCarNumber() {
return carNumber;
}
}
Loading