Skip to content

Commit

Permalink
Merge pull request #53 from coffee-meet/feat/#46
Browse files Browse the repository at this point in the history
Feat/#46  Certification 테이블 생성, 회사 인증 등록 API 구현
  • Loading branch information
1o18z authored Oct 26, 2023
2 parents 00acd0a + fd1ce0b commit 4ffbd4a
Show file tree
Hide file tree
Showing 26 changed files with 381 additions and 192 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,38 +25,32 @@ public class CertificationController {
private final CertificationService certificationService;

@PostMapping("/users/business-card")
public ResponseEntity<Void> uploadBusinessCard(
@Login
AuthInfo authInfo,
@NotNull
@RequestPart("businessCard")
MultipartFile businessCard
public ResponseEntity<Void> registerCompanyInfo(
@Login AuthInfo authInfo,
@RequestPart("companyEmail") @NotNull String companyEmail,
@RequestPart("department") @NotNull String department,
@RequestPart("businessCard") @NotNull MultipartFile businessCardImage
) {
certificationService.uploadBusinessCard(authInfo.userId(),
FileUtils.convertMultipartFileToFile(businessCard));
certificationService.registerCertification(authInfo.userId(), companyEmail, department,
FileUtils.convertMultipartFileToFile(businessCardImage));
return ResponseEntity.ok().build();
}

@PostMapping("/users/company-mail")
public ResponseEntity<Void> sendVerificationCodeByEmail(
@Login
AuthInfo authInfo,
@Valid @RequestBody
EmailDto emailDto
@Login AuthInfo authInfo,
@Valid @RequestBody EmailDto.Request request
) {
certificationService.sendVerificationMail(authInfo.userId(), emailDto.companyEmail());
certificationService.sendVerificationMail(authInfo.userId(), request.companyEmail());
return ResponseEntity.ok().build();

}

@PostMapping("/users/company-mail/verification")
public ResponseEntity<Void> verifyEmail(
@Login
AuthInfo authInfo,
@Valid @RequestBody
VerificationCodeDto verificationCodeDto
@Login AuthInfo authInfo,
@Valid @RequestBody VerificationCodeDto.Request request
) {
certificationService.verifyEmail(authInfo.userId(), verificationCodeDto.verificationCode());
certificationService.compareCode(authInfo.userId(), request.verificationCode());
return ResponseEntity.ok().build();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package coffeemeet.server.certification.domain;

import coffeemeet.server.common.entity.AdvancedBaseEntity;
import coffeemeet.server.user.domain.User;
import jakarta.persistence.Column;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.MapsId;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.NonNull;

@Entity
@Getter
@Table(name = "certifications")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Certification extends AdvancedBaseEntity {

@Id
private Long id;

@MapsId
@OneToOne(fetch = FetchType.LAZY)
private User user;

@Embedded
@Column(nullable = false)
private CompanyEmail companyEmail;

@Column(nullable = false)
private String businessCardUrl;

@Enumerated(EnumType.STRING)
private Department department;

@Column(nullable = false)
private boolean isCertificated;

@Builder
private Certification(
@NonNull CompanyEmail companyEmail, @NonNull String businessCardUrl,
@NonNull Department department, @NonNull User user) {
this.companyEmail = companyEmail;
this.businessCardUrl = businessCardUrl;
this.department = department;
this.isCertificated = false;
this.user = user;
}

}
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
package coffeemeet.server.user.domain;
package coffeemeet.server.certification.domain;

import coffeemeet.server.common.util.Patterns;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Embeddable
@NoArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class CompanyEmail {

private String companyEmail;
@Column(name = "company_email")
private String value;

public CompanyEmail(String companyEmail) {
validateCompanyEmail(companyEmail);
this.companyEmail = companyEmail;
public CompanyEmail(String value) {
validateCompanyEmail(value);
this.value = value;
}

private void validateCompanyEmail(String companyEmail) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package coffeemeet.server.certification.domain;

public enum Department {
IT, MANAGEMENT, SALES, DISTRIBUTION, DESIGN, MANUFACTURING, RESEARCH_AND_DEVELOPMENT, MARKETING, PLANNING
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package coffeemeet.server.certification.domain;

import coffeemeet.server.user.domain.CompanyEmail;
import java.time.LocalDateTime;
import lombok.Getter;
import org.springframework.data.annotation.Id;
Expand Down
12 changes: 8 additions & 4 deletions src/main/java/coffeemeet/server/certification/dto/EmailDto.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotNull;

public record EmailDto(
@Email @NotNull
String companyEmail
) {
public sealed interface EmailDto permits EmailDto.Request {

record Request(
@Email @NotNull
String companyEmail
) implements EmailDto {

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
import jakarta.validation.constraints.NotNull;
import org.hibernate.validator.constraints.Length;

public record VerificationCodeDto(
@NotNull @Length(min = 6, max = 6)
String verificationCode
) {
public sealed interface VerificationCodeDto permits VerificationCodeDto.Request {

record Request(
@NotNull @Length(min = 6, max = 6)
String verificationCode
) implements VerificationCodeDto {

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package coffeemeet.server.certification.repository;

import coffeemeet.server.certification.domain.Certification;
import coffeemeet.server.certification.domain.CompanyEmail;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface CertificationRepository extends JpaRepository<Certification, Long> {

Optional<Certification> findByUserId(Long userId);

boolean existsByCompanyEmail(CompanyEmail companyEmail);

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
import coffeemeet.server.certification.domain.EmailVerification;
import org.springframework.data.repository.CrudRepository;

public interface VerificationVoRepository extends CrudRepository<EmailVerification, Long> {
public interface EmailVerificationRepository extends CrudRepository<EmailVerification, Long> {

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@

import static coffeemeet.server.common.media.S3MediaService.KeyType.BUSINESS_CARD;

import coffeemeet.server.certification.domain.EmailVerification;
import coffeemeet.server.certification.repository.VerificationVoRepository;
import coffeemeet.server.certification.domain.CompanyEmail;
import coffeemeet.server.certification.domain.Department;
import coffeemeet.server.certification.service.cq.CertificationCommand;
import coffeemeet.server.certification.service.cq.CertificationQuery;
import coffeemeet.server.certification.service.cq.EmailVerificationCommand;
import coffeemeet.server.certification.service.cq.EmailVerificationQuery;
import coffeemeet.server.common.media.EmailService;
import coffeemeet.server.common.media.S3MediaService;
import coffeemeet.server.common.util.FileUtils;
import coffeemeet.server.user.domain.CompanyEmail;
import coffeemeet.server.user.domain.User;
import coffeemeet.server.user.service.UserService;
import java.io.File;
import java.util.random.RandomGenerator;
Expand All @@ -18,47 +22,57 @@
@RequiredArgsConstructor
public class CertificationService {

public static final String VERIFICATION_CODE_NOT_FOUND = "인증코드 기간이 만료되었거나 해당 유저가 인증 번호를 요청한 기록이 없습니다.";
public static final String WRONG_VERIFICATION_CODE = "잘못된 인증코드입니다.";
private static final String WRONG_VERIFICATION_CODE = "잘못된 인증코드입니다.";
private static final RandomGenerator RANDOM_GENERATOR = RandomGenerator.getDefault();

private final S3MediaService s3MediaService;
private final UserService userService;
private final EmailService emailService;
private final VerificationVoRepository verificationVoRepository;
private final UserService userService;
private final CertificationCommand certificationCommand;
private final CertificationQuery certificationQuery;
private final EmailVerificationCommand emailVerificationCommand;
private final EmailVerificationQuery emailVerificationQuery;

public void uploadBusinessCard(long userId, File file) {
public void registerCertification(long userId, String email, String departmentName,
File businessCardImage) {
String key = s3MediaService.generateKey(BUSINESS_CARD);
s3MediaService.upload(key, file);
userService.updateBusinessCardUrl(userId, s3MediaService.getUrl(key));
uploadBusinessCard(userId, key, businessCardImage);

FileUtils.delete(file);
CompanyEmail companyEmail = new CompanyEmail(email);
String businessCardUrl = s3MediaService.getUrl(key);
Department department = Department.valueOf(departmentName);
User user = userService.getUserById(userId);
certificationCommand.newCertification(companyEmail, businessCardUrl, department, user);
}

private void uploadBusinessCard(long userId, String key, File businessCardUrl) {
certificationQuery.applyIfCertifiedUser(userId, certification -> {
String oldKey = s3MediaService.extractKey(certification.getBusinessCardUrl(), BUSINESS_CARD);
s3MediaService.delete(oldKey);
});

s3MediaService.upload(key, businessCardUrl);
FileUtils.delete(businessCardUrl);
}

public void sendVerificationMail(Long userId, String email) {
CompanyEmail companyEmail = new CompanyEmail(email);
userService.validateDuplicatedCompanyEmail(companyEmail);
certificationQuery.checkDuplicatedCompanyEmail(companyEmail);

String verificationCode = generateVerificationCode();
emailService.sendVerificationCode(companyEmail, verificationCode);
verificationVoRepository.save(
new EmailVerification(userId, companyEmail, verificationCode));
}

private String generateVerificationCode() {
return String.format("%06d", RANDOM_GENERATOR.nextInt(1000000));
emailVerificationCommand.newEmailVerification(userId, companyEmail, verificationCode);
}

public void verifyEmail(Long userId, String verificationCode) {
EmailVerification emailVerification = verificationVoRepository.findById(userId)
.orElseThrow(
() -> new IllegalArgumentException(VERIFICATION_CODE_NOT_FOUND));

if (!emailVerification.getCode().equals(verificationCode)) {
public void compareCode(Long userId, String verificationCode) {
String correctCode = emailVerificationQuery.getCodeById(userId);
if (!correctCode.equals(verificationCode)) {
throw new IllegalArgumentException(WRONG_VERIFICATION_CODE);
}
}

userService.updateCompanyEmail(userId, emailVerification.getCompanyEmail());
private String generateVerificationCode() {
return String.format("%06d", RANDOM_GENERATOR.nextInt(1000000));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package coffeemeet.server.certification.service.cq;

import coffeemeet.server.certification.domain.Certification;
import coffeemeet.server.certification.domain.CompanyEmail;
import coffeemeet.server.certification.domain.Department;
import coffeemeet.server.certification.repository.CertificationRepository;
import coffeemeet.server.user.domain.User;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
@RequiredArgsConstructor
public class CertificationCommand {

private final CertificationRepository certificationRepository;

public void newCertification(CompanyEmail companyEmail, String businessCardUrl,
Department department, User user) {
certificationRepository.save(
Certification.builder()
.companyEmail(companyEmail)
.businessCardUrl(businessCardUrl)
.department(department)
.user(user)
.build()
);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package coffeemeet.server.certification.service.cq;

import coffeemeet.server.certification.domain.Certification;
import coffeemeet.server.certification.domain.CompanyEmail;
import coffeemeet.server.certification.repository.CertificationRepository;
import java.util.function.Consumer;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

public static final String CERTIFICATION_NOT_FOUND = "해당 사용자의 인증정보를 찾을 수 없습니다.";
private static final String EXISTED_COMPANY_EMAIL = "이미 사용 중인 회사 이메일입니다.";

private final CertificationRepository certificationRepository;

public Certification getCertificationByUserId(Long userId) {
return certificationRepository.findByUserId(userId)
.orElseThrow(() -> new IllegalArgumentException(CERTIFICATION_NOT_FOUND));
}

public void checkDuplicatedCompanyEmail(CompanyEmail companyEmail) {
if (certificationRepository.existsByCompanyEmail(companyEmail)) {
throw new IllegalArgumentException(EXISTED_COMPANY_EMAIL);
}
}

public void applyIfCertifiedUser(Long userId, Consumer<? super Certification> consumer) {
certificationRepository.findByUserId(userId).ifPresent(consumer);
}

}
Loading

0 comments on commit 4ffbd4a

Please sign in to comment.