package services import ( "time" "github.com/goravel/framework/contracts/database/orm" "github.com/goravel/framework/facades" apperrors "goravel/app/errors" "goravel/app/models" wsnotifications "goravel/app/websocket/notifications" ) type NotificationService interface { Create(title, content, notifType string, senderID *uint, receiverID *uint) (*models.Notification, error) List(adminID uint, page int, pageSize int, notifType string, isRead string) ([]models.Notification, int64, error) ListRecent(adminID uint, limit int) ([]models.Notification, error) MarkRead(adminID uint, notificationID uint) error MarkAllRead(adminID uint) error UnreadCount(adminID uint) (int64, error) } type NotificationServiceImpl struct{} func NewNotificationServiceImpl() NotificationService { return &NotificationServiceImpl{} } func (s *NotificationServiceImpl) Create(title, content, notifType string, senderID *uint, receiverID *uint) (*models.Notification, error) { if receiverID == nil { // 批量创建通知给所有管理员,使用事务确保原子性 var admins []models.Admin if err := facades.Orm().Query().Find(&admins); err != nil { return nil, err } if len(admins) == 0 { return nil, apperrors.ErrRecordNotFound.WithMessage("no admins found") } var first *models.Notification var notifications []*models.Notification var createdIDs []uint // 使用循环创建通知,如果失败则手动回滚已创建的通知 // 注意:这不是真正的事务,但在框架可能不支持事务的情况下提供基本的回滚机制 for _, admin := range admins { rid := admin.ID notification := &models.Notification{ Title: title, Content: content, Type: notifType, SenderID: senderID, ReceiverID: &rid, } if err := facades.Orm().Query().Create(notification); err != nil { // 如果创建失败,尝试删除已创建的通知(手动回滚) for _, id := range createdIDs { _, _ = facades.Orm().Query().Where("id", id).Delete(&models.Notification{}) } return nil, err } if first == nil { first = notification } notifications = append(notifications, notification) createdIDs = append(createdIDs, notification.ID) } // 事务成功后,在事务外进行 WebSocket 广播(避免阻塞事务) for _, notification := range notifications { wsnotifications.Hub().Broadcast(notification) } return first, nil } // 单个通知创建 notification := &models.Notification{ Title: title, Content: content, Type: notifType, SenderID: senderID, ReceiverID: receiverID, } if err := facades.Orm().Query().Create(notification); err != nil { return nil, err } wsnotifications.Hub().Broadcast(notification) return notification, nil } // buildNotificationQuery 构建通知查询条件(消除代码重复) // 对于私信类型,需要同时查询发送和接收的消息 // 对于其他类型,只查询接收的消息 func (s *NotificationServiceImpl) buildNotificationQuery(adminID uint, notifType, isRead string) orm.Query { query := facades.Orm().Query().Model(&models.Notification{}) if notifType == "message" { // 私信:查询发送或接收的消息 query = query.Where("(receiver_id = ? OR sender_id = ?) AND type = ?", adminID, adminID, "message") } else if notifType != "" { // 指定了其他类型:只查询接收的消息 query = query.Where("receiver_id = ? AND type = ?", adminID, notifType) } else { // 没有指定类型:查询接收的所有消息 + 发送的私信 query = query.Where("receiver_id = ? OR (sender_id = ? AND type = ?)", adminID, adminID, "message") } // 如果指定了已读/未读状态,添加状态筛选 if isRead == "true" { query = query.Where("is_read = ?", true) } else if isRead == "false" { query = query.Where("is_read = ?", false) } return query } func (s *NotificationServiceImpl) List(adminID uint, page int, pageSize int, notifType string, isRead string) ([]models.Notification, int64, error) { var notifications []models.Notification if page < 1 { page = 1 } if pageSize <= 0 || pageSize > 100 { pageSize = 20 } // 分页查询 var total int64 query := s.buildNotificationQuery(adminID, notifType, isRead).With("Sender").With("Receiver").Order("created_at desc") if err := query.Paginate(page, pageSize, ¬ifications, &total); err != nil { return nil, 0, err } return notifications, total, nil } func (s *NotificationServiceImpl) ListRecent(adminID uint, limit int) ([]models.Notification, error) { var notifications []models.Notification if limit <= 0 || limit > 10 { limit = 5 } // 查询最近的通知,包括接收的消息和发送的私信 if err := facades.Orm().Query().Model(&models.Notification{}).With("Sender").With("Receiver"). Where("(receiver_id = ? OR (sender_id = ? AND type = ?))", adminID, adminID, "message"). Order("created_at desc"). Limit(limit). Find(¬ifications); err != nil { return nil, err } return notifications, nil } func (s *NotificationServiceImpl) MarkRead(adminID uint, notificationID uint) error { var notification models.Notification if err := facades.Orm().Query().Where("id = ?", notificationID). Where("receiver_id = ?", adminID). First(¬ification); err != nil { return apperrors.ErrRecordNotFound.WithMessage("notification not found") } if notification.IsRead { return nil } now := time.Now() _, err := facades.Orm().Query(). Model(&models.Notification{}). Where("id = ?", notificationID). Update(map[string]any{ "is_read": true, "read_at": now, }) return err } func (s *NotificationServiceImpl) MarkAllRead(adminID uint) error { now := time.Now() _, err := facades.Orm().Query(). Table("notifications"). Where("receiver_id = ?", adminID). Where("is_read = ?", false). Update(map[string]any{ "is_read": true, "read_at": now, }) return err } func (s *NotificationServiceImpl) UnreadCount(adminID uint) (int64, error) { query := facades.Orm().Query().Model(&models.Notification{}). Where("receiver_id = ?", adminID). Where("is_read = ?", false) return query.Count() }