diff --git a/src/main/java/com/fullcar/core/response/ErrorCode.java b/src/main/java/com/fullcar/core/response/ErrorCode.java index a7f5517..5e9cc23 100644 --- a/src/main/java/com/fullcar/core/response/ErrorCode.java +++ b/src/main/java/com/fullcar/core/response/ErrorCode.java @@ -17,6 +17,7 @@ public enum ErrorCode { CANNOT_SEND_TO_OWN_CARPOOL(BAD_REQUEST, "자기자신의 카풀에는 신청할 수 없습니다."), DUPLICATED_FORM(BAD_REQUEST, "이미 요청을 보낸 카풀입니다."), DUPLICATED_NICKNAME(BAD_REQUEST, "중복된 닉네임 입니다."), + EXISTED_CAR_IN_MEMBER(BAD_REQUEST, "이미 차량이 등록되었습니다."), /* 401 UNAUTHORIZED */ UNAUTHORIZED_KAKAO_TOKEN(UNAUTHORIZED, "유효하지 않은 카카오 토큰"), diff --git a/src/main/java/com/fullcar/core/response/SuccessCode.java b/src/main/java/com/fullcar/core/response/SuccessCode.java index 1c1a4e7..7f2e414 100644 --- a/src/main/java/com/fullcar/core/response/SuccessCode.java +++ b/src/main/java/com/fullcar/core/response/SuccessCode.java @@ -17,7 +17,8 @@ public enum SuccessCode { READ_SUCCESS(OK, "조회 성공"), EMAIL_SENT_SUCCESS(OK, "인증메일 발송 성공"), LOGOUT_SUCCESS(OK, "로그아웃 성공"), - AVAILABLE_NICKNAME(OK, "사용 가능한 닉네임"); + AVAILABLE_NICKNAME(OK, "사용 가능한 닉네임"), + UPDATE_SUCCESS(OK, "수정 성공"); private final HttpStatus status; private final String message; diff --git a/src/main/java/com/fullcar/member/application/car/CarMapper.java b/src/main/java/com/fullcar/member/application/car/CarMapper.java index 0c59333..f493038 100644 --- a/src/main/java/com/fullcar/member/application/car/CarMapper.java +++ b/src/main/java/com/fullcar/member/application/car/CarMapper.java @@ -2,7 +2,8 @@ import com.fullcar.member.domain.car.Car; import com.fullcar.member.domain.car.service.CarIdService; -import com.fullcar.member.presentation.car.dto.CarDto; +import com.fullcar.member.presentation.car.dto.request.CarRequestDto; +import com.fullcar.member.presentation.car.dto.response.CarResponseDto; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; @@ -12,8 +13,8 @@ public class CarMapper { private final CarIdService carIdService; - public CarDto toDto(Car car) { - return CarDto.builder() + public CarResponseDto toDto(Car car) { + return CarResponseDto.builder() .id(car.getCarId().getId()) .carNo(car.getCarNo()) .carBrand(car.getCarBrand()) @@ -22,13 +23,13 @@ public CarDto toDto(Car car) { .build(); } - public Car toEntity(CarDto carDto) { + public Car toEntity(CarRequestDto carRequestDto) { return Car.builder() .carId(carIdService.nextId()) - .carNo(carDto.getCarNo()) - .carBrand(carDto.getCarBrand()) - .carName(carDto.getCarName()) - .carColor(carDto.getCarColor()) + .carNo(carRequestDto.getCarNo()) + .carBrand(carRequestDto.getCarBrand()) + .carName(carRequestDto.getCarName()) + .carColor(carRequestDto.getCarColor()) .build(); } } diff --git a/src/main/java/com/fullcar/member/application/car/CarService.java b/src/main/java/com/fullcar/member/application/car/CarService.java index efeb2e1..2cf93b7 100644 --- a/src/main/java/com/fullcar/member/application/car/CarService.java +++ b/src/main/java/com/fullcar/member/application/car/CarService.java @@ -1,11 +1,13 @@ package com.fullcar.member.application.car; +import com.fullcar.core.exception.CustomException; +import com.fullcar.core.response.ErrorCode; import com.fullcar.member.domain.car.Car; import com.fullcar.member.domain.car.CarRepository; -import com.fullcar.member.presentation.car.dto.CarDto; import com.fullcar.member.domain.member.Member; -import com.fullcar.member.domain.member.MemberId; import com.fullcar.member.domain.member.MemberRepository; +import com.fullcar.member.presentation.car.dto.request.CarRequestDto; +import com.fullcar.member.presentation.car.dto.response.CarResponseDto; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -21,11 +23,14 @@ public class CarService { private final CarMapper carMapper; @Transactional - public CarDto registerCar(MemberId memberId, CarDto carDto) { - Car car = carMapper.toEntity(carDto); + public CarResponseDto registerCar(Member member, CarRequestDto carRequestDto) { + Car car = carMapper.toEntity(carRequestDto); - Member member = memberRepository.findByIdAndIsDeletedOrThrow(memberId, false); - member.updateCarInformation(car.getCarId()); + if (memberRepository.findByIdAndIsDeletedOrThrow(member.getId(), false).getCarId() != null) { + throw new CustomException(ErrorCode.EXISTED_CAR_IN_MEMBER); + } + + memberRepository.saveAndFlush(member.addCarInformation(car.getCarId())); return carMapper.toDto( carRepository.saveAndFlush(car) @@ -33,8 +38,15 @@ public CarDto registerCar(MemberId memberId, CarDto carDto) { } @Transactional(readOnly = true) - public CarDto getCar(Member member) { + public CarResponseDto getCar(Member member) { Car car = carRepository.findByCarIdAndIsDeletedOrThrow(member.getCarId(), false); return carMapper.toDto(car); } + + @Transactional + public void updateCar(Member member, CarRequestDto carRequestDto) { + Car car = carRepository.findByCarIdAndIsDeletedOrThrow(member.getCarId(), false); + Car updatedCar = car.updateCar(carRequestDto); + carRepository.saveAndFlush(updatedCar); + } } diff --git a/src/main/java/com/fullcar/member/domain/car/Car.java b/src/main/java/com/fullcar/member/domain/car/Car.java index 7b3d2fd..07ec39d 100644 --- a/src/main/java/com/fullcar/member/domain/car/Car.java +++ b/src/main/java/com/fullcar/member/domain/car/Car.java @@ -1,5 +1,6 @@ package com.fullcar.member.domain.car; +import com.fullcar.member.presentation.car.dto.request.CarRequestDto; import jakarta.persistence.Column; import jakarta.persistence.EmbeddedId; import jakarta.persistence.Entity; @@ -50,4 +51,12 @@ public class Car { @Column(name = "updated_at") @LastModifiedDate private LocalDateTime updatedAt; + + public Car updateCar(CarRequestDto carUpdateRequestDto) { + this.carNo = carUpdateRequestDto.getCarNo(); + this.carName = carUpdateRequestDto.getCarName(); + this.carBrand = carUpdateRequestDto.getCarBrand(); + this.carColor = carUpdateRequestDto.getCarColor(); + return this; + } } diff --git a/src/main/java/com/fullcar/member/domain/member/Member.java b/src/main/java/com/fullcar/member/domain/member/Member.java index 6aa2797..a7c648e 100644 --- a/src/main/java/com/fullcar/member/domain/member/Member.java +++ b/src/main/java/com/fullcar/member/domain/member/Member.java @@ -69,8 +69,9 @@ public void loginMember(String deviceToken, String refreshToken) { this.refreshToken = refreshToken; } - public void updateCarInformation(CarId carId) { + public Member addCarInformation(CarId carId) { this.carId = carId; + return this; } public void saveOnBoardingInfo(Member member) { diff --git a/src/main/java/com/fullcar/member/domain/member/MemberRepository.java b/src/main/java/com/fullcar/member/domain/member/MemberRepository.java index 794daa9..8ed368a 100644 --- a/src/main/java/com/fullcar/member/domain/member/MemberRepository.java +++ b/src/main/java/com/fullcar/member/domain/member/MemberRepository.java @@ -4,6 +4,7 @@ import com.fullcar.core.exception.UnauthorizedException; import com.fullcar.core.response.ErrorCode; import com.fullcar.member.domain.auth.SocialId; +import com.fullcar.member.domain.car.CarId; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @@ -24,6 +25,8 @@ public interface MemberRepository extends JpaRepository { boolean existsByNickname(String nickname); + Optional findByCarId(CarId carId); + default Member findByIdAndIsDeletedOrThrow(MemberId id, boolean isDeleted) { return findByIdAndIsDeleted(id, isDeleted) .orElseThrow(() -> new CustomException(ErrorCode.NOT_EXIST_USER)); @@ -33,4 +36,9 @@ default Member findByRefreshTokenOrThrow(String refreshToken) { return findByRefreshToken(refreshToken) .orElseThrow(() -> new UnauthorizedException(ErrorCode.INVALID_MEMBER)); } + + default void existsByCarIdOrThrow(CarId carId) { + findByCarId(carId) + .orElseThrow(() -> new CustomException(ErrorCode.EXISTED_CAR_IN_MEMBER)); + } } diff --git a/src/main/java/com/fullcar/member/presentation/car/CarController.java b/src/main/java/com/fullcar/member/presentation/car/CarController.java index eff4947..f5a519e 100644 --- a/src/main/java/com/fullcar/member/presentation/car/CarController.java +++ b/src/main/java/com/fullcar/member/presentation/car/CarController.java @@ -1,11 +1,12 @@ package com.fullcar.member.presentation.car; import com.fullcar.member.application.car.CarService; -import com.fullcar.member.presentation.car.dto.CarDto; import com.fullcar.core.annotation.CurrentMember; import com.fullcar.core.response.ApiResponse; import com.fullcar.core.response.SuccessCode; import com.fullcar.member.domain.member.Member; +import com.fullcar.member.presentation.car.dto.request.CarRequestDto; +import com.fullcar.member.presentation.car.dto.response.CarResponseDto; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.responses.ApiResponses; @@ -24,11 +25,12 @@ public class CarController { @Operation(summary = "차량 등록 API") @ApiResponses({ @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "201", description = "등록 성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "이미 차량이 등록되었습니다.", content = @Content), @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "서버 내부 오류", content = @Content) }) @PostMapping() - public ApiResponse postCar(@CurrentMember Member member, @RequestBody @Valid CarDto carDto) { - CarDto responseDto = carService.registerCar(member.getId(), carDto); + public ApiResponse postCar(@CurrentMember Member member, @RequestBody @Valid CarRequestDto carRequestDto) { + CarResponseDto responseDto = carService.registerCar(member, carRequestDto); return ApiResponse.success(SuccessCode.REGISTER_SUCCESS, responseDto); } @@ -38,8 +40,19 @@ public ApiResponse postCar(@CurrentMember Member member, @RequestBody @V @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "서버 내부 오류", content = @Content) }) @GetMapping() - public ApiResponse getCar(@CurrentMember Member member) { - CarDto responseDto = carService.getCar(member); + public ApiResponse getCar(@CurrentMember Member member) { + CarResponseDto responseDto = carService.getCar(member); return ApiResponse.success(SuccessCode.READ_SUCCESS, responseDto); } + + @Operation(summary = "차량 정보 수정 API") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "수정 성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "서버 내부 오류", content = @Content) + }) + @PatchMapping() + public ApiResponse updateCar(@CurrentMember Member member, @RequestBody @Valid CarRequestDto carRequestDto) { + carService.updateCar(member, carRequestDto); + return ApiResponse.success(SuccessCode.UPDATE_SUCCESS); + } } diff --git a/src/main/java/com/fullcar/member/presentation/car/dto/CarDto.java b/src/main/java/com/fullcar/member/presentation/car/dto/request/CarRequestDto.java similarity index 67% rename from src/main/java/com/fullcar/member/presentation/car/dto/CarDto.java rename to src/main/java/com/fullcar/member/presentation/car/dto/request/CarRequestDto.java index 4e27d5b..f38274b 100644 --- a/src/main/java/com/fullcar/member/presentation/car/dto/CarDto.java +++ b/src/main/java/com/fullcar/member/presentation/car/dto/request/CarRequestDto.java @@ -1,30 +1,23 @@ -package com.fullcar.member.presentation.car.dto; +package com.fullcar.member.presentation.car.dto.request; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; import lombok.*; @Getter @Builder @AllArgsConstructor(access = AccessLevel.PROTECTED) @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class CarDto { - @Schema(description = "차량 id") - private Long id; +public class CarRequestDto { @Schema(description = "차량번호", example = "23루 1234") - @NotBlank private String carNo; @Schema(description = "차량명", example = "펠리세이드") - @NotBlank private String carName; @Schema(description = "브랜드", example = "현대") - @NotBlank private String carBrand; @Schema(description = "색상", example = "화이트") - @NotBlank private String carColor; } diff --git a/src/main/java/com/fullcar/member/presentation/car/dto/response/CarResponseDto.java b/src/main/java/com/fullcar/member/presentation/car/dto/response/CarResponseDto.java new file mode 100644 index 0000000..2b150e7 --- /dev/null +++ b/src/main/java/com/fullcar/member/presentation/car/dto/response/CarResponseDto.java @@ -0,0 +1,30 @@ +package com.fullcar.member.presentation.car.dto.response; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +@Getter +@SuperBuilder +@AllArgsConstructor(access = AccessLevel.PROTECTED) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Schema(description = "차량 응답 모델") +public class CarResponseDto { + @Schema(description = "차량 id") + private Long id; + + @Schema(description = "차량번호", example = "23루 1234") + private String carNo; + + @Schema(description = "차량명", example = "펠리세이드") + private String carName; + + @Schema(description = "브랜드", example = "현대") + private String carBrand; + + @Schema(description = "색상", example = "화이트") + private String carColor; +}