Skip to content

Commit

Permalink
Merge pull request #16 from woowa-techcamp-2024/feature/4_kimhyun5u_구…
Browse files Browse the repository at this point in the history
…매자_회원가입

구매자 회원가입
  • Loading branch information
kimhyun5u authored Aug 13, 2024
2 parents 2b44166 + b68368a commit 8c3c80d
Show file tree
Hide file tree
Showing 22 changed files with 910 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@ public class BadRequestException extends HttpStatusException {
public BadRequestException(ErrorCode errorCode) {
super(errorCode);
}

public BadRequestException(ErrorCode errorCode, String message) {
super(errorCode, message);
}
}
Empty file.
27 changes: 27 additions & 0 deletions src/main/java/camp/woowak/lab/customer/domain/Customer.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,45 @@
package camp.woowak.lab.customer.domain;

import camp.woowak.lab.customer.exception.InvalidCreationException;
import camp.woowak.lab.payaccount.domain.PayAccount;
import camp.woowak.lab.web.authentication.PasswordEncoder;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToOne;
import lombok.Getter;

@Entity
@Getter
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 50)
private String name;
@Column(unique = true, nullable = false, length = 100)
private String email;
@Column(nullable = false, length = 30)
private String password;
@Column(nullable = false, length = 30)
private String phone;
@OneToOne(fetch = FetchType.LAZY)
private PayAccount payAccount;

public Customer() {
}

public Customer(String name, String email, String password, String phone, PayAccount payAccount,
PasswordEncoder passwordEncoder) throws
InvalidCreationException {
CustomerValidator.validateCreation(name, email, password, phone, payAccount);
this.name = name;
this.email = email;
this.password = passwordEncoder.encode(password);
this.phone = phone;
this.payAccount = payAccount;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package camp.woowak.lab.customer.domain;

import camp.woowak.lab.customer.exception.CustomerErrorCode;
import camp.woowak.lab.customer.exception.InvalidCreationException;
import camp.woowak.lab.payaccount.domain.PayAccount;

public class CustomerValidator {
private static final int MAX_NAME_LENGTH = 50;
private static final int MAX_EMAIL_LENGTH = 100;
private static final int MIN_PASSWORD_LENGTH = 8;
private static final int MAX_PASSWORD_LENGTH = 30;
private static final int MAX_PHONE_LENGTH = 30;

private CustomerValidator() {
}

public static void validateCreation(String name, String email, String password, String phone,
PayAccount payAccount) throws InvalidCreationException {
validateName(name);
validateEmail(email);
validatePassword(password);
validatePhone(phone);
validatePayAccount(payAccount);
}

public static void validateName(String name) throws InvalidCreationException {
if (name == null || name.isBlank()) {
throw new InvalidCreationException(CustomerErrorCode.INVALID_CREATION, "Customer name cannot be blank");
}
if (name.length() > MAX_NAME_LENGTH) {
throw new InvalidCreationException(CustomerErrorCode.INVALID_CREATION,
"Customer name cannot be longer than 50 characters");
}
}

public static void validateEmail(String email) throws InvalidCreationException {
if (email == null || email.isBlank()) {
throw new InvalidCreationException(CustomerErrorCode.INVALID_CREATION, "Customer email cannot be blank");
}
if (email.trim().length() > MAX_EMAIL_LENGTH) {
throw new InvalidCreationException(CustomerErrorCode.INVALID_CREATION,
"Customer email cannot be longer than 100 characters");
}
}

public static void validatePassword(String password) throws InvalidCreationException {
if (password == null || password.isBlank()) {
throw new InvalidCreationException(CustomerErrorCode.INVALID_CREATION, "Customer password cannot be blank");
}
if (password.trim().length() < MIN_PASSWORD_LENGTH) {
throw new InvalidCreationException(CustomerErrorCode.INVALID_CREATION,
"Customer password cannot be shorter than 8 characters");
}
if (password.trim().length() > MAX_PASSWORD_LENGTH) {
throw new InvalidCreationException(CustomerErrorCode.INVALID_CREATION,
"Customer password cannot be longer than 30 characters");
}
}

public static void validatePhone(String phone) throws InvalidCreationException {
if (phone == null || phone.isBlank()) {
throw new InvalidCreationException(CustomerErrorCode.INVALID_CREATION, "Customer phone cannot be blank");
}
if (phone.trim().length() > MAX_PHONE_LENGTH) {
throw new InvalidCreationException(CustomerErrorCode.INVALID_CREATION,
"Customer phone cannot be longer than 30 characters");
}
}

public static void validatePayAccount(PayAccount payAccount) throws InvalidCreationException {
if (payAccount == null) {
throw new InvalidCreationException(CustomerErrorCode.INVALID_CREATION,
"Customer payAccount cannot be null");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package camp.woowak.lab.customer.exception;

import camp.woowak.lab.common.exception.ErrorCode;

public enum CustomerErrorCode implements ErrorCode {
INVALID_CREATION(400, "C1", "잘못된 요청입니다."),
DUPLICATE_EMAIL(400, "C2", "이미 존재하는 이메일입니다.");

private final int status;
private final String errorCode;
private final String message;

CustomerErrorCode(int status, String errorCode, String message) {
this.status = status;
this.errorCode = errorCode;
this.message = message;
}

@Override
public int getStatus() {
return status;
}

@Override
public String getErrorCode() {
return errorCode;
}

@Override
public String getMessage() {
return message;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package camp.woowak.lab.customer.exception;

import camp.woowak.lab.common.exception.BadRequestException;

public class DuplicateEmailException extends BadRequestException {
public DuplicateEmailException() {
super(CustomerErrorCode.DUPLICATE_EMAIL);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package camp.woowak.lab.customer.exception;

import camp.woowak.lab.common.exception.BadRequestException;
import camp.woowak.lab.common.exception.ErrorCode;

public class InvalidCreationException extends BadRequestException {
public InvalidCreationException(ErrorCode errorCode, String message) {
super(errorCode, message);
}
}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package camp.woowak.lab.customer.repository;

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

import camp.woowak.lab.customer.domain.Customer;

public interface CustomerRepository extends JpaRepository<Customer, Long> {
}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package camp.woowak.lab.customer.service;

import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;

import camp.woowak.lab.customer.domain.Customer;
import camp.woowak.lab.customer.exception.DuplicateEmailException;
import camp.woowak.lab.customer.exception.InvalidCreationException;
import camp.woowak.lab.customer.repository.CustomerRepository;
import camp.woowak.lab.customer.service.command.SignUpCustomerCommand;
import camp.woowak.lab.payaccount.domain.PayAccount;
import camp.woowak.lab.payaccount.repository.PayAccountRepository;
import camp.woowak.lab.web.authentication.PasswordEncoder;
import jakarta.transaction.Transactional;

@Service
public class SignUpCustomerService {
private final CustomerRepository customerRepository;
private final PayAccountRepository payAccountRepository;
private final PasswordEncoder passwordEncoder;

public SignUpCustomerService(CustomerRepository customerRepository, PayAccountRepository payAccountRepository,
PasswordEncoder passwordEncoder) {
this.customerRepository = customerRepository;
this.payAccountRepository = payAccountRepository;
this.passwordEncoder = passwordEncoder;
}

/**
*
* @throws InvalidCreationException 구매자 생성에 오류가 나는 경우
* @throws DuplicateEmailException 이메일이 중복되는 경우
*/
@Transactional
public Long signUp(SignUpCustomerCommand cmd) {
PayAccount payAccount = new PayAccount();
payAccountRepository.save(payAccount);

Customer newCustomer = new Customer(cmd.name(), cmd.email(), cmd.password(), cmd.phone(), payAccount,
passwordEncoder);

try {
customerRepository.save(newCustomer);
} catch (DataIntegrityViolationException e) {
throw new DuplicateEmailException();
}
return newCustomer.getId();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package camp.woowak.lab.customer.service.command;

public record SignUpCustomerCommand(String name, String email, String password, String phone) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package camp.woowak.lab.web.api.customer;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import camp.woowak.lab.customer.service.SignUpCustomerService;
import camp.woowak.lab.customer.service.command.SignUpCustomerCommand;
import camp.woowak.lab.web.api.utils.APIResponse;
import camp.woowak.lab.web.api.utils.APIUtils;
import camp.woowak.lab.web.dto.request.customer.SignUpCustomerRequest;
import camp.woowak.lab.web.dto.response.customer.SignUpCustomerResponse;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;

@RestController
public class CustomerApiController {
private final SignUpCustomerService signUpCustomerService;

public CustomerApiController(SignUpCustomerService signUpCustomerService) {
this.signUpCustomerService = signUpCustomerService;
}

@PostMapping("/customers")
public ResponseEntity<APIResponse<SignUpCustomerResponse>> signUp(@Valid @RequestBody SignUpCustomerRequest request,
HttpServletResponse response) {
SignUpCustomerCommand command =
new SignUpCustomerCommand(request.name(), request.email(), request.password(), request.phone());

Long registeredId = signUpCustomerService.signUp(command);

response.setHeader("Location", "/customers/" + registeredId);

return APIUtils.of(HttpStatus.CREATED, new SignUpCustomerResponse(registeredId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package camp.woowak.lab.web.api.customer;

import org.springframework.http.HttpStatus;
import org.springframework.http.ProblemDetail;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import camp.woowak.lab.common.advice.DomainExceptionHandler;
import camp.woowak.lab.common.exception.BadRequestException;
import camp.woowak.lab.customer.exception.DuplicateEmailException;
import camp.woowak.lab.customer.exception.InvalidCreationException;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@DomainExceptionHandler
public class CustomerExceptionHandler extends ResponseEntityExceptionHandler {
/**
*
* BadRequestException.class 와 MethodArgumentNotValidException.class 를 처리한다.
*/
@ExceptionHandler({InvalidCreationException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ProblemDetail handleBadRequestException(BadRequestException e) {
ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST,
e.errorCode().getErrorCode());
problemDetail.setProperty("errorCode", e.errorCode().getErrorCode());
return problemDetail;
}

/**
*
* DuplicateEmailException.class 를 처리한다.
*/
@ExceptionHandler({DuplicateEmailException.class})
@ResponseStatus(HttpStatus.CONFLICT)
public ProblemDetail handleDuplicateEmailException(DuplicateEmailException e) {
ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.CONFLICT,
e.errorCode().getMessage());
problemDetail.setProperty("errorCode", e.errorCode().getErrorCode());
return problemDetail;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package camp.woowak.lab.web.dto.request;

import org.hibernate.validator.constraints.Length;

import camp.woowak.lab.web.validation.annotation.Phone;
import jakarta.validation.constraints.Email;

public record SignUpVendorRequest(
@Length(min = 1, max = 50)
String name,
@Email
String email,
@Length(min = 1, max = 30)
String password,
@Phone
String phone
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package camp.woowak.lab.web.dto.request.customer;

import org.hibernate.validator.constraints.Length;

import camp.woowak.lab.web.validation.annotation.Phone;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;

public record SignUpCustomerRequest(
@Length(min = 1, max = 50, message = "이름은 1자 이상 50자 이하여야 합니다.")
String name,
@NotBlank(message = "이메일은 필수 입력값입니다.")
@Email(message = "이메일 형식이 올바르지 않습니다.")
String email,
@Length(min = 8, max = 20, message = "비밀번호는 8자 이상 20자 이하여야 합니다.")
String password,
@Phone(message = "전화번호 형식이 올바르지 않습니다.")
String phone
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package camp.woowak.lab.web.dto.response.customer;

public record SignUpCustomerResponse(Long id) {
}
Loading

0 comments on commit 8c3c80d

Please sign in to comment.