package kr.wisestone.owl.service.impl; import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.Lists; import com.sun.mail.smtp.SMTPAddressFailedException; import kr.wisestone.owl.config.CommonConfiguration; import kr.wisestone.owl.constant.ElasticSearchConstants; import kr.wisestone.owl.constant.MailConstants; import kr.wisestone.owl.constant.MsgConstants; import kr.wisestone.owl.domain.Department; import kr.wisestone.owl.domain.SystemEmail; import kr.wisestone.owl.domain.User; import kr.wisestone.owl.domain.UserDepartment; import kr.wisestone.owl.domain.enumType.EmailType; import kr.wisestone.owl.exception.OwlRuntimeException; import kr.wisestone.owl.repository.SystemEmailRepository; import kr.wisestone.owl.service.SystemEmailService; import kr.wisestone.owl.service.UserDepartmentService; import kr.wisestone.owl.service.UserService; import kr.wisestone.owl.util.*; import org.apache.commons.io.FilenameUtils; import org.flywaydb.core.internal.util.scanner.filesystem.FileSystemResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.mail.MailSendException; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import javax.activation.DataSource; import javax.activation.FileDataSource; import javax.mail.SendFailedException; import javax.mail.internet.*; import java.io.IOException; import java.util.*; import org.springframework.web.multipart.MultipartFile; import org.thymeleaf.context.Context; import org.thymeleaf.spring5.SpringTemplateEngine; @Service public class SystemEmailServiceImpl extends AbstractServiceImpl> implements SystemEmailService { private static final Logger log = LoggerFactory.getLogger(SystemEmailServiceImpl.class); @Autowired private SystemEmailRepository systemEmailRepository; @Autowired private JavaMailSender javaMailSender; @Autowired private UserService userService; @Autowired private SpringTemplateEngine springTemplateEngine; @Autowired private UserDepartmentService userDepartmentService; @Override protected JpaRepository getRepository() { return this.systemEmailRepository; } @Autowired protected CommonConfiguration commonConfiguration; @Value("${email.userName}") private String emailUserName; @Value("${email.sender}") private String emailSender; // 이메일 즉시 전송 @Async @Override @Transactional(readOnly = true) public void directEmail(String[] sendUsers, EmailType emailType, Map params, String toUser) { for (String sendUser : sendUsers) { User user = this.userService.findByAccount(sendUser); if (user != null) { Locale locale = CommonUtil.getUserLanguage(user.getLanguage()); // 메일을 받는 사용자가 사용하고 있는 언어 정보를 가져온다. // 받는 사람의 언어로 변경하여 즉시 이메일을 발송한다. this.makeDirectContextEmail(emailType, params, locale, new String[]{CommonUtil.decryptAES128(user.getAccount())}); } else { Locale locale = Locale.getDefault(); // 신규 사용자 초대같이 시스템에 등록되지 않은 사용자일 때는 로그인한 사용자의 언어로 전달한다. // 로그인 정보가 없을 경우에는 시스템 기본 로케일로 발송한다. - (스케쥴러나 비동기) if (!StringUtils.isEmpty(toUser)) { User loginUser = this.userService.findByAccount(toUser); if (loginUser != null) { locale = CommonUtil.getUserLanguage(loginUser.getLanguage()); } } this.makeDirectContextEmail(emailType, params, locale, new String[]{CommonUtil.decryptAES128(sendUser)}); } } } // 받는 사람의 언어로 변경하여 즉시 이메일을 발송한다. private void makeDirectContextEmail(EmailType emailType, Map params, Locale locale, String[] sendUsers) { MailConstants mailConstants = null; Context context; String content = null; String[] filePaths = null; params.put("url", this.commonConfiguration.getEmailSendUrl()); switch (emailType) { case WORKSPACE_JOIN: // 회원 가입 mailConstants = MailConstants.WORKSPACE_JOIN; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; case USER_SEARCH_PASSWORD: // 비밀번호 찾기 mailConstants = MailConstants.USER_SEARCH_PASSWORD; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; case USER_WITH_DRAW: // 회원 탈퇴 mailConstants = MailConstants.USER_WITH_DRAW; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; case REGULAR_PAYMENT: // 정기 결제 완료 mailConstants = MailConstants.REGULAR_PAYMENT; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; case REGULAR_PAYMENT_CANCEL: // 정기 결제 취소 mailConstants = MailConstants.REGULAR_PAYMENT_CANCEL; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; case REGULAR_PAYMENT_CANCEL_BY_ACCOUNTING_MANAGER: // 정기 결제 취소 정보를 회계 담당자에게 전달한다. mailConstants = MailConstants.REGULAR_PAYMENT_CANCEL_BY_ACCOUNTING_MANAGER; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; case REGULAR_PAYMENT_MODIFY: // 정기 결제 변경 mailConstants = MailConstants.REGULAR_PAYMENT_MODIFY; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; case WORKSPACE_INVITE_NEW_USER: // 신규 사용자 초대 메일 mailConstants = MailConstants.WORKSPACE_INVITE_NEW_USER; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; case WORKSPACE_INVITE_SYSTEM_USER: // 기존 사용자 업무 공간 초대 mailConstants = MailConstants.WORKSPACE_INVITE_SYSTEM_USER; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; case WORKSPACE_MAX_USER_EXCESS: // 업무 공간 활성 사용자 초과 알림 메일 전송 mailConstants = MailConstants.WORKSPACE_MAX_USER_EXCESS; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; case ISSUE_SEND: // 이슈 이메일로 대상자에게 발송 mailConstants = MailConstants.ISSUE_SEND; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; case ISSUE_SEND_1: // 이슈 이메일 템플릿1로 대상자에게 발송 mailConstants = MailConstants.ISSUE_SEND_1; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; case ISSUE_SEND_2: // 이슈 이메일 템플릿2로 대상자에게 발송 mailConstants = MailConstants.ISSUE_SEND_2; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; case ISSUE_SEND_3: // 이슈 이메일 템플릿3로 대상자에게 발송 mailConstants = MailConstants.ISSUE_SEND_3; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; case USER_JOIN_STATISTICS: // 일일 사용자 가입 정보 발송 mailConstants = MailConstants.USER_JOIN_STATISTICS; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; case TOTAL_STATISTICS: mailConstants = MailConstants.TOTAL_STATISTICS; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; case WORKSPACE_EXPIRE: // 업무 공간 사용 기간 만료 mailConstants = MailConstants.WORKSPACE_EXPIRE; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; case WORKSPACE_EXPIRE_ALARM: // 업무 공간 사용 기간 만료 예약 안내 mailConstants = MailConstants.WORKSPACE_EXPIRE_ALARM; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; } this.sendEmail(this.messageAccessor.message(mailConstants.getTitle(), locale), content, sendUsers, filePaths, null); } // 이메일 템플릿 찾기 public String makeEmailContent(EmailType emailType, Map params) { MailConstants mailConstants = null; Context context; String content = null; //params.put("url", this.commonConfiguration.getEmailSendUrl()); Locale locale = Locale.getDefault(); switch (emailType) { case ISSUE_SEND_1: // 이슈 이메일 템플릿1로 대상자에게 발송 mailConstants = MailConstants.ISSUE_SEND_1; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; case ISSUE_SEND_2: // 이슈 이메일 템플릿2로 대상자에게 발송 mailConstants = MailConstants.ISSUE_SEND_2; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; case ISSUE_SEND_3: // 이슈 이메일 템플릿3로 대상자에게 발송 mailConstants = MailConstants.ISSUE_SEND_3; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; } return content; } // 이메일을 발송한다. @Override @Transactional(readOnly = true) public void sendEmail(String subject, String content, String[] to, String[] filePaths, List multipartFiles) { MimeMessage message = this.javaMailSender.createMimeMessage(); try { if (this.emailSender != null && !this.emailSender.equals("") && this.emailUserName != null && !this.emailUserName.equals("")) { InternetAddress from = new InternetAddress(this.emailUserName, this.emailSender); MimeMessageHelper messageHelper = new MimeMessageHelper(message, true, "utf-8"); messageHelper.setSubject(subject); messageHelper.setText(content, true); messageHelper.setFrom(from); messageHelper.setTo(to); if (multipartFiles != null && multipartFiles.size() > 0) { for (MultipartFile file : multipartFiles) { // 파일 첨부 messageHelper.addAttachment(MimeUtility.encodeText(file.getOriginalFilename(), "utf-8", "B"), file); } } if (filePaths != null && filePaths.length > 0) { for (String filePath : filePaths) { if (!StringUtils.isEmpty(filePath)) { DataSource dataSource = new FileDataSource(filePath); messageHelper.addAttachment(MimeUtility.encodeText( FilenameUtils.getBaseName(filePath), "utf-8", "B"), dataSource); } } } this.javaMailSender.send(message); } } catch (MailSendException e) { log.error(e.getMessage()); Exception[] exceptions = e.getMessageExceptions(); for (Exception exception : exceptions) { if (exception instanceof SendFailedException) { Exception nextException = ((SendFailedException) exception).getNextException(); if (nextException instanceof SMTPAddressFailedException) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_INVALID_EMAIL), e); } } throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.EMAIL_NOT_SEND)); } } catch (Exception e) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.EMAIL_NOT_SEND)); } } // 이메일 예약 전송 @Override @Transactional public void reservationEmail(String[] sendUsers, EmailType emailType, Map params) { ObjectMapper mapper = new ObjectMapper(); String parameter = null; try { params.put("eventOccurDate", DateUtil.convertDateToStr(new Date())); parameter = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(params); } catch (Exception e) { log.error("systemEmailServiceImpl -> reservationEmail Exception"); } for (String account : sendUsers) { SystemEmail systemEmail = new SystemEmail(); systemEmail.setEmailType(emailType); systemEmail.setSendAddress(account); systemEmail.setParameter(parameter); systemEmail.setSendYn(false); this.systemEmailRepository.saveAndFlush(systemEmail); } } // 프로젝트에서 변경된 일반 사용자, 프로젝트 관리자를 찾기 위해 사용 @Override @Transactional(readOnly = true) public List notificationUserChange(List totalUsers, List targetUsers) { List results = Lists.newArrayList(); // 제외 대상자 찾기 for (User user : totalUsers) { boolean excludeCheck = true; for (User newUser : targetUsers) { if (user.getId().equals(newUser.getId())) { excludeCheck = false; break; } } if (excludeCheck) { results.add(user.getAccount()); } } return results; } // 프로젝트에서 변경된 담당부서를 찾기 위해 사용 @Override @Transactional(readOnly = true) public List notificationDepartmentChange(List totalDepartments, List targetDepartments) { List results = Lists.newArrayList(); // 제외 대상자 찾기 for (Department department : totalDepartments) { boolean excludeCheck = true; for (Department newDepartment : targetDepartments) { if (department.getId().equals(newDepartment.getId())) { excludeCheck = false; break; } } if (excludeCheck) { /*List userDepartments = this.userDepartmentService.getUserDepartments(department.getId()); if(userDepartments != null){ for (UserDepartment userDepartment : userDepartments){ User user = this.userService.getUser(userDepartment.getUserId()); results.add(user.getAccount()); } }*/ results.add(department.getDepartmentName()); } } return results; } // 아직 발송되지 않은 이메일을 조회한다. @Override @Transactional(readOnly = true) public List findBySendAddressAndSendYn(String sendAddress) { return this.systemEmailRepository.findBySendAddressAndSendYn(sendAddress, false); } // 사용자가 지정한 시간에 이메일 발송 @Override @Transactional public void reservationSendEmail() { // 30분 마다 해당 시간을 이메일 알람 시간으로 한 대상자를 찾는다. List sendUsers = this.userService.findByReservationNotifyTime(); for (String sendUser : sendUsers) { // 사용자 별로 아직 발송되지 않은 이메일을 조회한다. List systemEmails = this.findBySendAddressAndSendYn(sendUser); StringBuilder emailBuilder = new StringBuilder(); User user = this.userService.findByAccount(sendUser); if (user != null) { Locale locale = CommonUtil.getUserLanguage(user.getLanguage()); // 메일을 받는 사용자가 사용하고 있는 언어 정보를 가져온다. for (SystemEmail systemEmail : systemEmails) { // 받는 사람의 언어로 변경하여 예약된 이메일을 발송한다. this.makeReservationContextEmail(emailBuilder, systemEmail, locale); systemEmail.setSendYn(true); } // 내용이 있으면 발송 if (!StringUtils.isEmpty(emailBuilder.toString())) { // 이메일을 발송한다. this.sendEmail(this.messageAccessor.message(MsgConstants.RESERVATION_EMAIL_TITLE, locale), emailBuilder.toString(), new String[]{CommonUtil.decryptAES128(sendUser)}, null, null); this.systemEmailRepository.saveAll(systemEmails); } } } } // 실시간 이메일 발송 @Override @Transactional public void realTimeSendEmail() { List sendUsers = this.userService.findByRealTimeNotifyTime(); for (String sendUser : sendUsers) { // 사용자 별로 아직 발송되지 않은 이메일을 조회한다. List systemEmails = this.findBySendAddressAndSendYn(sendUser); StringBuilder emailBuilder = new StringBuilder(); User user = this.userService.findByAccount(sendUser); if (user != null) { Locale locale = CommonUtil.getUserLanguage(user.getLanguage()); // 메일을 받는 사용자가 사용하고 있는 언어 정보를 가져온다. for (SystemEmail systemEmail : systemEmails) { // 받는 사람의 언어로 변경하여 예약된 이메일을 발송한다. this.makeReservationContextEmail(emailBuilder, systemEmail, locale); systemEmail.setSendYn(true); } // 내용이 있으면 발송 if (!StringUtils.isEmpty(emailBuilder.toString())) { // 이메일을 발송한다. this.sendEmail(this.messageAccessor.message(MsgConstants.REALTIME_EMAIL_TITLE, locale), emailBuilder.toString(), new String[]{CommonUtil.decryptAES128(sendUser)}, null, null); this.systemEmailRepository.saveAll(systemEmails); } } } } // 받는 사람의 언어로 변경하여 예약된 이메일을 발송한다. private void makeReservationContextEmail(StringBuilder emailBuilder, SystemEmail systemEmail, Locale locale) { MailConstants mailConstants; Context context; String content = null; Map params = new HashMap<>(); try { ObjectMapper mapper = new ObjectMapper(); params = mapper.readValue(systemEmail.getParameter(), new TypeReference>() { }); } catch (JsonGenerationException e) { log.error("SystemEmailServiceImpl -> makeTitleAndContent JsonGenerationException"); } catch (JsonMappingException e) { log.error("SystemEmailServiceImpl -> makeTitleAndContent JsonMappingException"); } catch (IOException e) { log.error("SystemEmailServiceImpl -> makeTitleAndContent IOException"); } switch (systemEmail.getEmailType()) { case PROJECT_DEFAULT_EXCLUDE: // 프로젝트 일반 사용자 제외 안내 mailConstants = MailConstants.PROJECT_DEFAULT_EXCLUDE; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; case PROJECT_MANAGER_EXCLUDE: // 프로젝트 관리자 제외 안내 mailConstants = MailConstants.PROJECT_MANAGER_EXCLUDE; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; case PROJECT_DEFAULT_INCLUDE: // 프로젝트 일반 사용자 참여 안내 mailConstants = MailConstants.PROJECT_DEFAULT_INCLUDE; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; case PROJECT_MANAGER_INCLUDE: // 프로젝트 관리자 참여 안내 mailConstants = MailConstants.PROJECT_MANAGER_INCLUDE; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; case PROJECT_MANAGER_EXCLUDE_AND_PROJECT_DEFAULT_INCLUDE: // 프로젝트 관리자 제외 후 일반 사용자로 참여 안내 mailConstants = MailConstants.PROJECT_MANAGER_EXCLUDE_AND_PROJECT_DEFAULT_INCLUDE; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; case PROJECT_DEFAULT_EXCLUDE_AND_PROJECT_MANAGER_INCLUDE: // 일반 사용자에서 제외 후 프로젝트 관리자로 참여 안내 mailConstants = MailConstants.PROJECT_DEFAULT_EXCLUDE_AND_PROJECT_MANAGER_INCLUDE; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; case ISSUE_ADD: // 이슈 생성 mailConstants = MailConstants.ISSUE_ADD; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; case ISSUE_REMOVE: // 이슈 삭제 mailConstants = MailConstants.ISSUE_REMOVE; context = StringTemplateUtil.makeContext(params, locale); content = this.springTemplateEngine.process(mailConstants.getMailTemplate(), context); break; } emailBuilder.append(content); } @Override public void information(Map params) { String email = MapUtil.getString(params, "email"); /*Optional description = Optional.of(MapUtil.getString(params, "description")); description.orElseThrow(()-> new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_INVALID_EMAIL)));*/ if (email == null) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.USER_INVALID_EMAIL)); } MimeMessage message = this.javaMailSender.createMimeMessage(); try { InternetAddress from = new InternetAddress(email); MimeMessageHelper messageHelper = new MimeMessageHelper(message, true, "utf-8"); messageHelper.setSubject("OWL ITS 문의"); messageHelper.setText(MapUtil.getString(params, "description"), false); messageHelper.setFrom(from); messageHelper.setTo(this.emailUserName); this.javaMailSender.send(message); } catch (Exception e) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.EMAIL_NOT_SEND)); } } }