Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/itcast-build-and-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ jobs:
spring.datasource.url: ${{ secrets.MYSQL_URL }}
spring.datasource.username: ${{ secrets.DB_USERNAME }}
spring.datasource.password: ${{ secrets.DB_PASSWORD }}
slack.token: ${{ secrets.SLACK_TOKEN }}
slack.channel.monitor: ${{ secrets.SLACK_CHANNEL_MONITOR }}

- name: Set Admin Yaml
uses: microsoft/variable-substitution@v1
Expand All @@ -49,6 +51,10 @@ jobs:
aws.ses.secret-key: ${{ secrets.AWS_SES_SECRET_KEY }}
aws.ses.sender-email: ${{ secrets.AWS_SES_SENDER_EMAIL }}
jwt.secret.key: ${{ secrets.JWT_SECRET_KEY }}
mail.username: ${{ secrets.ADMIN_MAIL }}
mail.password: ${{ secrets.ADMIN_MAIL_PASSWORD }}
slack.token: ${{ secrets.SLACK_TOKEN }}
slack.channel.monitor: ${{ secrets.SLACK_CHANNEL_MONITOR }}

- name: Set B2C Yaml
uses: microsoft/variable-substitution@v1
Expand All @@ -62,7 +68,13 @@ jobs:
aws.ses.secret-key: ${{ secrets.AWS_SES_SECRET_KEY }}
aws.ses.sender-email: ${{ secrets.AWS_SES_SENDER_EMAIL }}
spring.kakao.client-id: ${{ secrets.KAKAO_CLIENT_ID }}
spring.kakao.redirect-uri: ${{ secrets.KAKAO_REDIRECT_URI }}
jwt.secret.key: ${{ secrets.JWT_SECRET_KEY }}
sms.api.key: ${{ secrets.SMS_API_KEY }}
sms.api.secret: ${{ secrets.SMS_API_SECRET }}
sms.sender.phone: ${{ secrets.SMS_SENDER_PHONE }}
slack.token: ${{ secrets.SLACK_TOKEN }}
slack.channel.monitor: ${{ secrets.SLACK_CHANNEL_MONITOR }}

- name: Set Schedule Yaml
uses: microsoft/variable-substitution@v1
Expand All @@ -78,7 +90,10 @@ jobs:
jwt.secret.key: ${{ secrets.JWT_SECRET_KEY }}
sms.api.key: ${{ secrets.SMS_API_KEY }}
sms.api.secret: ${{ secrets.SMS_API_SECRET }}
sms.sender.phone: ${{ secrets.SMS_SENDER_PHONE }}
openai.secret-key: ${{ secrets.OPENAI_SECRET_KEY }}
slack.token: ${{ secrets.SLACK_TOKEN }}
slack.channel.monitor: ${{ secrets.SLACK_CHANNEL_MONITOR }}

- name: Install Docker Compose
run: |
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,4 @@ $git clone https://github.com/crawling-project-crowrong/it-cast.git
$docker-compose up -d
```
#### 3. 애플리케이션 실행

5 changes: 4 additions & 1 deletion admin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
runtimeOnly 'io.micrometer:micrometer-registry-prometheus'

//csv 작업
implementation 'com.opencsv:opencsv:5.9'
implementation 'org.springframework.boot:spring-boot-starter-mail'

implementation 'com.amazonaws:aws-java-sdk-ses:1.12.3'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
}

test {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,15 @@
import itcast.jwt.repository.UserRepository;
import itcast.repository.AdminRepository;
import itcast.repository.BlogHistoryRepository;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import lombok.RequiredArgsConstructor;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;

import java.io.StringWriter;
Expand All @@ -25,6 +30,7 @@ public class AdminBlogHistoryService {
private final UserRepository userRepository;
private final AdminRepository adminRepository;
private final BlogHistoryRepository blogHistoryRepository;
private final JavaMailSender mailSender;

public Page<AdminBlogHistoryResponse> retrieveBlogHistory(Long adminId, Long userId, Long blogId, LocalDate createdAt,
int page, int size
Expand Down Expand Up @@ -63,6 +69,24 @@ public String createCsvFile(Long adminId, Long userId, Long blogId, LocalDate st
return stringWriter.toString();
}

public void sendEmail(byte[] csvFile) throws MessagingException {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");

String fileName = "BlogHistory_File(" + LocalDate.now() + ").csv";
String title = "[관리자 전용 발신] 블로그 히스토리 CSV 파일";
String content = "첨부된 CSV 파일을 확인해주십시오.";
String to = "hamiwood@naver.com";

helper.setFrom("hamiwood@naver.com");
helper.setTo(to);
helper.setSubject(title);
helper.setText(content, false);

helper.addAttachment(fileName, new ByteArrayResource(csvFile));
mailSender.send(message);
}

private void isAdmin(Long id) {
User user = userRepository.findById(id).orElseThrow(() ->
new ItCastApplicationException(ErrorCodes.USER_NOT_FOUND));
Expand Down
65 changes: 65 additions & 0 deletions admin/src/main/java/itcast/application/AdminEmailSender.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package itcast.application;

import com.amazonaws.services.simpleemail.AmazonSimpleEmailService;
import com.amazonaws.services.simpleemail.model.Body;
import com.amazonaws.services.simpleemail.model.Content;
import com.amazonaws.services.simpleemail.model.Destination;
import com.amazonaws.services.simpleemail.model.Message;
import com.amazonaws.services.simpleemail.model.SendEmailRequest;
import itcast.dto.request.AdminSendMailRequest;
import itcast.mail.dto.request.SendMailRequest;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

@Getter
@Component
@RequiredArgsConstructor
public class AdminEmailSender {

private static final String MAIL_SUBJECT = "[IT-Cast 뉴스레터] 오늘의 인기 글을 확인해보세요~🔖";
private final AmazonSimpleEmailService amazonSimpleEmailService;

@Value("${aws.ses.sender-email}")
private String senderEmail;

private final TemplateEngine templateEngine;

public void send(AdminSendMailRequest request) {
final SendEmailRequest emailRequest = from(request, request.getReceiver());
amazonSimpleEmailService.sendEmail(emailRequest);
}

private SendEmailRequest from(final AdminSendMailRequest request, final String receiver) {
final Destination destination = new Destination()
.withToAddresses(receiver);

final Message message = new Message()
.withSubject(createContent(MAIL_SUBJECT))
.withBody(new Body()
.withHtml(createContent(createHtmlBody(request))));

return new SendEmailRequest()
.withSource(senderEmail)
.withDestination(destination)
.withMessage(message);
}

private Content createContent(final String text) {
return new Content()
.withCharset("UTF-8")
.withData(text);
}

private String createHtmlBody(final AdminSendMailRequest request) {
final Context context = new Context();
context.setVariable("sender", senderEmail);
context.setVariable("subject", MAIL_SUBJECT);
context.setVariable("contents", request.getContents());

return templateEngine.process("email-template", context);
}
}
53 changes: 46 additions & 7 deletions admin/src/main/java/itcast/application/AdminMailService.java
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
package itcast.application;

import itcast.domain.mailEvent.MailEvents;
import itcast.dto.request.AdminSendMailRequest;
import itcast.dto.response.MailResponse;
import itcast.exception.ErrorCodes;
import itcast.exception.ItCastApplicationException;
import itcast.jwt.repository.UserRepository;
import itcast.mail.repository.MailEventsRepository;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class AdminMailService {

private final MailEventsRepository mailEventsRepository;
private final AdminCheckService adminCheckService;
private final AdminEmailSender adminEmailSender;
private final UserRepository userRepository;

public Page<MailResponse> retrieveMailEvent(Long adminId, int page, int size) {
adminCheckService.isAdmin(adminId);
Expand Down Expand Up @@ -56,4 +62,37 @@ public Page<MailResponse> retrieveMailEvent(Long adminId, int page, int size) {

return new PageImpl<>(mailResponses, pageable, mailEventsPage.getTotalElements());
}
}

public void sendMailEvent(Long adminId, Long userId, LocalDate createdAt) {
adminCheckService.isAdmin(adminId);

LocalDateTime startOfDay = createdAt.atStartOfDay();
LocalDateTime endOfDay = createdAt.atTime(23, 59, 59);

List<MailEvents> mailEvents = mailEventsRepository.findByUserIdAndCreatedAtBetween(userId, startOfDay,
endOfDay);
if (mailEvents.isEmpty()) {
throw new ItCastApplicationException(ErrorCodes.EMAIL_EVENT_NOT_FOUND);
}

String userEmail = userRepository.findEmailById(userId)
.orElseThrow(() -> new ItCastApplicationException(ErrorCodes.USER_EMAIL_NOT_FOUND));

List<AdminSendMailRequest.MailContent> mailContents = mailEvents.stream()
.map(event -> new AdminSendMailRequest.MailContent(
event.getId(),
event.getTitle(),
event.getSummary(),
event.getOriginalLink(),
event.getThumbnail()
))
.collect(Collectors.toList());

AdminSendMailRequest sendMailRequest = new AdminSendMailRequest(
userEmail,
mailContents
);

adminEmailSender.send(sendMailRequest);
}
}
Original file line number Diff line number Diff line change
@@ -1,33 +1,38 @@
package itcast.application;

import com.opencsv.CSVWriter;
import itcast.domain.newsHistory.NewsHistory;
import itcast.domain.user.User;
import itcast.dto.response.AdminNewsHistoryResponse;
import itcast.exception.ErrorCodes;
import itcast.exception.ItCastApplicationException;
import itcast.jwt.repository.UserRepository;
import itcast.repository.AdminRepository;
import itcast.repository.NewsHistoryRepository;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import java.io.StringWriter;
import java.time.LocalDate;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;

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

@Service
@RequiredArgsConstructor
public class AdminNewsHistoryService {

private final UserRepository userRepository;
private final AdminRepository adminRepository;
private final NewsHistoryRepository newsHistoryRepository;
private final JavaMailSender mailSender;

public Page<AdminNewsHistoryResponse> retrieveNewsHistory(Long adminId, Long userId, Long newsId, LocalDate createdAt,
public Page<AdminNewsHistoryResponse> retrieveNewsHistory(Long adminId, Long userId, Long newsId,
LocalDate createdAt,
int page, int size
) {
isAdmin(adminId);
Expand All @@ -37,7 +42,8 @@ public Page<AdminNewsHistoryResponse> retrieveNewsHistory(Long adminId, Long use

public String createCsvFile(Long adminId, Long userId, Long newsId, LocalDate startAt, LocalDate endAt) {
isAdmin(adminId);
List<AdminNewsHistoryResponse> newsHistoryList = newsHistoryRepository.downloadNewsHistoryListByCondition(userId, newsId, startAt, endAt);
List<AdminNewsHistoryResponse> newsHistoryList = newsHistoryRepository.downloadNewsHistoryListByCondition(
userId, newsId, startAt, endAt);
StringWriter stringWriter = new StringWriter();
CSVWriter csvWriter = new CSVWriter(stringWriter);

Expand All @@ -64,6 +70,24 @@ public String createCsvFile(Long adminId, Long userId, Long newsId, LocalDate st
return stringWriter.toString();
}

public void sendEmail(byte[] csvFile) throws MessagingException {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");

String fileName = "NewsHistory_File(" + LocalDate.now() + ").csv";
String title = "[관리자 전용 발신] 뉴스 히스토리 CSV 파일";
String content = "첨부된 CSV 파일을 확인해주십시오.";
String to = "hamiwood@naver.com";

helper.setFrom("hamiwood@naver.com");
helper.setTo(to);
helper.setSubject(title);
helper.setText(content, false);

helper.addAttachment(fileName, new ByteArrayResource(csvFile));
mailSender.send(message);
}

private void isAdmin(Long id) {
User user = userRepository.findById(id).orElseThrow(() ->
new ItCastApplicationException(ErrorCodes.USER_NOT_FOUND));
Expand Down
34 changes: 34 additions & 0 deletions admin/src/main/java/itcast/config/MailConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package itcast.config;

import itcast.dto.request.MailProperties;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import java.util.Properties;

@Configuration
@RequiredArgsConstructor
public class MailConfig {

private final MailProperties mailProperties;

@Bean
public JavaMailSender javaMailSender() {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost(mailProperties.getHost());
mailSender.setPort(mailProperties.getPort());
mailSender.setUsername(mailProperties.getUsername());
mailSender.setPassword(mailProperties.getPassword());

Properties props = mailSender.getJavaMailProperties();
props.put("mail.transport.protocol", "smtp");
props.put("mail.smtp.auth", String.valueOf(mailProperties.getProperties().isAuth()));
props.put("mail.smtp.ssl.enable", String.valueOf(mailProperties.getProperties().isSslEnable()));
props.put("mail.smtp.starttls.enable", String.valueOf(mailProperties.getProperties().isStarttlsEnable()));
props.put("mail.debug", "true");

return mailSender;
}
}
Loading
Loading