appointment_system/backend/src/services/adminNotificationService.js
2025-12-23 17:57:19 +08:00

157 lines
4.9 KiB
JavaScript

const { Notification, User } = require('../models');
const logger = require('../config/logger');
const { Op } = require('sequelize');
/**
* Admin Notification Management Service
*/
class AdminNotificationService {
/**
* Get all notifications with pagination and filtering (admin view)
*/
static async getNotifications({ page = 1, limit = 20, type, userId, isRead, search }) {
try {
const offset = (page - 1) * limit;
const where = {};
if (type) where.type = type;
if (userId) where.userId = userId;
if (isRead !== undefined && isRead !== '') {
where.isRead = isRead === 'true' || isRead === true;
}
if (search) {
where[Op.or] = [
{ titleZh: { [Op.like]: `%${search}%` } },
{ titleEn: { [Op.like]: `%${search}%` } },
{ contentZh: { [Op.like]: `%${search}%` } },
];
}
const { count, rows } = await Notification.findAndCountAll({
where,
include: [{ model: User, as: 'user', attributes: ['id', 'nickname', 'phone'] }],
order: [['createdAt', 'DESC']],
limit: parseInt(limit),
offset: parseInt(offset),
});
return {
data: rows.map(n => n.toJSON()),
pagination: { page: parseInt(page), limit: parseInt(limit), total: count, totalPages: Math.ceil(count / limit) },
};
} catch (error) {
logger.error('Error fetching notifications (admin):', error);
throw error;
}
}
/**
* Send notification to specific user (supports both userId and uid)
*/
static async sendToUser(userId, notificationData) {
try {
let user;
// Support both UUID and UID (6-digit number)
if (/^\d{6}$/.test(userId)) {
user = await User.findOne({ where: { uid: userId } });
} else {
user = await User.findByPk(userId);
}
if (!user) {
const error = new Error('User not found');
error.statusCode = 404;
throw error;
}
const notification = await Notification.create({
userId: user.id,
type: notificationData.type || 'system',
titleZh: notificationData.titleZh,
titleEn: notificationData.titleEn || notificationData.titleZh,
titleEs: notificationData.titleEs || notificationData.titleZh,
contentZh: notificationData.contentZh,
contentEn: notificationData.contentEn || notificationData.contentZh,
contentEs: notificationData.contentEs || notificationData.contentZh,
isRead: false,
});
logger.info(`Admin sent notification to user ${user.id} (uid: ${user.uid})`);
return notification.toJSON();
} catch (error) {
logger.error('Error sending notification:', error);
throw error;
}
}
/**
* Send notification to all users (broadcast)
*/
static async broadcast(notificationData) {
try {
const users = await User.findAll({ attributes: ['id'] });
const notifications = users.map(user => ({
userId: user.id,
type: notificationData.type || 'system',
titleZh: notificationData.titleZh,
titleEn: notificationData.titleEn || notificationData.titleZh,
titleEs: notificationData.titleEs || notificationData.titleZh,
contentZh: notificationData.contentZh,
contentEn: notificationData.contentEn || notificationData.contentZh,
contentEs: notificationData.contentEs || notificationData.contentZh,
isRead: false,
}));
await Notification.bulkCreate(notifications);
logger.info(`Admin broadcast notification to ${users.length} users`);
return { count: users.length };
} catch (error) {
logger.error('Error broadcasting notification:', error);
throw error;
}
}
/**
* Delete notification by admin
*/
static async deleteNotification(notificationId) {
try {
const notification = await Notification.findByPk(notificationId);
if (!notification) {
const error = new Error('Notification not found');
error.statusCode = 404;
throw error;
}
await notification.destroy();
logger.info(`Admin deleted notification ${notificationId}`);
return true;
} catch (error) {
logger.error(`Error deleting notification ${notificationId}:`, error);
throw error;
}
}
/**
* Get notification statistics
*/
static async getStatistics() {
try {
const [total, unread, byType] = await Promise.all([
Notification.count(),
Notification.count({ where: { isRead: false } }),
Notification.findAll({
attributes: ['type', [require('sequelize').fn('COUNT', '*'), 'count']],
group: ['type'],
raw: true,
}),
]);
return { total, unread, read: total - unread, byType };
} catch (error) {
logger.error('Error getting notification statistics:', error);
throw error;
}
}
}
module.exports = AdminNotificationService;