import type { Db } from '@barev/db'; import type { OrderStatus } from '@barev/core'; /** Доменные события. Воркер превращает их в уведомления на языке получателя. */ export type DomainEvent = | { type: 'order.created'; orderId: string } | { type: 'order.status'; orderId: string; from: OrderStatus; to: OrderStatus } /** событие без смены статуса, например «Я на месте» */ | { type: 'order.event'; orderId: string; code: string } | { type: 'order.rescheduled'; orderId: string } | { type: 'order.unassigned'; orderId: string; providerId: string } | { type: 'offer.created'; offerId: string } | { type: 'offer.declined'; offerId: string } | { type: 'offer.expired'; offerId: string } | { type: 'company.order'; orderId: string; companyId: string } | { type: 'order.reminder'; orderId: string; hours: 24 | 1 } | { type: 'gift.activated'; clientId: string; endsAt: string } /** оператор сменил статус шага переезда — клиенту сообщение в бота */ | { type: 'lead.step'; stepId: string } /** оператор ответил в чате консьержа — ответ уходит в чат с ботом */ | { type: 'concierge.reply'; messageId: string } /** оператор включил подписку по заявке из приложения */ | { type: 'subscription.activated'; subscriptionId: string } /** уведомления операторам в рабочий бот */ | { type: 'concierge.message'; messageId: string } | { type: 'lead.submitted'; leadId: string } | { type: 'clientRequest.created'; requestId: string } | { type: 'webRequest.created'; requestId: string }; export interface Notifier { emit(event: DomainEvent): Promise | void; } export interface Ctx { db: Db; notify: Notifier; now: () => Date; } export const silentNotifier: Notifier = { emit: () => {} }; /** Ошибка бизнес-логики. code переводится на фронте как errors.. */ export class DomainError extends Error { constructor( public code: DomainErrorCode, public details?: Record, ) { super(code); this.name = 'DomainError'; } } export type DomainErrorCode = | 'NOT_FOUND' | 'FORBIDDEN' | 'CONSENT_REQUIRED' | 'SERVICE_UNAVAILABLE' | 'INVALID_PARAMS' | 'SLOT_UNAVAILABLE' | 'ADDRESS_REQUIRED' | 'TRANSITION_NOT_ALLOWED' | 'CHECKLIST_INCOMPLETE' | 'PHOTO_REQUIRED' | 'ESTIMATE_NOT_ACCEPTED' | 'CANCEL_VIA_OPERATOR' | 'RESCHEDULE_VIA_OPERATOR' | 'AGENCY_CODE_INVALID' | 'AGENCY_ALREADY_SET' | 'OFFER_NOT_ACTIVE' | 'PROVIDER_BUSY' | 'ALREADY_ASSIGNED' | 'INVITE_INVALID' | 'NOT_PAID_STATE' | 'PHONE_INVALID' | 'CONTACT_INVALID' | 'NAME_REQUIRED' | 'PROVIDER_NOT_LINKED' | 'CARD_EXPIRED' | 'OFFER_NOT_AVAILABLE' | 'OFFER_LIMIT_REACHED'; /** Имя очереди BullMQ, через которую API и боты передают события воркеру */ export const EVENTS_QUEUE = 'barev-events';