This commit is contained in:
Joe
2026-01-16 15:49:34 +08:00
commit 550d3e1f42
380 changed files with 62024 additions and 0 deletions
+75
View File
@@ -0,0 +1,75 @@
package notifications
import (
"time"
"github.com/gorilla/websocket"
)
type notificationClient struct {
hub *NotificationHub
conn *websocket.Conn
send chan []byte
adminID uint
}
func newNotificationClient(hub *NotificationHub, conn *websocket.Conn, adminID uint) *notificationClient {
return &notificationClient{
hub: hub,
conn: conn,
send: make(chan []byte, 256),
adminID: adminID,
}
}
func (c *notificationClient) readPump() {
defer func() {
c.hub.unregister <- c
_ = c.conn.Close()
}()
c.conn.SetReadLimit(512)
_ = c.conn.SetReadDeadline(time.Now().Add(60 * time.Second))
c.conn.SetPongHandler(func(string) error {
_ = c.conn.SetReadDeadline(time.Now().Add(60 * time.Second))
return nil
})
for {
if _, _, err := c.conn.ReadMessage(); err != nil {
break
}
}
}
func (c *notificationClient) writePump() {
ticker := time.NewTicker(25 * time.Second)
defer func() {
ticker.Stop()
_ = c.conn.Close()
}()
for {
select {
case message, ok := <-c.send:
_ = c.conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
if !ok {
_ = c.conn.WriteMessage(websocket.CloseMessage, []byte{})
return
}
if err := c.conn.WriteMessage(websocket.TextMessage, message); err != nil {
return
}
case <-ticker.C:
_ = c.conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil {
return
}
}
}
}
func (h *NotificationHub) RegisterConnection(conn *websocket.Conn, adminID uint) {
client := newNotificationClient(h, conn, adminID)
h.register <- client
go client.writePump()
go client.readPump()
}
+136
View File
@@ -0,0 +1,136 @@
package notifications
import (
"encoding/json"
"sync"
"time"
"goravel/app/models"
)
type NotificationHub struct {
clients map[uint]map[*notificationClient]bool
register chan *notificationClient
unregister chan *notificationClient
broadcast chan *models.Notification
mu sync.RWMutex
}
var hubInstance = newNotificationHub()
func init() {
go hubInstance.run()
}
func Hub() *NotificationHub {
return hubInstance
}
func newNotificationHub() *NotificationHub {
return &NotificationHub{
clients: make(map[uint]map[*notificationClient]bool),
register: make(chan *notificationClient),
unregister: make(chan *notificationClient),
broadcast: make(chan *models.Notification, 100),
}
}
func (h *NotificationHub) run() {
for {
select {
case client := <-h.register:
h.addClient(client)
case client := <-h.unregister:
h.removeClient(client)
case notification := <-h.broadcast:
h.dispatch(notification)
}
}
}
func (h *NotificationHub) addClient(client *notificationClient) {
h.mu.Lock()
defer h.mu.Unlock()
if _, ok := h.clients[client.adminID]; !ok {
h.clients[client.adminID] = make(map[*notificationClient]bool)
}
h.clients[client.adminID][client] = true
}
func (h *NotificationHub) removeClient(client *notificationClient) {
h.mu.Lock()
defer h.mu.Unlock()
if adminClients, ok := h.clients[client.adminID]; ok {
if _, exists := adminClients[client]; exists {
delete(adminClients, client)
close(client.send)
}
if len(adminClients) == 0 {
delete(h.clients, client.adminID)
}
}
}
func (h *NotificationHub) dispatch(notification *models.Notification) {
payload := h.payload(notification)
data, err := json.Marshal(payload)
if err != nil {
return
}
if notification.ReceiverID == nil {
h.mu.RLock()
defer h.mu.RUnlock()
for _, adminClients := range h.clients {
for client := range adminClients {
client.send <- data
}
}
return
}
h.mu.RLock()
defer h.mu.RUnlock()
if adminClients, ok := h.clients[*notification.ReceiverID]; ok {
for client := range adminClients {
client.send <- data
}
}
}
func (h *NotificationHub) payload(notification *models.Notification) map[string]any {
var readAt *string
if notification.ReadAt != nil {
formatted := notification.ReadAt.Format(time.RFC3339)
readAt = &formatted
}
var receiver uint
if notification.ReceiverID != nil {
receiver = *notification.ReceiverID
}
var sender uint
if notification.SenderID != nil {
sender = *notification.SenderID
}
return map[string]any{
"id": notification.ID,
"title": notification.Title,
"content": notification.Content,
"type": notification.Type,
"sender_id": sender,
"receiver_id": receiver,
"is_read": notification.IsRead,
"read_at": readAt,
"created_at": notification.CreatedAt.Format(time.RFC3339),
}
}
func (h *NotificationHub) Broadcast(notification *models.Notification) {
h.broadcast <- notification
}