notification
This commit is contained in:
1 parent
382cafa152
commit
60391ee123
26 files changed
+1077
-14
No files matched your search
@@ -0,0 +1,9 @@
|
||||
import type { NotificationChannelValue } from './value-objects/notification-channel.vo.js';
|
||||
|
||||
/** DI-токен для набора отправщиков, по одному на каждый канал доставки */
|
||||
export const NOTIFICATION_CHANNEL_SENDERS = Symbol('NOTIFICATION_CHANNEL_SENDERS');
|
||||
|
||||
export interface NotificationChannelSender {
|
||||
readonly channel: NotificationChannelValue;
|
||||
send(params: { userId: string; subject?: string; content: string }): Promise<void>;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import type { NotificationChannelValue } from '../value-objects/notification-channel.vo.js';
|
||||
|
||||
/**
|
||||
* Событие, которым любой модуль просит отправить уведомление, не зная,
|
||||
* как именно notification-модуль его доставит (email/push, сейчас/потом).
|
||||
*/
|
||||
export const NOTIFY_EVENT = 'notification.notify';
|
||||
|
||||
export interface NotifyEventPayload {
|
||||
userId: string;
|
||||
/** Если не задано — уведомление уходит по всем каналам, включённым у пользователя */
|
||||
channels?: NotificationChannelValue[];
|
||||
/** Используется email-каналом как тема письма */
|
||||
subject?: string;
|
||||
content: string;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import type { NotificationDeviceToken } from '../../../../generated/prisma/client.js';
|
||||
|
||||
/** DI-токен, т.к. интерфейсы стираются при компиляции TS */
|
||||
export const DEVICE_TOKEN_REPOSITORY = Symbol('DEVICE_TOKEN_REPOSITORY');
|
||||
|
||||
export interface IDeviceTokenRepository {
|
||||
/** Регистрирует токен устройства, обновляет запись при повторной регистрации того же токена */
|
||||
upsert(userId: string, token: string, platform: string): Promise<void>;
|
||||
remove(userId: string, token: string): Promise<void>;
|
||||
findByUserId(userId: string): Promise<NotificationDeviceToken[]>;
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import type { NotificationMessage } from '../../../../generated/prisma/client.js';
|
||||
import type { NotificationChannelValue } from '../value-objects/notification-channel.vo.js';
|
||||
import type { NotificationStatusValue } from '../value-objects/notification-status.vo.js';
|
||||
|
||||
/** DI-токен, т.к. интерфейсы стираются при компиляции TS */
|
||||
export const NOTIFICATION_MESSAGE_REPOSITORY = Symbol('NOTIFICATION_MESSAGE_REPOSITORY');
|
||||
|
||||
export interface INotificationMessageRepository {
|
||||
create(params: {
|
||||
userId: string;
|
||||
channel: NotificationChannelValue;
|
||||
subject?: string;
|
||||
content: string;
|
||||
}): Promise<NotificationMessage>;
|
||||
markStatus(id: string, status: NotificationStatusValue, error?: string): Promise<void>;
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import type { NotificationPreference } from '../../../../generated/prisma/client.js';
|
||||
import type { NotificationChannelValue } from '../value-objects/notification-channel.vo.js';
|
||||
|
||||
/** DI-токен, т.к. интерфейсы стираются при компиляции TS */
|
||||
export const NOTIFICATION_PREFERENCE_REPOSITORY = Symbol('NOTIFICATION_PREFERENCE_REPOSITORY');
|
||||
|
||||
export interface INotificationPreferenceRepository {
|
||||
findByUserId(userId: string): Promise<NotificationPreference[]>;
|
||||
set(userId: string, channel: NotificationChannelValue, enabled: boolean): Promise<void>;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
export const NOTIFICATION_CHANNELS = ['EMAIL', 'PUSH'] as const;
|
||||
|
||||
export type NotificationChannelValue = (typeof NOTIFICATION_CHANNELS)[number];
|
||||
|
||||
export class NotificationChannel {
|
||||
private constructor(private readonly _value: NotificationChannelValue) {}
|
||||
|
||||
static create(value: string): NotificationChannel {
|
||||
if (!NOTIFICATION_CHANNELS.includes(value as NotificationChannelValue)) {
|
||||
throw new Error(`Недопустимый канал уведомлений: "${value}". Разрешены: ${NOTIFICATION_CHANNELS.join(', ')}`);
|
||||
}
|
||||
|
||||
return new NotificationChannel(value as NotificationChannelValue);
|
||||
}
|
||||
|
||||
get value(): NotificationChannelValue {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
equals(other: NotificationChannel): boolean {
|
||||
return this._value === other.value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
export const NOTIFICATION_STATUSES = ['PENDING', 'SENT', 'FAILED'] as const;
|
||||
|
||||
export type NotificationStatusValue = (typeof NOTIFICATION_STATUSES)[number];
|
||||
|
||||
export class NotificationStatus {
|
||||
private constructor(private readonly _value: NotificationStatusValue) {}
|
||||
|
||||
static create(value: string): NotificationStatus {
|
||||
if (!NOTIFICATION_STATUSES.includes(value as NotificationStatusValue)) {
|
||||
throw new Error(`Недопустимый статус уведомления: "${value}". Разрешены: ${NOTIFICATION_STATUSES.join(', ')}`);
|
||||
}
|
||||
|
||||
return new NotificationStatus(value as NotificationStatusValue);
|
||||
}
|
||||
|
||||
static pending(): NotificationStatus {
|
||||
return new NotificationStatus('PENDING');
|
||||
}
|
||||
|
||||
get value(): NotificationStatusValue {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
equals(other: NotificationStatus): boolean {
|
||||
return this._value === other.value;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user