Replace websocket flow with SSE support in backend.
Update handlers, services, router, and data conversion logic to support server-sent events and related message pipeline changes. Made-with: Cursor
This commit is contained in:
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"carrot_bbs/internal/dto"
|
||||
"carrot_bbs/internal/model"
|
||||
"carrot_bbs/internal/pkg/websocket"
|
||||
"carrot_bbs/internal/pkg/sse"
|
||||
"carrot_bbs/internal/repository"
|
||||
)
|
||||
|
||||
@@ -42,8 +42,6 @@ type PushService interface {
|
||||
|
||||
// 系统消息推送
|
||||
PushSystemMessage(ctx context.Context, userID string, msgType, title, content string, data map[string]interface{}) error
|
||||
PushNotification(ctx context.Context, userID string, notification *websocket.NotificationMessage) error
|
||||
PushAnnouncement(ctx context.Context, announcement *websocket.AnnouncementMessage) error
|
||||
|
||||
// 系统通知推送(新接口,使用独立的 SystemNotification 模型)
|
||||
PushSystemNotification(ctx context.Context, userID string, notification *model.SystemNotification) error
|
||||
@@ -67,7 +65,7 @@ type pushServiceImpl struct {
|
||||
pushRepo *repository.PushRecordRepository
|
||||
deviceRepo *repository.DeviceTokenRepository
|
||||
messageRepo *repository.MessageRepository
|
||||
wsManager *websocket.WebSocketManager
|
||||
sseHub *sse.Hub
|
||||
|
||||
// 推送队列
|
||||
pushQueue chan *pushTask
|
||||
@@ -86,13 +84,13 @@ func NewPushService(
|
||||
pushRepo *repository.PushRecordRepository,
|
||||
deviceRepo *repository.DeviceTokenRepository,
|
||||
messageRepo *repository.MessageRepository,
|
||||
wsManager *websocket.WebSocketManager,
|
||||
sseHub *sse.Hub,
|
||||
) PushService {
|
||||
return &pushServiceImpl{
|
||||
pushRepo: pushRepo,
|
||||
deviceRepo: deviceRepo,
|
||||
messageRepo: messageRepo,
|
||||
wsManager: wsManager,
|
||||
sseHub: sseHub,
|
||||
pushQueue: make(chan *pushTask, PushQueueSize),
|
||||
stopChan: make(chan struct{}),
|
||||
}
|
||||
@@ -140,11 +138,7 @@ func (s *pushServiceImpl) PushToUser(ctx context.Context, userID string, message
|
||||
// pushViaWebSocket 通过WebSocket推送消息
|
||||
// 返回true表示推送成功,false表示用户不在线
|
||||
func (s *pushServiceImpl) pushViaWebSocket(ctx context.Context, userID string, message *model.Message) bool {
|
||||
if s.wsManager == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if !s.wsManager.IsUserOnline(userID) {
|
||||
if s.sseHub == nil || !s.sseHub.HasSubscribers(userID) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -154,36 +148,33 @@ func (s *pushServiceImpl) pushViaWebSocket(ctx context.Context, userID string, m
|
||||
// 从 segments 中提取文本内容
|
||||
content := dto.ExtractTextContentFromModel(message.Segments)
|
||||
|
||||
notification := &websocket.NotificationMessage{
|
||||
ID: fmt.Sprintf("%s", message.ID),
|
||||
Type: string(message.SystemType),
|
||||
Content: content,
|
||||
Extra: make(map[string]interface{}),
|
||||
CreatedAt: message.CreatedAt.UnixMilli(),
|
||||
notification := map[string]interface{}{
|
||||
"id": fmt.Sprintf("%s", message.ID),
|
||||
"type": string(message.SystemType),
|
||||
"content": content,
|
||||
"extra": map[string]interface{}{},
|
||||
"created_at": message.CreatedAt.UnixMilli(),
|
||||
}
|
||||
|
||||
// 填充额外数据
|
||||
if message.ExtraData != nil {
|
||||
notification.Extra["actor_id"] = message.ExtraData.ActorID
|
||||
notification.Extra["actor_name"] = message.ExtraData.ActorName
|
||||
notification.Extra["avatar_url"] = message.ExtraData.AvatarURL
|
||||
notification.Extra["target_id"] = message.ExtraData.TargetID
|
||||
notification.Extra["target_type"] = message.ExtraData.TargetType
|
||||
notification.Extra["action_url"] = message.ExtraData.ActionURL
|
||||
notification.Extra["action_time"] = message.ExtraData.ActionTime
|
||||
|
||||
// 设置触发用户信息
|
||||
extra := notification["extra"].(map[string]interface{})
|
||||
extra["actor_id"] = message.ExtraData.ActorID
|
||||
extra["actor_name"] = message.ExtraData.ActorName
|
||||
extra["avatar_url"] = message.ExtraData.AvatarURL
|
||||
extra["target_id"] = message.ExtraData.TargetID
|
||||
extra["target_type"] = message.ExtraData.TargetType
|
||||
extra["action_url"] = message.ExtraData.ActionURL
|
||||
extra["action_time"] = message.ExtraData.ActionTime
|
||||
if message.ExtraData.ActorID > 0 {
|
||||
notification.TriggerUser = &websocket.NotificationUser{
|
||||
ID: fmt.Sprintf("%d", message.ExtraData.ActorID),
|
||||
Username: message.ExtraData.ActorName,
|
||||
Avatar: message.ExtraData.AvatarURL,
|
||||
notification["trigger_user"] = map[string]interface{}{
|
||||
"id": fmt.Sprintf("%d", message.ExtraData.ActorID),
|
||||
"username": message.ExtraData.ActorName,
|
||||
"avatar": message.ExtraData.AvatarURL,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wsMsg := websocket.CreateWSMessage(websocket.MessageTypeNotification, notification)
|
||||
s.wsManager.SendToUser(userID, wsMsg)
|
||||
s.sseHub.PublishToUser(userID, "system_notification", notification)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -208,8 +199,10 @@ func (s *pushServiceImpl) pushViaWebSocket(ctx context.Context, userID string, m
|
||||
SenderID: message.SenderID,
|
||||
}
|
||||
|
||||
wsMsg := websocket.CreateWSMessage(websocket.MessageTypeMessage, event)
|
||||
s.wsManager.SendToUser(userID, wsMsg)
|
||||
s.sseHub.PublishToUser(userID, "chat_message", map[string]interface{}{
|
||||
"detail_type": detailType,
|
||||
"message": event,
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -451,73 +444,21 @@ func (s *pushServiceImpl) PushSystemMessage(ctx context.Context, userID string,
|
||||
|
||||
// pushSystemViaWebSocket 通过WebSocket推送系统消息
|
||||
func (s *pushServiceImpl) pushSystemViaWebSocket(ctx context.Context, userID string, msgType, title, content string, data map[string]interface{}) bool {
|
||||
if s.wsManager == nil {
|
||||
if s.sseHub == nil || !s.sseHub.HasSubscribers(userID) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !s.wsManager.IsUserOnline(userID) {
|
||||
return false
|
||||
sysMsg := map[string]interface{}{
|
||||
"type": msgType,
|
||||
"title": title,
|
||||
"content": content,
|
||||
"data": data,
|
||||
"created_at": time.Now().UnixMilli(),
|
||||
}
|
||||
|
||||
sysMsg := &websocket.SystemMessage{
|
||||
Type: msgType,
|
||||
Title: title,
|
||||
Content: content,
|
||||
Data: data,
|
||||
CreatedAt: time.Now().UnixMilli(),
|
||||
}
|
||||
|
||||
wsMsg := websocket.CreateWSMessage(websocket.MessageTypeSystem, sysMsg)
|
||||
s.wsManager.SendToUser(userID, wsMsg)
|
||||
s.sseHub.PublishToUser(userID, "system_notification", sysMsg)
|
||||
return true
|
||||
}
|
||||
|
||||
// PushNotification 推送通知消息
|
||||
func (s *pushServiceImpl) PushNotification(ctx context.Context, userID string, notification *websocket.NotificationMessage) error {
|
||||
// 首先尝试WebSocket推送
|
||||
if s.pushNotificationViaWebSocket(ctx, userID, notification) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 用户不在线,创建待推送记录
|
||||
// 通知消息可以等用户上线后拉取
|
||||
return errors.New("user is offline, notification will be available on next sync")
|
||||
}
|
||||
|
||||
// pushNotificationViaWebSocket 通过WebSocket推送通知消息
|
||||
func (s *pushServiceImpl) pushNotificationViaWebSocket(ctx context.Context, userID string, notification *websocket.NotificationMessage) bool {
|
||||
if s.wsManager == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if !s.wsManager.IsUserOnline(userID) {
|
||||
return false
|
||||
}
|
||||
|
||||
if notification.CreatedAt == 0 {
|
||||
notification.CreatedAt = time.Now().UnixMilli()
|
||||
}
|
||||
|
||||
wsMsg := websocket.CreateWSMessage(websocket.MessageTypeNotification, notification)
|
||||
s.wsManager.SendToUser(userID, wsMsg)
|
||||
return true
|
||||
}
|
||||
|
||||
// PushAnnouncement 广播公告消息
|
||||
func (s *pushServiceImpl) PushAnnouncement(ctx context.Context, announcement *websocket.AnnouncementMessage) error {
|
||||
if s.wsManager == nil {
|
||||
return errors.New("websocket manager not available")
|
||||
}
|
||||
|
||||
if announcement.CreatedAt == 0 {
|
||||
announcement.CreatedAt = time.Now().UnixMilli()
|
||||
}
|
||||
|
||||
wsMsg := websocket.CreateWSMessage(websocket.MessageTypeAnnouncement, announcement)
|
||||
s.wsManager.Broadcast(wsMsg)
|
||||
return nil
|
||||
}
|
||||
|
||||
// PushSystemNotification 推送系统通知(使用独立的 SystemNotification 模型)
|
||||
func (s *pushServiceImpl) PushSystemNotification(ctx context.Context, userID string, notification *model.SystemNotification) error {
|
||||
// 首先尝试WebSocket推送
|
||||
@@ -531,45 +472,40 @@ func (s *pushServiceImpl) PushSystemNotification(ctx context.Context, userID str
|
||||
|
||||
// pushSystemNotificationViaWebSocket 通过WebSocket推送系统通知
|
||||
func (s *pushServiceImpl) pushSystemNotificationViaWebSocket(ctx context.Context, userID string, notification *model.SystemNotification) bool {
|
||||
if s.wsManager == nil {
|
||||
if s.sseHub == nil || !s.sseHub.HasSubscribers(userID) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !s.wsManager.IsUserOnline(userID) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 构建 WebSocket 通知消息
|
||||
wsNotification := &websocket.NotificationMessage{
|
||||
ID: fmt.Sprintf("%d", notification.ID),
|
||||
Type: string(notification.Type),
|
||||
Title: notification.Title,
|
||||
Content: notification.Content,
|
||||
Extra: make(map[string]interface{}),
|
||||
CreatedAt: notification.CreatedAt.UnixMilli(),
|
||||
sseNotification := map[string]interface{}{
|
||||
"id": fmt.Sprintf("%d", notification.ID),
|
||||
"type": string(notification.Type),
|
||||
"title": notification.Title,
|
||||
"content": notification.Content,
|
||||
"extra": map[string]interface{}{},
|
||||
"created_at": notification.CreatedAt.UnixMilli(),
|
||||
}
|
||||
|
||||
// 填充额外数据
|
||||
if notification.ExtraData != nil {
|
||||
wsNotification.Extra["actor_id_str"] = notification.ExtraData.ActorIDStr
|
||||
wsNotification.Extra["actor_name"] = notification.ExtraData.ActorName
|
||||
wsNotification.Extra["avatar_url"] = notification.ExtraData.AvatarURL
|
||||
wsNotification.Extra["target_id"] = notification.ExtraData.TargetID
|
||||
wsNotification.Extra["target_type"] = notification.ExtraData.TargetType
|
||||
wsNotification.Extra["action_url"] = notification.ExtraData.ActionURL
|
||||
wsNotification.Extra["action_time"] = notification.ExtraData.ActionTime
|
||||
extra := sseNotification["extra"].(map[string]interface{})
|
||||
extra["actor_id_str"] = notification.ExtraData.ActorIDStr
|
||||
extra["actor_name"] = notification.ExtraData.ActorName
|
||||
extra["avatar_url"] = notification.ExtraData.AvatarURL
|
||||
extra["target_id"] = notification.ExtraData.TargetID
|
||||
extra["target_type"] = notification.ExtraData.TargetType
|
||||
extra["action_url"] = notification.ExtraData.ActionURL
|
||||
extra["action_time"] = notification.ExtraData.ActionTime
|
||||
|
||||
// 设置触发用户信息
|
||||
if notification.ExtraData.ActorIDStr != "" {
|
||||
wsNotification.TriggerUser = &websocket.NotificationUser{
|
||||
ID: notification.ExtraData.ActorIDStr,
|
||||
Username: notification.ExtraData.ActorName,
|
||||
Avatar: notification.ExtraData.AvatarURL,
|
||||
sseNotification["trigger_user"] = map[string]interface{}{
|
||||
"id": notification.ExtraData.ActorIDStr,
|
||||
"username": notification.ExtraData.ActorName,
|
||||
"avatar": notification.ExtraData.AvatarURL,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wsMsg := websocket.CreateWSMessage(websocket.MessageTypeNotification, wsNotification)
|
||||
s.wsManager.SendToUser(userID, wsMsg)
|
||||
s.sseHub.PublishToUser(userID, "system_notification", sseNotification)
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user