Files
Edu/services/msg/src/notifications/notifications.repository.ts

192 lines
4.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { and, count, desc, eq, inArray, lte, sql } from "drizzle-orm";
import { getDb } from "../config/database.js";
import {
notifications,
type NewNotification,
type Notification,
} from "./notifications.schema.js";
/**
* NotificationsRepository —— 通知数据访问层。
*
* 职责:封装 MySQL 读写,与业务逻辑解耦。
* 仲裁依据 G10使用 getDb() 函数式获取 db 实例。
*/
export async function insertNotification(
row: NewNotification,
): Promise<Notification> {
const db = getDb();
await db.insert(notifications).values(row);
return row as Notification;
}
export async function insertNotifications(
rows: NewNotification[],
): Promise<void> {
if (rows.length === 0) return;
const db = getDb();
await db.insert(notifications).values(rows);
}
export async function findById(id: string): Promise<Notification | undefined> {
const db = getDb();
const [row] = await db
.select()
.from(notifications)
.where(eq(notifications.id, id))
.limit(1);
return row;
}
export async function findByEventId(
eventId: string,
): Promise<Notification | undefined> {
const db = getDb();
const [row] = await db
.select()
.from(notifications)
.where(eq(notifications.eventId, eventId))
.limit(1);
return row;
}
/**
* 批量查询已存在的 eventId用于 sendBatch 幂等过滤)。
* 返回已存在的 eventId 集合。
*/
export async function findExistingEventIds(
eventIds: string[],
): Promise<Set<string>> {
if (eventIds.length === 0) return new Set();
const db = getDb();
const rows = await db
.select({ eventId: notifications.eventId })
.from(notifications)
.where(
and(
inArray(notifications.eventId, eventIds),
sql`${notifications.eventId} IS NOT NULL`,
),
);
return new Set(
rows
.map((r) => r.eventId)
.filter((id): id is string => id !== null && id !== undefined),
);
}
export async function listByUser(
userId: string,
options: {
onlyUnread?: boolean;
type?: string;
page: number;
pageSize: number;
},
): Promise<{ items: Notification[]; total: number }> {
const db = getDb();
const conditions = [eq(notifications.userId, userId)];
if (options.onlyUnread) {
conditions.push(eq(notifications.isRead, false));
}
if (options.type) {
conditions.push(
eq(notifications.type, options.type as Notification["type"]),
);
}
const where = and(...conditions);
const offset = (options.page - 1) * options.pageSize;
const items = await db
.select()
.from(notifications)
.where(where)
.orderBy(desc(notifications.createdAt))
.limit(options.pageSize)
.offset(offset);
const [countRow] = await db
.select({ value: count() })
.from(notifications)
.where(where);
return { items, total: countRow?.value ?? 0 };
}
export async function getUnreadCount(userId: string): Promise<number> {
const db = getDb();
const [row] = await db
.select({ value: count() })
.from(notifications)
.where(
and(eq(notifications.userId, userId), eq(notifications.isRead, false)),
);
return row?.value ?? 0;
}
export async function markAsRead(id: string, userId: string): Promise<void> {
const db = getDb();
await db
.update(notifications)
.set({ isRead: true, readAt: new Date(), status: "read" })
.where(and(eq(notifications.id, id), eq(notifications.userId, userId)));
}
export async function batchMarkAsRead(
ids: string[],
userId: string,
): Promise<void> {
const db = getDb();
await db
.update(notifications)
.set({ isRead: true, readAt: new Date(), status: "read" })
.where(
and(inArray(notifications.id, ids), eq(notifications.userId, userId)),
);
}
export async function markAllAsRead(
userId: string,
before?: number,
): Promise<number> {
const db = getDb();
const conditions = [
eq(notifications.userId, userId),
eq(notifications.isRead, false),
];
if (before !== undefined) {
conditions.push(lte(notifications.createdAt, new Date(before)));
}
const result = await db
.update(notifications)
.set({ isRead: true, readAt: new Date(), status: "read" })
.where(and(...conditions));
// mysql2 affectedRows
return (result as unknown as { affectedRows?: number }).affectedRows ?? 0;
}
export async function recallByGroup(groupId: string): Promise<number> {
const db = getDb();
const result = await db
.update(notifications)
.set({ status: "recalled" })
.where(eq(notifications.groupId, groupId));
return (result as unknown as { affectedRows?: number }).affectedRows ?? 0;
}
export async function deleteById(id: string): Promise<void> {
const db = getDb();
await db.delete(notifications).where(eq(notifications.id, id));
}
/** 用于测试/重置 */
export async function deleteAllByUserId(userId: string): Promise<void> {
const db = getDb();
await db.delete(notifications).where(eq(notifications.userId, userId));
}