feat(realtime): implement SSE realtime notifications; publish events from resolvers; remove polling in chat/sidebar/supplies/warehouse and wire realtime refetch

This commit is contained in:
Bivekich
2025-08-11 22:13:33 +03:00
parent 52107e793e
commit 3a56092385
14 changed files with 562 additions and 40 deletions

View File

@ -4,6 +4,7 @@ import { GraphQLError, GraphQLScalarType, Kind } from 'graphql'
import jwt from 'jsonwebtoken'
import { prisma } from '@/lib/prisma'
import { notifyMany, notifyOrganization } from '@/lib/realtime'
import { DaDataService } from '@/services/dadata-service'
import { MarketplaceService } from '@/services/marketplace-service'
import { SmsService } from '@/services/sms-service'
@ -3336,6 +3337,18 @@ export const resolvers = {
},
})
// Уведомляем получателя о новой заявке
try {
notifyOrganization(args.organizationId, {
type: 'counterparty:request:new',
payload: {
requestId: request.id,
senderId: request.senderId,
receiverId: request.receiverId,
},
})
} catch {}
return {
success: true,
message: 'Заявка отправлена',
@ -3425,6 +3438,14 @@ export const resolvers = {
])
}
// Оповещаем обе стороны об обновлении заявки и возможном изменении списка контрагентов
try {
notifyMany([request.senderId, request.receiverId], {
type: 'counterparty:request:updated',
payload: { requestId: updatedRequest.id, status: updatedRequest.status },
})
} catch {}
return {
success: true,
message: args.accept ? 'Заявка принята' : 'Заявка отклонена',
@ -3597,6 +3618,19 @@ export const resolvers = {
},
})
// Реалтайм нотификация для обеих организаций (отправитель и получатель)
try {
notifyMany([currentUser.organization.id, args.receiverOrganizationId], {
type: 'message:new',
payload: {
messageId: message.id,
senderOrgId: message.senderOrganizationId,
receiverOrgId: message.receiverOrganizationId,
type: message.type,
},
})
} catch {}
return {
success: true,
message: 'Сообщение отправлено',
@ -3684,6 +3718,18 @@ export const resolvers = {
},
})
try {
notifyMany([currentUser.organization.id, args.receiverOrganizationId], {
type: 'message:new',
payload: {
messageId: message.id,
senderOrgId: message.senderOrganizationId,
receiverOrgId: message.receiverOrganizationId,
type: message.type,
},
})
} catch {}
return {
success: true,
message: 'Голосовое сообщение отправлено',
@ -3765,6 +3811,18 @@ export const resolvers = {
},
})
try {
notifyMany([currentUser.organization.id, args.receiverOrganizationId], {
type: 'message:new',
payload: {
messageId: message.id,
senderOrgId: message.senderOrganizationId,
receiverOrgId: message.receiverOrganizationId,
type: message.type,
},
})
} catch {}
return {
success: true,
message: 'Изображение отправлено',
@ -3846,6 +3904,18 @@ export const resolvers = {
},
})
try {
notifyMany([currentUser.organization.id, args.receiverOrganizationId], {
type: 'message:new',
payload: {
messageId: message.id,
senderOrgId: message.senderOrganizationId,
receiverOrgId: message.receiverOrganizationId,
type: message.type,
},
})
} catch {}
return {
success: true,
message: 'Файл отправлен',
@ -4225,6 +4295,14 @@ export const resolvers = {
description: args.input.description,
})
// Реалтайм: уведомляем о смене складских остатков
try {
notifyOrganization(currentUser.organization.id, {
type: 'warehouse:changed',
payload: { supplyId: updatedSupply.id, change: -args.input.quantityUsed },
})
} catch {}
return {
success: true,
message: `Использовано ${args.input.quantityUsed} ${updatedSupply.unit} расходника "${updatedSupply.name}"`,
@ -4492,6 +4570,20 @@ export const resolvers = {
},
})
// Реалтайм: уведомляем поставщика и вовлеченные стороны о новом заказе
try {
const orgIds = [
currentUser.organization.id,
args.input.partnerId,
fulfillmentCenterId || undefined,
args.input.logisticsPartnerId || undefined,
].filter(Boolean) as string[]
notifyMany(orgIds, {
type: 'supply-order:new',
payload: { id: supplyOrder.id, organizationId: currentUser.organization.id },
})
} catch {}
// 📦 РЕЗЕРВИРУЕМ ТОВАРЫ У ПОСТАВЩИКА
// Увеличиваем поле "ordered" для каждого заказанного товара
for (const item of args.input.items) {
@ -6361,6 +6453,19 @@ export const resolvers = {
console.warn('🎉 Склад организации успешно обновлен!')
}
// Уведомляем вовлеченные организации об изменении статуса заказа
try {
const orgIds = [
existingOrder.organizationId,
existingOrder.partnerId,
existingOrder.fulfillmentCenterId || undefined,
].filter(Boolean) as string[]
notifyMany(orgIds, {
type: 'supply-order:updated',
payload: { id: updatedOrder.id, status: updatedOrder.status },
})
} catch {}
return {
success: true,
message: `Статус заказа поставки обновлен на "${args.status}"`,
@ -6467,6 +6572,19 @@ export const resolvers = {
newStatus: 'CONFIRMED',
})
try {
const orgIds = [
existingOrder.organizationId,
existingOrder.partnerId,
existingOrder.fulfillmentCenterId || undefined,
args.logisticsPartnerId,
].filter(Boolean) as string[]
notifyMany(orgIds, {
type: 'supply-order:updated',
payload: { id: updatedOrder.id, status: updatedOrder.status },
})
} catch {}
return {
success: true,
message: 'Логистика успешно назначена',
@ -6590,6 +6708,19 @@ export const resolvers = {
})
console.warn(`[DEBUG] Заказ ${args.id} успешно обновлен до статуса: ${updatedOrder.status}`)
try {
const orgIds = [
updatedOrder.organizationId,
updatedOrder.partnerId,
updatedOrder.fulfillmentCenterId || undefined,
updatedOrder.logisticsPartnerId || undefined,
].filter(Boolean) as string[]
notifyMany(orgIds, {
type: 'supply-order:updated',
payload: { id: updatedOrder.id, status: updatedOrder.status },
})
} catch {}
return {
success: true,
message: 'Заказ поставки одобрен поставщиком. Товары зарезервированы, остатки обновлены.',
@ -6693,6 +6824,19 @@ export const resolvers = {
updatedOrder.items.map((item) => `${item.productId}: -${item.quantity} шт.`).join(', '),
)
try {
const orgIds = [
updatedOrder.organizationId,
updatedOrder.partnerId,
updatedOrder.fulfillmentCenterId || undefined,
updatedOrder.logisticsPartnerId || undefined,
].filter(Boolean) as string[]
notifyMany(orgIds, {
type: 'supply-order:updated',
payload: { id: updatedOrder.id, status: updatedOrder.status },
})
} catch {}
return {
success: true,
message: args.reason ? `Заказ отклонен поставщиком. Причина: ${args.reason}` : 'Заказ отклонен поставщиком',
@ -6792,6 +6936,19 @@ export const resolvers = {
},
})
try {
const orgIds = [
updatedOrder.organizationId,
updatedOrder.partnerId,
updatedOrder.fulfillmentCenterId || undefined,
updatedOrder.logisticsPartnerId || undefined,
].filter(Boolean) as string[]
notifyMany(orgIds, {
type: 'supply-order:updated',
payload: { id: updatedOrder.id, status: updatedOrder.status },
})
} catch {}
return {
success: true,
message: "Заказ отправлен поставщиком. Товары переведены в статус 'в пути'.",
@ -6859,6 +7016,19 @@ export const resolvers = {
},
})
try {
const orgIds = [
updatedOrder.organizationId,
updatedOrder.partnerId,
updatedOrder.fulfillmentCenterId || undefined,
updatedOrder.logisticsPartnerId || undefined,
].filter(Boolean) as string[]
notifyMany(orgIds, {
type: 'supply-order:updated',
payload: { id: updatedOrder.id, status: updatedOrder.status },
})
} catch {}
return {
success: true,
message: 'Заказ подтвержден логистической компанией',
@ -6926,6 +7096,19 @@ export const resolvers = {
},
})
try {
const orgIds = [
updatedOrder.organizationId,
updatedOrder.partnerId,
updatedOrder.fulfillmentCenterId || undefined,
updatedOrder.logisticsPartnerId || undefined,
].filter(Boolean) as string[]
notifyMany(orgIds, {
type: 'supply-order:updated',
payload: { id: updatedOrder.id, status: updatedOrder.status },
})
} catch {}
return {
success: true,
message: args.reason