我从 Mercure hub 开始,但我有一些疑问阻止了我。这个想法是在 Symfony 5.4 Web 应用程序(它是一个基于 sf 5.4 的管理 Web 应用程序)中添加一个经典的用户操作后的通知铃<在这种特定情况下,一旦注册了新的患者转移请求,通知就必须出现。对于属于患者当前注册的医疗机构的用户,将启动通知铃。
注意:在本地,它安装了一个 WAMP 服务器,带有一个 Windows 10 上的 https 虚拟主机。
首先,我安装了美居包:composer require mercure
我已经下载了适用于 Windows 操作系统的 mercure 可执行文件。
必要的环境变量:我知道MERCURE_URL
它是 Web 服务器和 Mercure 集线器之间通信的MERCURE_PUBLIC_URL
地址,它是客户端订阅集线器的地址。
怀疑:基于MERCURE_JWT_SECRET
应该生成的数据。这必须是存储在不变的环境变量中的静态 jwt 令牌?
mercure.yaml 上的美居食谱:
mercure:
hubs:
default:
url: '%env(MERCURE_URL)%'
public_url: '%env(MERCURE_PUBLIC_URL)%'
jwt:
secret: '%env(MERCURE_JWT_SECRET)%'
publish: '*'
疑惑:我不明白JWT在mercure bundle的上下文中的组成,应该如何配置的发布部分?
这是存储新患者转移请求并通过向配置的消息总线发送消息来启动通知过程的操作:
/**
*
* @param Request $request
* @param ManagerRegistry $manager
* @param UserInterface $user
* @param UuidEncoder $uuidEncoder
* @param LoggerInterface $logger
* @param \Symfony\Component\Messenger\MessageBusInterface $messageBus
* @return Response
* @throws Exception
* @throws type
*/
public function solicitarTrasladoAction(Request $request, ManagerRegistry $manager, UserInterface $user, UuidEncoder $uuidEncoder, LoggerInterface $logger, \Symfony\Component\Messenger\MessageBusInterface $messageBus): Response
{
if ($request->isXmlHttpRequest()) {
try {
if (!$request->isMethod(Request::METHOD_POST)) {
return new Response("Operación no soportada!!!", 500);
}
$id = $uuidEncoder->decode($request->request->get('embarazadaId', null));
$cmfDestinoId = $uuidEncoder->decode($request->get('cmfDestino', null));
$em = $manager->getManager();
$conn = $em->getConnection();
$conn->beginTransaction();
try {
$cmfDestino = $manager->getRepository(EstructuraOrganizativa::class)->findOneJoinTipoEstructuraOrganizativa($cmfDestinoId);
if (\is_null($cmfDestino)) {
throw new \Exception("No se encontró la unidad de destino.", 404);
} else if ($cmfDestino->getTipoEstructuraOrganizativa()->getId() !== 6) {
throw new \Exception("No es posible ubicar una embarazada fuera de un CMF.", 406);
}
$embarazada = $em->getRepository(Embarazada::class)->findOneJoinEstructuraOrganizativa($id);
if (\is_null($embarazada)) {
throw new \Exception("No se encontró la embarazada solicitada", 404);
}
if ($embarazada->getEstructuraOrganizativa()->getId() === $cmfDestino->getId()) {
throw new \Exception("No es posible reubicar la embarazada en el CMF al que pertenece actualmente.", 406);
}
$posibleSolicitud = $em->getRepository(\App\Entity\SolicitudTrasladoEmbarazada::class)->findOneBy(['embarazada' => $embarazada, 'estado' => 'solicitado']);
if (!\is_null($posibleSolicitud)) {
throw new \Exception("Ya existe una solicitud de traslado para esta paciente.", 406);
}
$nuevaSolicitudTraslado = new \App\Entity\SolicitudTrasladoEmbarazada();
$nuevaSolicitudTraslado->setEmbarazada($embarazada);
$nuevaSolicitudTraslado->setCmfDestino($cmfDestino);
$nuevaSolicitudTraslado->setEstado("solicitado");
$nuevaSolicitudTraslado->setAsunto("Solicitud de traslado");
$nuevaSolicitudTraslado->setMensaje(\sprintf("Solicito reubicar a '%s' hacia provincia '%s', municipio '%s', CMF: %s.", $embarazada->getNombre(), $cmfDestino->getParent()->getParent()->getParent()->getParent()->getTitle(), $cmfDestino->getParent()->getParent()->getParent()->getTitle(), $cmfDestino->getTitle()));
$em->persist($nuevaSolicitudTraslado);
$em->flush();
$conn->commit();
} catch (\Exception $exc) {
$conn->rollback();
$conn->close();
if (in_array($exc->getCode(), array(404, 406))) {
return new Response($exc->getMessage(), 500);
}
$logger->error(sprintf("[%s:%s]: %s", __CLASS__, __FUNCTION__, $exc->getMessage()));
return new Response("Ocurrió un error inesperado al ejecutar la operación", 500);
}
$messageBus->dispatch(new \App\Message\NotificacionSolicitudTrasladoEmbarazadaMessage($uuidEncoder->encode($nuevaSolicitudTraslado->getIdPublico())));
return new Response("La solicitud de traslado fue enviada satisfactoriamente");
} catch (\Exception $exc) {
$logger->error(sprintf("[%s:%s]: %s", self::class, __FUNCTION__, $exc->getMessage()));
return new Response("Ocurrió un error inesperado al ejecutar la operación", 500);
}
} else {
throw $this->createNotFoundException("Recurso no encontrado");
}
}
在将响应发送给用户以生成新的患者转移请求并达到异步行为之前,我已将新消息添加到 Symfony 消息总线(特别是其使用原则传输),作为内容的一部分传递of message 已创建的新注册表的公共 ID (idPublico)。
// messenger.yaml 配方的内容:
framework:
messenger:
# Uncomment this (and the failed transport below) to send failed messages to this transport for later handling.
failure_transport: failed
reset_on_message: true
transports:
# https://symfony.com/doc/current/messenger.html#transport-configuration
async:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
options:
auto_setup: false
failed:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
options:
queue_name: 'failed'
sync: 'sync://'
routing:
# Route your messages to the transports
'App\Message\NotificacionSolicitudTrasladoEmbarazadaMessage': async
消息类:
namespace App\Message;
class NotificacionSolicitudTrasladoEmbarazadaMessage
{
private $content;
public function __construct(string $content)
{
$this->content = $content;
}
public function getContent(): string
{
return $this->content;
}
}
消息处理程序类,这里它封装了调用函数上的美居集线器逻辑:
use App\Message\NotificacionSolicitudTrasladoEmbarazadaMessage;
use App\Repository\SolicitudTrasladoEmbarazadaRepository;
use App\Services\UuidEncoder;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update;
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
/**
* Envia al Mercure bus la nueva solicitud de traslado para ser notificada a los usuarios
*/
class NotificacionSolicitudTrasladoEmbarazadaMessageHandler implements MessageHandlerInterface
{
private $mercureHub;
private $repositorySolicitudTrasladoEmbarazada;
private $uuidEncoder;
public function __construct(HubInterface $mercureHub, SolicitudTrasladoEmbarazadaRepository $repositorySolicitudTrasladoEmbarazada, UuidEncoder $uuidEncoder)
{
$this->mercureHub = $mercureHub;
$this->repositorySolicitudTrasladoEmbarazada = $repositorySolicitudTrasladoEmbarazada;
$this->uuidEncoder = $uuidEncoder;
}
public function __invoke(NotificacionSolicitudTrasladoEmbarazadaMessage $message)
{
// get the real content of the message
$idPublico = $this->uuidEncoder->decode($message->getContent());
// get the fresh object from the database
$solicitud = $this->repositorySolicitudTrasladoEmbarazada->findOneBy(['idPublico' => $idPublico]);
if(\is_null()){
return;
}
/** Count all unatended request of movements **/
$totalNoAtendidas = $this->repositorySolicitudTrasladoEmbarazada->contarNoAtendidas($solicitud->getCmfDestino());
// make a update to the hub
$actualizacion = new Update(
'https://the-uri-of-resource', // This URI must be generated with symfony routing services, or its a simple formality?
\json_encode(['ultimaSolictud' => $solicitud->getAsunto(), 'totalNoAtendidas' => $totalNoAtendidas]),
true // privado necesita jwt auth set that need JWT auth
);
$this->mercureHub->publish($actualizacion);
return new Response("Publicado");
}
}
Doubt
: Update 对象的 URI 参数必须是使用 Symfony 路由生成的 URL 还是简单的“形式”?
此时消息不断传递到失败的队列,重试发送到mercure hub,因为我无法配置和运行mercure.exe
对于客户端,订阅案例,我仍然不明白如何设置 JWT 令牌,但我将把它留给另一个问题