You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Shopware 6:SwagProductReviews邮件黑名单集成失效及事件改造需求

SwagProductReviews 集成 EmailBlackList 问题解决与改造方案

问题背景

使用SwagProductReviews插件通过定时任务发送评价邀请邮件,需要集成EmailBlackList插件实现:

  • 拦截黑名单邮箱的邮件发送
  • 标记黑名单对应的订单为“已发送”,避免定时任务重复处理

当前存在两个问题:

  1. 黑名单校验逻辑失效,黑名单邮箱仍能收到邮件
  2. 需要通过事件驱动实现插件间解耦,让EmailBlackList监听发送前事件并决定是否拦截

现有代码问题分析

1. MailHelper.php sendMail 方法逻辑错误

原代码中黑名单校验的条件为:

if (!$testMail && !$this->getConfig()->isSendMails() && $this->isEmailBlacklisted($email, $context))

这里的逻辑完全错误——只有当插件配置禁止发送邮件时才会校验黑名单,这和需求完全相反,导致黑名单拦截从未触发。

2. 插件间耦合度高

当前通过装饰器MailHelperWithBlacklist.php修改发送逻辑,耦合度高且可能未正确注册,导致装饰器未生效。

解决方案

步骤1:修复黑名单校验逻辑

修改MailHelper.php的sendMail方法,修正条件判断:

public function sendMail(array $data, Context $context, array $templateData = [], bool $testMail = false): ?Email
{
    $email = $data['email'];
    // 仅非测试邮件时校验黑名单,去掉错误的配置判断
    if (!$testMail && $this->isEmailBlacklisted($email, $context)) {
        $this->loggerHelper->addDirectRecord(Logger::DEBUG, 'Sending Mail blocked by blacklist.');
        return null;
    }

    if ($testMail) {
        $this->saveTemplateData($context, $data['technicalName'], $templateData);
    }

    return $this->getMailService()->send(
        $data,
        $context,
        $templateData
    );
}

步骤2:实现事件驱动的插件解耦

2.1 在SwagProductReviews中定义前置事件

创建事件类BeforeSendProductReviewInvitationEvent.php:

<?php

namespace Swag\ProductReviews\Event;

use Shopware\Core\Checkout\Order\OrderEntity;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\Event\ShopwareEvent;
use Symfony\Contracts\EventDispatcher\Event;

class BeforeSendProductReviewInvitationEvent extends Event implements ShopwareEvent
{
    public const NAME = 'swag.product_reviews.before_send_invitation';

    private OrderEntity $order;
    private string $email;
    private Context $context;
    private bool $blocked = false;

    public function __construct(OrderEntity $order, string $email, Context $context)
    {
        $this->order = $order;
        $this->email = $email;
        $this->context = $context;
    }

    public function getOrder(): OrderEntity
    {
        return $this->order;
    }

    public function getEmail(): string
    {
        return $this->email;
    }

    public function getContext(): Context
    {
        return $this->context;
    }

    public function isBlocked(): bool
    {
        return $this->blocked;
    }

    public function setBlocked(bool $blocked): void
    {
        $this->blocked = $blocked;
    }
}

2.2 在MailHelper中注入事件调度器并触发事件

修改MailHelper的构造函数,注入EventDispatcherInterface:

use Symfony\Component\EventDispatcher\EventDispatcherInterface;

class MailHelper implements MailHelperInterface
{
    // 其他已声明的属性
    private EventDispatcherInterface $eventDispatcher;

    public function __construct(
        // 其他原有依赖
        EventDispatcherInterface $eventDispatcher
    ) {
        // 原有依赖赋值
        $this->eventDispatcher = $eventDispatcher;
    }
}

修改sendInvitationMail方法,在发送邮件前触发事件并处理拦截逻辑:

public function sendInvitationMail(Context $outerContext, OrderEntity $order, bool $testMail = false): bool
{
    try {
        $message = null;
        $context = $this->getOrderContext($outerContext, $order);
        if (!is_null($order->getLineItems()) && count($order->getLineItems()) > 0 && !is_null($order->getOrderCustomer()) && !is_null($order->getOrderCustomer()->getEmail()) && !is_null($order->getOrderCustomer()->getCustomerId())) {
            if ($this->orderService->allProductsRated($order, $outerContext)) {
                $this->markInvitationAsAlreadyRated($order, $outerContext);
                if (!$testMail) {
                    return true;
                }
            }
            $data = $this->prepareMailTemplate(MailHelperInterface::MAIL_TEMPLATE_INVITATION, $context, $order, $testMail);
            $salesChannel = $this->getSalesChannel($context, $order);

            if (!$testMail) {
                $data->set(
                    'recipients',
                    [
                        $order->getOrderCustomer()->getEmail() => $order->getOrderCustomer()->getFirstName() . ' ' . $order->getOrderCustomer()->getLastName(),
                    ]
                );

                if ($this->getConfig()->isSendInvitationCopy()) {
                    $storeOwnerEmail = $this->getStoreOwnerEmail();
                    $data->set('recipientsBcc', $storeOwnerEmail);
                }
            }

            $helper = new SwagProductReviewsHelper();
            $orderHash = $helper->getOrderHash($order);
            $hashShort = substr($orderHash, 0, 6);

            // 新增:触发前置事件,检查是否被拦截
            $event = new BeforeSendProductReviewInvitationEvent($order, $order->getOrderCustomer()->getEmail(), $context);
            $this->eventDispatcher->dispatch($event, BeforeSendProductReviewInvitationEvent::NAME);

            if ($event->isBlocked()) {
                $this->markInvitationAsSent($order, $context);
                $this->loggerHelper->addDirectRecord(Logger::INFO, 'Invitation blocked by blacklist, marked order as sent.', ['Order id' => $order->getId(), 'Order Number' => $order->getOrderNumber()]);
                return true; // 返回true让定时任务认为已处理
            }

            $message = $this->sendMail(
                $data->all(),
                $context,
                [
                    'order' => $order,
                    'salesChannel' => $salesChannel,
                    'hash' => $hashShort
                ],
                $testMail
            );

            // 原有后续逻辑...
        } else {
            // 原有逻辑...
        }
    } catch (\Throwable $e) {
        // 原有异常处理...
    }
}

2.3 在EmailBlackList中创建事件监听器

创建监听器类BlockReviewInvitationListener.php:

<?php

namespace YourNamespace\EmailBlackList\EventListener;

use Swag\ProductReviews\Event\BeforeSendProductReviewInvitationEvent;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;

class BlockReviewInvitationListener
{
    private EntityRepository $blacklistRepository;

    public function __construct(EntityRepository $blacklistRepository)
    {
        $this->blacklistRepository = $blacklistRepository;
    }

    public function __invoke(BeforeSendProductReviewInvitationEvent $event): void
    {
        $email = $event->getEmail();
        $context = $event->getContext();

        $criteria = new Criteria();
        $criteria->addFilter(new EqualsFilter('email', $email));

        $blacklistEntry = $this->blacklistRepository->search($criteria, $context)->first();

        if ($blacklistEntry !== null) {
            $event->setBlocked(true);
        }
    }
}

2.4 注册监听器

在EmailBlackList插件的src/Resources/config/services.xml中添加监听器注册:

<service id="YourNamespace\EmailBlackList\EventListener\BlockReviewInvitationListener">
    <argument type="service" id="email_blacklist.repository"/> <!-- 替换为你的黑名单仓库ID -->
    <tag name="kernel.event_listener" event="swag.product_reviews.before_send_invitation" method="__invoke"/>
</service>

步骤3:移除冗余的装饰器

事件驱动方案已替代MailHelperWithBlacklist.php的功能,可删除该装饰器及相关注册配置,降低耦合度。

效果验证

  1. 将邮箱加入黑名单,执行定时任务
  2. 查看日志,应出现Invitation blocked by blacklist, marked order as sent.的记录
  3. 该订单不会被定时任务重复处理,且黑名单邮箱不会收到邮件

内容的提问来源于stack exchange,提问作者mlodhi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 13:54:52