fix
This commit is contained in:
parent
a050a1291a
commit
9fa3e76810
@ -54,6 +54,10 @@ setInterval(async () => {
|
|||||||
// Синхронизируем реестр с существующими топиками
|
// Синхронизируем реестр с существующими топиками
|
||||||
await topicManager.syncRegistry();
|
await topicManager.syncRegistry();
|
||||||
|
|
||||||
|
// Очищаем старые топики перед началом работы
|
||||||
|
console.log('[index] Clearing old topics...');
|
||||||
|
await topicManager.clearTopics('mail-send-');
|
||||||
|
|
||||||
// Подписываемся на существующие топики
|
// Подписываемся на существующие топики
|
||||||
const existingTopics = await topicManager.getAllTopics();
|
const existingTopics = await topicManager.getAllTopics();
|
||||||
if (existingTopics.length > 0) {
|
if (existingTopics.length > 0) {
|
||||||
|
|||||||
@ -127,7 +127,8 @@ export class DynamicConsumer {
|
|||||||
try {
|
try {
|
||||||
await this.consumer.run({
|
await this.consumer.run({
|
||||||
eachMessage: async ({ topic, partition, message }) => {
|
eachMessage: async ({ topic, partition, message }) => {
|
||||||
console.log(`[DynamicConsumer] Received message: topic=${topic}, partition=${partition}, offset=${message.offset}`);
|
const messageId = `${topic}-${partition}-${message.offset}`;
|
||||||
|
console.log(`[DynamicConsumer] Received message: ${messageId}`);
|
||||||
|
|
||||||
if (this.messageHandler) {
|
if (this.messageHandler) {
|
||||||
try {
|
try {
|
||||||
@ -142,8 +143,11 @@ export class DynamicConsumer {
|
|||||||
smtpServerId: task.smtpServerId
|
smtpServerId: task.smtpServerId
|
||||||
});
|
});
|
||||||
await this.messageHandler(task, topic);
|
await this.messageHandler(task, topic);
|
||||||
|
console.log(`[DynamicConsumer] Successfully processed message: ${messageId}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[DynamicConsumer] Error processing message from ${topic}:`, error);
|
console.error(`[DynamicConsumer] Error processing message from ${topic}:`, error);
|
||||||
|
// Не подтверждаем сообщение при ошибке, чтобы оно было переотправлено
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -198,6 +202,19 @@ async function processEmailTask(task, topic) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Проверяем, не был ли уже отправлен email для этого подписчика в этой кампании
|
||||||
|
const existingLog = await DeliveryLog.findOne({
|
||||||
|
where: {
|
||||||
|
campaign_id: task.campaignId,
|
||||||
|
subscriber_id: task.subscriberId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existingLog) {
|
||||||
|
console.log(`[DynamicConsumer] Email already sent for campaign ${task.campaignId}, subscriber ${task.subscriberId}, status: ${existingLog.status}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await new Promise((resolve) => setTimeout(resolve, 60_000));
|
await new Promise((resolve) => setTimeout(resolve, 60_000));
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import dns from 'dns/promises';
|
import dns from 'dns/promises';
|
||||||
import { topicManager } from './topicManager.js';
|
import { topicManager } from './topicManager.js';
|
||||||
|
import { DeliveryLog, Subscriber } from '../models/index.js';
|
||||||
|
import { Op } from 'sequelize';
|
||||||
|
|
||||||
async function getMxDomain(email) {
|
async function getMxDomain(email) {
|
||||||
const domain = email.split('@')[1];
|
const domain = email.split('@')[1];
|
||||||
@ -51,8 +53,48 @@ export async function fillQueueForCampaign(campaign, subscribers, smtpServers) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Отправляем сообщения в топик
|
// Проверяем, какие подписчики уже получили email
|
||||||
const messages = subs.map(sub => {
|
const existingDeliveryLogs = await DeliveryLog.findAll({
|
||||||
|
where: {
|
||||||
|
campaign_id: campaign.id,
|
||||||
|
subscriber_id: { [Op.in]: subs.map(s => s.id) }
|
||||||
|
},
|
||||||
|
attributes: ['subscriber_id'],
|
||||||
|
raw: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const sentSubscriberIds = new Set(existingDeliveryLogs.map(log => log.subscriber_id));
|
||||||
|
const unsentSubs = subs.filter(sub => !sentSubscriberIds.has(sub.id));
|
||||||
|
|
||||||
|
// Дополнительно проверяем статус подписчиков
|
||||||
|
const activeSubs = await Subscriber.findAll({
|
||||||
|
where: {
|
||||||
|
id: { [Op.in]: unsentSubs.map(s => s.id) },
|
||||||
|
status: 'active'
|
||||||
|
},
|
||||||
|
attributes: ['id', 'email'],
|
||||||
|
raw: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const activeSubIds = new Set(activeSubs.map(s => s.id));
|
||||||
|
const finalSubs = unsentSubs.filter(sub => activeSubIds.has(sub.id));
|
||||||
|
|
||||||
|
console.log(`[queueFiller] After status check: ${finalSubs.length} active subscribers`);
|
||||||
|
|
||||||
|
if (finalSubs.length === 0) {
|
||||||
|
console.log(`[queueFiller] No active subscribers for campaign ${campaign.id}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[queueFiller] Found ${subs.length} subscribers, ${unsentSubs.length} unsent, ${finalSubs.length} active`);
|
||||||
|
|
||||||
|
if (finalSubs.length === 0) {
|
||||||
|
console.log(`[queueFiller] No active subscribers for campaign ${campaign.id}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Отправляем сообщения в топик только для активных неотправленных подписчиков
|
||||||
|
const messages = finalSubs.map(sub => {
|
||||||
// Проверяем наличие версии шаблона
|
// Проверяем наличие версии шаблона
|
||||||
if (!campaign.EmailTemplateVersion) {
|
if (!campaign.EmailTemplateVersion) {
|
||||||
console.error(`[queueFiller] Campaign ${campaign.id} has no EmailTemplateVersion`);
|
console.error(`[queueFiller] Campaign ${campaign.id} has no EmailTemplateVersion`);
|
||||||
@ -81,9 +123,17 @@ export async function fillQueueForCampaign(campaign, subscribers, smtpServers) {
|
|||||||
}).filter(msg => msg !== null);
|
}).filter(msg => msg !== null);
|
||||||
|
|
||||||
for (const message of messages) {
|
for (const message of messages) {
|
||||||
|
console.log(`[queueFiller] Sending message to topic ${topicName}:`, {
|
||||||
|
campaignId: message.campaignId,
|
||||||
|
subscriberId: message.subscriberId,
|
||||||
|
email: message.email
|
||||||
|
});
|
||||||
|
|
||||||
const sent = await topicManager.sendMessage(topicName, message);
|
const sent = await topicManager.sendMessage(topicName, message);
|
||||||
if (!sent) {
|
if (!sent) {
|
||||||
console.error(`[queueFiller] Failed to send message to topic: ${topicName}`);
|
console.error(`[queueFiller] Failed to send message to topic: ${topicName}`);
|
||||||
|
} else {
|
||||||
|
console.log(`[queueFiller] Successfully sent message to topic: ${topicName}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Campaign, MailingGroup, GroupSubscriber, Subscriber, SmtpServer, EmailTemplateVersion } from '../models/index.js';
|
import { Campaign, MailingGroup, GroupSubscriber, Subscriber, SmtpServer, EmailTemplateVersion, DeliveryLog, sequelize } from '../models/index.js';
|
||||||
import { fillQueueForCampaign } from './queueFiller.js';
|
import { fillQueueForCampaign } from './queueFiller.js';
|
||||||
import { Op } from 'sequelize';
|
import { Op } from 'sequelize';
|
||||||
import { topicManager } from './topicManager.js';
|
import { topicManager } from './topicManager.js';
|
||||||
@ -13,7 +13,17 @@ async function clearKafkaTopics(prefix = 'mail-send-') {
|
|||||||
export async function processScheduledCampaigns() {
|
export async function processScheduledCampaigns() {
|
||||||
|
|
||||||
const campaigns = await Campaign.findAll({
|
const campaigns = await Campaign.findAll({
|
||||||
where: { status: 'scheduled' },
|
where: {
|
||||||
|
status: 'scheduled',
|
||||||
|
// Исключаем кампании, которые уже обрабатываются или отправлены
|
||||||
|
id: {
|
||||||
|
[Op.notIn]: sequelize.literal(`(
|
||||||
|
SELECT DISTINCT campaign_id
|
||||||
|
FROM delivery_logs
|
||||||
|
WHERE campaign_id = Campaign.id
|
||||||
|
)`)
|
||||||
|
}
|
||||||
|
},
|
||||||
include: [
|
include: [
|
||||||
MailingGroup,
|
MailingGroup,
|
||||||
SmtpServer,
|
SmtpServer,
|
||||||
@ -69,10 +79,36 @@ export async function processScheduledCampaigns() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Проверяем, не завершена ли уже кампания
|
||||||
|
if (campaign.status === 'sent' || campaign.status === 'failed') {
|
||||||
|
console.log(`[queueFillerJob] Campaign ${campaign.id} already completed with status: ${campaign.status}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем, не обрабатывается ли уже кампания
|
||||||
|
if (campaign.status === 'sending') {
|
||||||
|
console.log(`[queueFillerJob] Campaign ${campaign.id} already being processed`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем, есть ли уже записи в DeliveryLog для этой кампании
|
||||||
|
const campaignLogs = await DeliveryLog.findAll({
|
||||||
|
where: { campaign_id: campaign.id },
|
||||||
|
limit: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
if (campaignLogs.length > 0) {
|
||||||
|
console.log(`[queueFillerJob] Campaign ${campaign.id} already has delivery logs, skipping`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < allSubscriberIds.length; i += BATCH_SIZE) {
|
for (let i = 0; i < allSubscriberIds.length; i += BATCH_SIZE) {
|
||||||
const batchIds = allSubscriberIds.slice(i, i + BATCH_SIZE);
|
const batchIds = allSubscriberIds.slice(i, i + BATCH_SIZE);
|
||||||
const subscribers = await Subscriber.findAll({
|
const subscribers = await Subscriber.findAll({
|
||||||
where: { id: { [Op.in]: batchIds } },
|
where: {
|
||||||
|
id: { [Op.in]: batchIds },
|
||||||
|
status: 'active' // Только активные подписчики
|
||||||
|
},
|
||||||
attributes: ['id', 'email'],
|
attributes: ['id', 'email'],
|
||||||
raw: true,
|
raw: true,
|
||||||
});
|
});
|
||||||
@ -96,9 +132,10 @@ export async function processScheduledCampaigns() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
await fillQueueForCampaign(campaign, subscribers, smtpServers);
|
await fillQueueForCampaign(campaign, subscribers, smtpServers);
|
||||||
}
|
}
|
||||||
|
|
||||||
await campaign.update({ status: 'sending' });
|
// Обновляем статус кампании на 'sending' только если были отправлены сообщения
|
||||||
|
await campaign.update({ status: 'sending' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user