import Notification, { type NotificationType } from '#models/notification' interface NotifyInput { type: NotificationType title: string message?: string | null meta?: Record | null } interface ListParams { page?: number perPage?: number type?: NotificationType /** Lọc theo trạng thái đọc: true = chỉ đã đọc, false = chỉ chưa đọc. */ isRead?: boolean } /** * Thông báo hướng người dùng (sản phẩm mới khi sync, import trùng, kết quả thao tác). * Tương tự LogService nhưng có trạng thái đọc/chưa đọc và hiển thị trên UI. */ export default class NotificationService { static async notify(input: NotifyInput): Promise { return Notification.create({ type: input.type, title: input.title, message: input.message ?? null, meta: input.meta ?? null, isRead: false, }) } /** Tạo nhiều thông báo một lượt. */ static async notifyMany(inputs: NotifyInput[]): Promise { if (!inputs.length) return await Notification.createMany( inputs.map((i) => ({ type: i.type, title: i.title, message: i.message ?? null, meta: i.meta ?? null, isRead: false, })) ) } static async list(params: ListParams) { const page = params.page ?? 1 const perPage = params.perPage ?? 25 const query = Notification.query().orderBy('created_at', 'desc') if (params.type) query.where('type', params.type) if (params.isRead !== undefined) query.where('is_read', params.isRead) return query.paginate(page, perPage) } /** Số thông báo chưa đọc (cho badge). */ static async unreadCount(): Promise { const row = await Notification.query().where('is_read', false).count('* as total').first() return Number((row as any)?.$extras?.total ?? 0) } /** Đánh dấu 1 thông báo là đã đọc. */ static async markRead(id: number): Promise { const notification = await Notification.findOrFail(id) notification.isRead = true await notification.save() return notification } /** Đánh dấu tất cả (hoặc theo loại) là đã đọc. Trả về số bản ghi cập nhật. */ static async markAllRead(type?: NotificationType): Promise { const query = Notification.query().where('is_read', false) if (type) query.where('type', type) const result = await query.update({ is_read: true }) return Array.isArray(result) ? Number(result[0]) : Number(result) } static async destroy(id: number): Promise { const notification = await Notification.findOrFail(id) await notification.delete() } }