Skip to content

Commit

Permalink
[feat] 메일링 서비스 기능 구현 (#168)
Browse files Browse the repository at this point in the history
* [feat] 메일 템플릿 및 기능 구현

* [feat] 도메인 및 ENUM 작성

* [feat] 메일링 서비스 구현

* [feat] 에러 처리

* [refact] url 및 메서드 명 변경

* [add] 루트 권한 설정

* [chore] enum 필드 삭제
  • Loading branch information
suhhyun524 authored Jan 19, 2024
1 parent 8af6019 commit 084f952
Show file tree
Hide file tree
Showing 18 changed files with 410 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package ceos.backend.domain.subscriber;


import ceos.backend.domain.subscriber.dto.request.SubscribeRequest;
import ceos.backend.domain.subscriber.service.SubscriberService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping(value = "/subscribe")
@Tag(name = "Subscriber")
public class SubscriberController {

private final SubscriberService subscriberService;

@Operation(summary = "메일링 서비스 구독")
@PostMapping
public void subscribeMail(@RequestBody @Valid SubscribeRequest subscribeRequest) {
log.info("메일링 서비스 구독");
subscriberService.subscribeMail(subscribeRequest);
}

@Operation(summary = "슈퍼유저 - 메일링 서비스")
@GetMapping("/mail")
public void setRecruitingMail() {
log.info("슈퍼유저 - 메일링 서비스");
subscriberService.sendRecruitingMail();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package ceos.backend.domain.subscriber.domain;


import ceos.backend.global.common.entity.BaseEntity;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Subscriber extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "subscriber_id")
private Long id;

@NotNull
@Size(max = 255)
private String email;

// 생성자
@Builder
private Subscriber(String email) {
this.email = email;
}

public static Subscriber from(String email) {
return Subscriber.builder()
.email(email)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ceos.backend.domain.subscriber.dto.request;


import ceos.backend.global.common.annotation.ValidEmail;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;

@Getter
public class SubscribeRequest {

@Schema(defaultValue = "[email protected]", description = "이메일")
@ValidEmail
private String email;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ceos.backend.domain.subscriber.exception;


import ceos.backend.global.error.BaseErrorException;

public class DuplicateData extends BaseErrorException {

public static final DuplicateData EXCEPTION = new DuplicateData();

private DuplicateData() {
super(SubscriberErrorCode.DUPLICATE_DATA);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ceos.backend.domain.subscriber.exception;


import ceos.backend.global.error.BaseErrorException;

public class InvalidAction extends BaseErrorException {

public static final InvalidAction EXCEPTION = new InvalidAction();

private InvalidAction() {
super(SubscriberErrorCode.INVALID_ACTION);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package ceos.backend.domain.subscriber.exception;

import ceos.backend.global.common.dto.ErrorReason;
import ceos.backend.global.error.BaseErrorCode;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

import static org.springframework.http.HttpStatus.*;

@Getter
@AllArgsConstructor
public enum SubscriberErrorCode implements BaseErrorCode {

/* Data */
INVALID_ACTION(BAD_REQUEST, "SUBSCRIBER_400_1", "리쿠르팅 시작 전입니다."),
DUPLICATE_DATA(CONFLICT, "SUBSCRIBER_409_1", "이미 존재하는 데이터입니다");

private HttpStatus status;
private String code;
private String reason;

@Override
public ErrorReason getErrorReason() {
return ErrorReason.of(status.value(), code, reason);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package ceos.backend.domain.subscriber.helper;

import ceos.backend.domain.subscriber.exception.DuplicateData;
import ceos.backend.domain.subscriber.exception.InvalidAction;
import ceos.backend.domain.subscriber.repository.SubscriberRepository;
import ceos.backend.global.common.dto.AwsSESRecruitMail;
import ceos.backend.global.common.event.Event;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import java.time.LocalDate;

@Component
@RequiredArgsConstructor
public class SubscriberHelper {

private final SubscriberRepository subscriberRepository;

public void validateEmail(String email) {
if (subscriberRepository.findByEmail(email).isPresent()) {
throw DuplicateData.EXCEPTION;
}
}

public void validateDate(LocalDate date, LocalDate now) {
if (!date.equals(now)) {
throw InvalidAction.EXCEPTION;
}
}

public void sendRecruitEmail(String email) {
Event.raise(AwsSESRecruitMail.from(email));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package ceos.backend.domain.subscriber.repository;


import ceos.backend.domain.subscriber.domain.Subscriber;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface SubscriberRepository extends JpaRepository<Subscriber, Long> {
Optional<Subscriber> findByEmail(String email);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package ceos.backend.domain.subscriber.service;


import ceos.backend.domain.recruitment.repository.RecruitmentRepository;
import ceos.backend.domain.subscriber.domain.Subscriber;
import ceos.backend.domain.subscriber.dto.request.SubscribeRequest;
import ceos.backend.domain.subscriber.helper.SubscriberHelper;
import ceos.backend.domain.subscriber.repository.SubscriberRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor
public class SubscriberService {

private final SubscriberHelper subscriberHelper;
private final RecruitmentRepository recruitmentRepository;
private final SubscriberRepository subscriberRepository;

@Transactional
public void subscribeMail(SubscribeRequest subscribeRequest) {

//이메일 중복 검증
subscriberHelper.validateEmail(subscribeRequest.getEmail());

Subscriber subscriber = Subscriber.from(subscribeRequest.getEmail());
subscriberRepository.save(subscriber);
}

@Transactional(readOnly = true)
public void sendRecruitingMail() {
LocalDate date = recruitmentRepository.findAll().get(0).getStartDateDoc().toLocalDate();
LocalDate now = LocalDate.now();
List<Subscriber> subscribers = subscriberRepository.findAll();

//리쿠르팅 시작 날짜 검증
subscriberHelper.validateDate(date, now);

// 메일 보내기
for (Subscriber subscriber : subscribers) {
subscriberHelper.sendRecruitEmail(subscriber.getEmail());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package ceos.backend.global.common.dto;


import lombok.Builder;
import lombok.Getter;

@Getter
public class AwsSESRecruitMail {
//수정 예정 -> 이름을 받을 것인가 말 것인가...
private String email;

@Builder
private AwsSESRecruitMail(String email) {
this.email = email;
}

public static AwsSESRecruitMail from(String email) {
return AwsSESRecruitMail.builder().email(email)
.build();
}
}
22 changes: 22 additions & 0 deletions src/main/java/ceos/backend/global/common/dto/mail/EmailInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package ceos.backend.global.common.dto.mail;


import ceos.backend.global.common.dto.AwsSESRecruitMail;
import lombok.Builder;
import lombok.Getter;

@Getter
public class EmailInfo {
private String email;

@Builder
private EmailInfo(String email) {
this.email = email;
}

public static EmailInfo from(AwsSESRecruitMail awsSESRecruitMail) {
return EmailInfo.builder()
.email(awsSESRecruitMail.getEmail())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public class WebSecurityConfig {
"/applications/interview", "/applications/pass"
};

private final String[] RootPatterns = {"/admin/super"};
private final String[] RootPatterns = {"/admin/super", "/subscribe/mail"};

@Bean
public UserDetailsService userDetailsService() {
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/ceos/backend/infra/ses/AwsSESMailGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import ceos.backend.domain.application.vo.AnswerVo;
import ceos.backend.global.common.dto.AwsSESMail;
import ceos.backend.global.common.dto.AwsSESPasswordMail;
import ceos.backend.global.common.dto.AwsSESRecruitMail;
import ceos.backend.global.common.dto.ParsedDuration;
import ceos.backend.global.common.dto.mail.*;
import ceos.backend.global.common.entity.Part;
Expand Down Expand Up @@ -128,4 +129,16 @@ public Context generatePasswordMailContext(AwsSESPasswordMail awsSESPasswordMail
public String generatePasswordMailSubject() {
return "세오스 관리자 페이지 임시 비밀번호 발급";
}

public Context generateRecruitMailContext(AwsSESRecruitMail awsSESRecruitMail) {
Context context = new Context();
context.setVariable("email", EmailInfo.from(awsSESRecruitMail));

return context;
}

// 수정 예정
public String generateRecruitMailSubject() {
return "세오스 리쿠르팅 메일 발송";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import ceos.backend.domain.application.dto.request.CreateApplicationRequest;
import ceos.backend.global.common.dto.AwsSESMail;
import ceos.backend.global.common.dto.AwsSESPasswordMail;
import ceos.backend.global.common.dto.AwsSESRecruitMail;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
Expand Down Expand Up @@ -35,4 +36,12 @@ public void handle(AwsSESPasswordMail awsSESPasswordMail) {
final Context CONTEXT = awsSESMailGenerator.generatePasswordMailContext(awsSESPasswordMail);
awsSesUtils.singleEmailRequest(TO, SUBJECT, "sendPasswordMail", CONTEXT);
}

@EventListener(AwsSESRecruitMail.class)
public void handle(AwsSESRecruitMail awsSESRecruitMail) {
final String TO = awsSESRecruitMail.getEmail();
final String SUBJECT = awsSESMailGenerator.generateRecruitMailSubject();
final Context CONTEXT = awsSESMailGenerator.generateRecruitMailContext(awsSESRecruitMail);
awsSesUtils.singleEmailRequest(TO, SUBJECT, "sendRecruitMail", CONTEXT);
}
}
2 changes: 1 addition & 1 deletion src/main/resources/templates/component/copyright.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
font-size: 20px;
line-height: 150%;
color: #D6DADF;">
© 2016-2023 Ceos ALL RIGHTS RESERVED.
© 2016-2024 Ceos ALL RIGHTS RESERVED.
</span>
</div>
</html>
30 changes: 30 additions & 0 deletions src/main/resources/templates/component/recruit.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
</head>
<div th:fragment="passwordInfo(passwordInfo)">
<div style="
width: 680px;
">
<span style="
text-decoration: none;
outline: 0px;
font-family: Pretendard, AppleSDGothic, apple sd gothic neo,
noto sans korean, noto sans korean regular, noto sans cjk kr,
noto sans cjk, nanum gothic, malgun gothic, dotum, arial,
helvetica, MS Gothic, sans-serif;
font-style: normal;
font-weight: 600;
font-size: 16px;
line-height: 150%;
color: #232527;"
>
세오스 리루르팅 시작 알림
텍스트추가텍스트추가
</span>

<div style="height: 16px; width: 680px"></div>
</div>
</div>
</html>
Loading

0 comments on commit 084f952

Please sign in to comment.