Section 12: 实现通知微服务 (05:24:10 - 06:12:24)

创建通知微服务 (05:24:10 - 05:44:04)

现在,让我们创建通知微服务。我们将使用 Spring Initializr 或 IntelliJ IDEA 创建一个新的 Maven 项目,并添加以下依赖项:

  • Spring Mail
  • Spring Data MongoDB (optional, 如果需要持久化通知信息)
  • Thymeleaf (如果需要使用模板引擎生成邮件内容)
  • Spring Cloud Starter Config
  • Spring Cloud Starter Netflix Eureka Client
  • Spring for Apache Kafka
  • Lombok

创建好项目后,我们需要定义实体类。

  • Notification Entity: 通知实体,包含通知的基本信息,例如通知 ID、通知类型、通知日期、订单确认信息或支付确认信息。
  • NotificationType Enum: 通知类型枚举,定义了通知的类型,例如订单确认和支付确认。

package com.alibou.ecommerce.notification;

public enum NotificationType {
    ORDER_CONFIRMATION,
    PAYMENT_CONFIRMATION
}
* OrderConfirmation Record: 订单确认信息,包含订单编号、总金额、支付方式、客户信息(姓名、邮箱)和商品列表。 * PaymentConfirmation Record: 支付确认信息,包含订单编号、金额、支付方式和客户信息。

实现 Kafka 消费者 (05:44:04 - 05:53:58)

接下来,我们将实现 Kafka 消费者,用于监听 Kafka 队列并接收订单确认和支付确认消息。

创建名为 NotificationConsumer 的类,并添加以下代码:

package com.alibou.ecommerce.kafka;

import com.alibou.ecommerce.email.EmailService;
import com.alibou.ecommerce.notification.NotificationRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.kafka.annotation.KafkaListener;
import com.alibou.ecommerce.notification.Kafka.PaymentNotificationRequest;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
@Slf4j
public class NotificationConsumer {

    private final NotificationRepository repository;
    private final EmailService emailService;

    @KafkaListener(topics = "payment-topic")
    public void consumePaymentSuccessNotification(PaymentNotificationRequest paymentConfirmation) {
        log.info("Consuming message from payment topic {}", paymentConfirmation);

        // TODO: Save notification to database (optional)

        try {
            emailService.sendPaymentSuccessEmail(
                    paymentConfirmation.getCustomerEmail(),
                    paymentConfirmation.getCustomerFirstName() + " " + paymentConfirmation.getCustomerLastName(),
                    paymentConfirmation.getAmount(),
                    paymentConfirmation.getOrderReference()
            );
        } catch (MessagingException e) {
            log.warn("Cannot send email to {}", paymentConfirmation.getCustomerEmail());
        }
    }

    @KafkaListener(topics = "order-topic")
    public void consumeOrderConfirmationNotification(OrderConfirmation orderConfirmation) {
        log.info("Consuming message from order topic {}", orderConfirmation);

        // TODO: Save notification to database (optional)

        try {
            emailService.sendOrderConfirmationEmail(
                    orderConfirmation.getCustomerEmail(),
                    orderConfirmation.getCustomerFirstName() + " " + orderConfirmation.getCustomerLastName(),
                    orderConfirmation.getTotalAmount(),
                    orderConfirmation.getOrderReference(),
                    orderConfirmation.getProducts()
            );
        } catch (MessagingException e) {
            log.warn("Cannot send email to {}", orderConfirmation.getCustomerEmail());
        }
    }
}

在这个类中,我们使用 @KafkaListener 注解来指定监听的 Kafka 主题。当 Kafka 队列中有新的消息时,相应的方法会被自动调用。

实现邮件服务 (05:53:58 - 06:12:24)

接下来,我们需要实现邮件服务,用于发送邮件通知。 创建一个名为 EmailService 的类,并添加以下代码:

package com.alibou.ecommerce.email;

import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
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.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@RequiredArgsConstructor
public class EmailService {

    private final JavaMailSender mailSender;
    private final TemplateEngine templateEngine;

    @Value("contact@aliboucoding.com")
    private String fromEmail;

    @Async
    public void sendPaymentSuccessEmail(String destinationEmail, String customerName, BigDecimal amount, String orderReference) throws MessagingException {
        MimeMessage message = mailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(message,
                MimeMessageHelper.MULTIPART_MODE_MIXED_RELATED,
                StandardCharsets.UTF_8.name());

        String templateName = EmailTemplates.PAYMENT_CONFIRMATION.getTemplate();
        String subject = EmailTemplates.PAYMENT_CONFIRMATION.getSubject();

        Map<String, Object> variables = new HashMap<>();
        variables.put("customerName", customerName);
        variables.put("amount", amount);
        variables.put("orderReference", orderReference);

        Context context = new Context();
        context.setVariables(variables);

        String htmlTemplate = templateEngine.process(templateName, context);

        helper.setTo(destinationEmail);
        helper.setText(htmlTemplate, true);
        helper.setSubject(subject);
        helper.setFrom(fromEmail);

        mailSender.send(message);
    }

    @Async
    public void sendOrderConfirmationEmail(String destinationEmail, String customerName, BigDecimal totalAmount, String orderReference, List<Product> products) throws MessagingException {
        MimeMessage message = mailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(message,
                MimeMessageHelper.MULTIPART_MODE_MIXED_RELATED,
                StandardCharsets.UTF_8.name());

        String templateName = EmailTemplates.ORDER_CONFIRMATION.getTemplate();
        String subject = EmailTemplates.ORDER_CONFIRMATION.getSubject();

        Map<String, Object> variables = new HashMap<>();
        variables.put("customerName", customerName);
        variables.put("totalAmount", totalAmount);
        variables.put("orderReference", orderReference);
        variables.put("products", products);

        Context context = new Context();
        context.setVariables(variables);

        String htmlTemplate = templateEngine.process(templateName, context);

        helper.setTo(destinationEmail);
        helper.setText(htmlTemplate, true);
        helper.setSubject(subject);
        helper.setFrom(fromEmail);

        mailSender.send(message);
    }
}

在这个类中,我们注入了 JavaMailSenderTemplateEngine sendPaymentSuccessEmailsendOrderConfirmationEmail 方法分别用于发送支付成功确认邮件和订单确认邮件。这些方法使用 Thymeleaf 模板引擎来生成邮件内容,并将相关信息传递给模板。

我们需要配置邮件服务器的相关信息,例如主机、端口、用户名和密码。 这些信息可以在 application.yml 文件中进行配置。

spring:
  mail:
    host: localhost
    port: 1025
    username: alibou
    password: alibou
    properties:
      mail:
        smtp:
          auth: true
          starttls:
            enable: true
        connection:
          timeout: 5000
        timeout: 3000
        writetimeout: 5000

确保你已经安装了 MailDev 工具,并配置了正确的端口。

同时,我们需要在 src/main/resources/templates 目录下创建邮件模板。 创建两个 HTML 文件:payment-confirmation.htmlorder-confirmation.html,并添加相应的邮件内容。

至此,通知微服务也完成了配置。