Skip to content

Commit

Permalink
Merge pull request #72 from bandalgomsu/main
Browse files Browse the repository at this point in the history
feat : 메일전송 비동기 처리
  • Loading branch information
bandalgomsu authored Jun 10, 2024
2 parents 552a8fc + d01ead9 commit 5ac601a
Show file tree
Hide file tree
Showing 18 changed files with 267 additions and 186 deletions.
9 changes: 8 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-redis' //redis

implementation 'org.springframework.boot:spring-boot-starter-mail'

implementation 'org.springframework.boot:spring-boot-starter-actuator' // 액츄에이터
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' //S3
implementation 'org.springframework.boot:spring-boot-starter-validation' //validation
Expand All @@ -65,6 +65,13 @@ dependencies {
implementation 'com.google.firebase:firebase-admin:6.8.1'
//okhttp
implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.2.2'

//Query Dsl
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"

}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.scheduling.annotation.EnableAsync;

@EnableAsync
@EnableJpaAuditing
@SpringBootApplication
public class GraduateMinionsApplication {
Expand Down
10 changes: 4 additions & 6 deletions src/main/java/com/example/jolvre/auth/api/AuthController.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import com.example.jolvre.auth.dto.LoginDTO.PasswordUpdateRequest;
import com.example.jolvre.auth.dto.SignUpDTO.RefreshRequest;
import com.example.jolvre.auth.dto.SignUpDTO.TokenResponse;
import com.example.jolvre.auth.email.dto.EmailDTO.EmailSendResponse;
import com.example.jolvre.auth.email.dto.EmailDTO.EmailVerifyRequest;
import com.example.jolvre.auth.email.dto.EmailDTO.FindPwEmailVerifyResponse;
import com.example.jolvre.auth.email.service.MailService;
Expand Down Expand Up @@ -54,10 +53,9 @@ public ResponseEntity<TokenResponse> refresh(@RequestBody RefreshRequest request

@Operation(summary = "비밀번호 찾기 인증 메일 발송", description = "비밀번호 찾기 인증 메일을 발송합니다")
@GetMapping("/pw/email/{email}")
public ResponseEntity<EmailSendResponse> sendPwFindAuthEmail(@PathVariable String email) {
EmailSendResponse response = mailService.sendFindPwEmail(email);

return ResponseEntity.ok().body(response);
public ResponseEntity<Void> sendPwFindAuthEmail(@PathVariable String email) {
mailService.sendFindPwEmail(email);
return ResponseEntity.ok().build();
}

@Operation(summary = "비밀번호 찾기 인증 메일 검증", description = "비밀번호 찾기 인증 메일을 검증합니다")
Expand All @@ -73,7 +71,7 @@ public ResponseEntity<FindPwEmailVerifyResponse> verifyPwFindAuthEmail(@RequestB
@PutMapping("/pw")
public ResponseEntity<Void> updatePw(@AuthenticationPrincipal PrincipalDetails principalDetails,
@RequestBody PasswordUpdateRequest request) {
userService.updatePassword(principalDetails.getId(),request.getPassword());
userService.updatePassword(principalDetails.getId(), request.getPassword());
return ResponseEntity.ok().build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ public ResponseEntity<DuplicateEmailResponse> checkDuplicateEmail(@PathVariable
@Operation(summary = "회원가입 인증 메일 발송", description = "회원가입 인증 메일을 발송합니다")
@GetMapping("/email/{email}")
public ResponseEntity<EmailSendResponse> sendSignUpAuthEmail(@PathVariable String email) {
EmailSendResponse response = mailService.sendSignUpEmail(email);
mailService.sendSignUpEmail(email);

return ResponseEntity.ok().body(response);
return ResponseEntity.ok().body(null);
}

@Operation(summary = "회원가입 인증 메일 검증", description = "회원가입 인증 메일을 검증합니다")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.example.jolvre.auth.email.service;

import com.example.jolvre.auth.email.dto.EmailDTO.EmailSendResponse;
import com.example.jolvre.common.util.RedisUtil;
import com.example.jolvre.user.repository.UserRepository;
import jakarta.mail.MessagingException;
Expand All @@ -9,6 +8,7 @@
import lombok.RequiredArgsConstructor;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
Expand All @@ -32,7 +32,8 @@ public void makeRandomNumber() {


//mail을 어디서 보내는지, 어디로 보내는지 , 인증 번호를 html 형식으로 어떻게 보내는지 작성합니다.
public EmailSendResponse sendSignUpEmail(String email) {
@Async
public void sendSignUpEmail(String email) {
makeRandomNumber();
String setFrom = "jolvre"; // email-config에 설정한 자신의 이메일 주소를 입력
String toMail = email;
Expand All @@ -44,20 +45,10 @@ public EmailSendResponse sendSignUpEmail(String email) {
"<br>" +
"인증번호를 제대로 입력해주세요";
mailSend(setFrom, toMail, title, content);
return EmailSendResponse.builder()
.email(toMail)
.authNum(Integer.toString(authNumber))
.build();

}

public EmailSendResponse sendFindPwEmail(String email) {
if (!userRepository.existsByEmail(email)) {
return EmailSendResponse.builder()
.isUser(false)
.build();
}

@Async
public void sendFindPwEmail(String email) {
makeRandomNumber();
String setFrom = "jolvre"; // email-config에 설정한 자신의 이메일 주소를 입력
String toMail = email;
Expand All @@ -69,12 +60,6 @@ public EmailSendResponse sendFindPwEmail(String email) {
"<br>" +
"인증번호를 제대로 입력해주세요";
mailSend(setFrom, toMail, title, content);
return EmailSendResponse.builder()
.email(toMail)
.authNum(Integer.toString(authNumber))
.isUser(true)
.build();

}

//이메일을 전송합니다.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.example.jolvre.auth.email.dto.EmailDTO.FindPwEmailVerifyResponse;
import com.example.jolvre.auth.email.dto.EmailDTO.SignUpEmailVerifyResponse;
import com.example.jolvre.auth.jwt.service.JwtService;
import com.example.jolvre.common.error.user.UserAlreadyExistException;
import com.example.jolvre.common.error.user.UserNotFoundException;
import com.example.jolvre.common.util.RedisUtil;
import com.example.jolvre.user.entity.User;
Expand All @@ -21,6 +22,10 @@ public class MailVerifyService {
private final UserRepository userRepository;

public SignUpEmailVerifyResponse CheckSignUpAuthNum(String email, String authNum) {
if (userRepository.existsByEmail(email)) {
throw new UserAlreadyExistException();
}

if (redisUtil.getData(email) == null) {
return SignUpEmailVerifyResponse.builder()
.verifyResult(false)
Expand Down
30 changes: 30 additions & 0 deletions src/main/java/com/example/jolvre/common/config/AsyncConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.example.jolvre.common.config;

import java.util.concurrent.Executor;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Override
@Bean
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(5);
executor.setQueueCapacity(10);
executor.setThreadNamePrefix("Async MailExecutor-");
executor.initialize();
return executor;
}

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return AsyncConfigurer.super.getAsyncUncaughtExceptionHandler();
}
}
19 changes: 19 additions & 0 deletions src/main/java/com/example/jolvre/common/config/QueryDslConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.example.jolvre.common.config;

import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@RequiredArgsConstructor
public class QueryDslConfig {
private final EntityManager entityManager;

@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public enum ErrorCode {
//User
USER_ACCESS_DENIED("U01", "User Access is Denied.", HttpStatus.UNAUTHORIZED.value()),
USER_NOT_FOUND("U02", "User is not Found.", HttpStatus.BAD_REQUEST.value()),
USER_ALREADY_EXIST("U03", "User is already exists.", HttpStatus.BAD_REQUEST.value()),
//Exhibit
EXHIBIT_NOT_FOUND("E01", "Exhibit is not found", HttpStatus.BAD_REQUEST.value()),
//Diary
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example.jolvre.common.error.user;

import com.example.jolvre.common.error.EntityNotFoundException;
import com.example.jolvre.common.error.ErrorCode;

public class UserAlreadyExistException extends EntityNotFoundException {

public UserAlreadyExistException() {
super(ErrorCode.USER_NOT_FOUND);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.example.jolvre.exhibition.api;

import com.example.jolvre.auth.PrincipalDetails;
import com.example.jolvre.exhibition.dto.ExhibitDTO.ExhibitCommentInfoResponses;
import com.example.jolvre.exhibition.dto.ExhibitDTO.ExhibitCommentUpdateRequest;
import com.example.jolvre.exhibition.dto.ExhibitDTO.ExhibitCommentUploadRequest;
import com.example.jolvre.exhibition.service.ExhibitCommentService;
import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
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
@RequiredArgsConstructor
@Slf4j
@RequestMapping("/api/v1/exhibit")
public class ExhibitionCommentController {
private final ExhibitCommentService exhibitCommentService;

@Operation(summary = "해당 전시 코멘트 업로드", description = "해당 전시에 코멘트를 업로드 한다")
@PostMapping("/{exhibitId}/comment")
public ResponseEntity<Void> uploadComment(@PathVariable Long exhibitId,
@AuthenticationPrincipal PrincipalDetails principalDetails,
@RequestBody ExhibitCommentUploadRequest request) {
exhibitCommentService.uploadComment(exhibitId, principalDetails.getId(), request);

return ResponseEntity.ok().build();
}

@Operation(summary = "해당 전시 모든 코멘트 조회", description = "해당 전시에 코멘트를 입력한다")
@GetMapping("/{exhibitId}/comment")
public ResponseEntity<ExhibitCommentInfoResponses> getAllComment(@PathVariable Long exhibitId) {
ExhibitCommentInfoResponses response = exhibitCommentService.getAllCommentInfo(exhibitId);

return ResponseEntity.ok().body(response);
}

@Operation(summary = "코멘트 수정", description = "해당 전시에 특정 코멘트를 수정한다")
@PatchMapping("/{exhibitId}/comment/{commentId}")
public ResponseEntity<Void> updateComment(@PathVariable Long exhibitId,
@PathVariable Long commentId,
@AuthenticationPrincipal PrincipalDetails principalDetails,
@RequestBody ExhibitCommentUpdateRequest request) {
exhibitCommentService.updateComment(commentId, principalDetails.getId(), request);

return ResponseEntity.ok().build();
}

@Operation(summary = "코멘트 삭제", description = "해당 전시에 특정 코멘트를 삭제한다")
@DeleteMapping("/{exhibitId}/comment/{commentId}")
public ResponseEntity<Void> deleteComment(@PathVariable Long exhibitId,
@PathVariable Long commentId,
@AuthenticationPrincipal PrincipalDetails principalDetails) {
exhibitCommentService.deleteComment(commentId, principalDetails.getId());

return ResponseEntity.ok().build();
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package com.example.jolvre.exhibition.api;

import com.example.jolvre.auth.PrincipalDetails;
import com.example.jolvre.exhibition.dto.ExhibitDTO.ExhibitCommentInfoResponses;
import com.example.jolvre.exhibition.dto.ExhibitDTO.ExhibitCommentUpdateRequest;
import com.example.jolvre.exhibition.dto.ExhibitDTO.ExhibitCommentUploadRequest;
import com.example.jolvre.exhibition.dto.ExhibitDTO.ExhibitInfoResponse;
import com.example.jolvre.exhibition.dto.ExhibitDTO.ExhibitInfoResponses;
import com.example.jolvre.exhibition.dto.ExhibitDTO.ExhibitInvitationResponse;
Expand All @@ -27,7 +24,6 @@
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
Expand Down Expand Up @@ -82,7 +78,6 @@ public ResponseEntity<Void> distributeExhibit(@AuthenticationPrincipal Principal
return ResponseEntity.ok().build();
}

//todo : 페이징 필요
@Operation(summary = "전체 전시 조회 (전시탭에서)")
@GetMapping
public ResponseEntity<ExhibitInfoResponses> getAllExhibit() {
Expand All @@ -106,15 +101,7 @@ public ResponseEntity<ExhibitInfoResponse> getExhibit(@PathVariable Long exhibit

return ResponseEntity.ok().body(response);
}

@Operation(summary = "바동기 전시 업데이트 테스트")
@PatchMapping(path = "/user/{exhibitId}/testAsync", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public void updateExhibitAsync(@AuthenticationPrincipal PrincipalDetails principalDetails,
@ModelAttribute ExhibitUploadRequest request) {

exhibitService.uploadAsync(request, principalDetails.getId());
}


@Operation(summary = "초대장 생성", description = "해당 전시에 맞는 초대장을 생성해준다")
@GetMapping("/{exhibitId}/invitation")
public ResponseEntity<ExhibitInvitationResponse> createInvitation(@PathVariable Long exhibitId) {
Expand All @@ -123,45 +110,6 @@ public ResponseEntity<ExhibitInvitationResponse> createInvitation(@PathVariable
return ResponseEntity.ok().body(response);
}

@Operation(summary = "해당 전시 코멘트 업로드", description = "해당 전시에 코멘트를 업로드 한다")
@PostMapping("/{exhibitId}/comment")
public ResponseEntity<Void> uploadComment(@PathVariable Long exhibitId,
@AuthenticationPrincipal PrincipalDetails principalDetails,
@RequestBody ExhibitCommentUploadRequest request) {
exhibitService.uploadComment(exhibitId, principalDetails.getId(), request);

return ResponseEntity.ok().build();
}

@Operation(summary = "해당 전시 모든 코멘트 조회", description = "해당 전시에 코멘트를 입력한다")
@GetMapping("/{exhibitId}/comment")
public ResponseEntity<ExhibitCommentInfoResponses> getAllComment(@PathVariable Long exhibitId) {
ExhibitCommentInfoResponses response = exhibitService.getAllCommentInfo(exhibitId);

return ResponseEntity.ok().body(response);
}

@Operation(summary = "코멘트 수정", description = "해당 전시에 특정 코멘트를 수정한다")
@PatchMapping("/{exhibitId}/comment/{commentId}")
public ResponseEntity<Void> updateComment(@PathVariable Long exhibitId,
@PathVariable Long commentId,
@AuthenticationPrincipal PrincipalDetails principalDetails,
@RequestBody ExhibitCommentUpdateRequest request) {
exhibitService.updateComment(commentId, principalDetails.getId(), request);

return ResponseEntity.ok().build();
}

@Operation(summary = "코멘트 삭제", description = "해당 전시에 특정 코멘트를 삭제한다")
@DeleteMapping("/{exhibitId}/comment/{commentId}")
public ResponseEntity<Void> deleteComment(@PathVariable Long exhibitId,
@PathVariable Long commentId,
@AuthenticationPrincipal PrincipalDetails principalDetails) {
exhibitService.deleteComment(commentId, principalDetails.getId());

return ResponseEntity.ok().build();
}

@Operation(summary = "키워드를 통한 조회", description = "키워드를 통해 전시회 정보를 가져온다")
@GetMapping("/keyword")
public ResponseEntity<Page<ExhibitInfoResponse>> searchByKeyword(@RequestParam(required = false) String keyword,
Expand All @@ -173,4 +121,6 @@ public ResponseEntity<Page<ExhibitInfoResponse>> searchByKeyword(@RequestParam(r

return ResponseEntity.ok().body(response);
}


}
Loading

0 comments on commit 5ac601a

Please sign in to comment.