refactor(server): decouple services and improve architecture
- Introduce interfaces for all major services (JWT, PostAI, Comment, Message, Notification, QRCodeLogin, Upload, Vote, etc.) to support dependency inversion. - Move query parameters from `internal/dto` to a new `internal/query` package to separate request payloads from data transfer objects. - Refactor `internal/router` to use embedded `RouterDeps` for cleaner dependency management. - Decouple handlers from repositories by injecting services instead of direct repository access, ensuring proper layering. - Improve database initialization by moving it from `internal/model` to `internal/database`. - Optimize message decryption by implementing a more efficient `BatchDecrypt` method in `MessageEncryptor` using a worker pool. - Enhance error handling and security by implementing fail-fast checks for encryption key length during startup. - Clean up unused code, including the `avatar` package and several unused DTOs.
This commit is contained in:
@@ -22,8 +22,26 @@ const (
|
||||
CacheJitterRatio = 0.1
|
||||
)
|
||||
|
||||
// MessageService 消息服务
|
||||
type MessageService struct {
|
||||
// MessageService 消息服务接口
|
||||
type MessageService interface {
|
||||
SendMessage(ctx context.Context, senderID, receiverID string, segments model.MessageSegments) (*model.Message, error)
|
||||
GetConversations(ctx context.Context, userID string, page, pageSize int) ([]*model.Conversation, int64, error)
|
||||
GetMessages(ctx context.Context, conversationID string, page, pageSize int) ([]*model.Message, int64, error)
|
||||
GetMessagesAfterSeq(ctx context.Context, conversationID string, afterSeq int64, limit int) ([]*model.Message, error)
|
||||
MarkAsRead(ctx context.Context, conversationID string, userID string, lastReadSeq int64) error
|
||||
GetUnreadCount(ctx context.Context, conversationID string, userID string) (int64, error)
|
||||
GetOrCreateConversation(ctx context.Context, user1ID, user2ID string) (*model.Conversation, error)
|
||||
GetConversationParticipants(conversationID string) ([]*model.ConversationParticipant, error)
|
||||
GetParticipantsBatch(ctx context.Context, convIDs []string) (map[string][]*model.ConversationParticipant, error)
|
||||
GetMyParticipantsBatch(ctx context.Context, convIDs []string, userID string) (map[string]*model.ConversationParticipant, error)
|
||||
InvalidateUserConversationCache(userID string)
|
||||
InvalidateUserUnreadCache(userID, conversationID string)
|
||||
GetMessagesByCursor(ctx context.Context, conversationID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Message], error)
|
||||
GetConversationsByCursor(ctx context.Context, userID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Conversation], error)
|
||||
}
|
||||
|
||||
// messageService 消息服务实现
|
||||
type messageService struct {
|
||||
// 基础仓储
|
||||
messageRepo repository.MessageRepository
|
||||
|
||||
@@ -33,11 +51,11 @@ type MessageService struct {
|
||||
// 基础缓存(用于简单缓存操作)
|
||||
baseCache cache.Cache
|
||||
|
||||
uploadService *UploadService
|
||||
uploadService UploadService
|
||||
}
|
||||
|
||||
// NewMessageService 创建消息服务
|
||||
func NewMessageService(messageRepo repository.MessageRepository, cacheBackend cache.Cache, uploadService *UploadService) *MessageService {
|
||||
func NewMessageService(messageRepo repository.MessageRepository, cacheBackend cache.Cache, uploadService UploadService) MessageService {
|
||||
// 创建适配器
|
||||
convRepoAdapter := cache.NewConversationRepositoryAdapter(messageRepo)
|
||||
msgRepoAdapter := cache.NewMessageRepositoryAdapter(messageRepo)
|
||||
@@ -51,7 +69,7 @@ func NewMessageService(messageRepo repository.MessageRepository, cacheBackend ca
|
||||
nil,
|
||||
)
|
||||
|
||||
return &MessageService{
|
||||
return &messageService{
|
||||
messageRepo: messageRepo,
|
||||
conversationCache: conversationCache,
|
||||
baseCache: cacheBackend,
|
||||
@@ -67,7 +85,7 @@ type ConversationListResult struct {
|
||||
|
||||
// SendMessage 发送消息(使用 segments)
|
||||
// senderID 和 receiverID 参数为 string 类型(UUID格式),与JWT中user_id保持一致
|
||||
func (s *MessageService) SendMessage(ctx context.Context, senderID, receiverID string, segments model.MessageSegments) (*model.Message, error) {
|
||||
func (s *messageService) SendMessage(ctx context.Context, senderID, receiverID string, segments model.MessageSegments) (*model.Message, error) {
|
||||
// 获取或创建会话
|
||||
conv, err := s.messageRepo.GetOrCreatePrivateConversation(senderID, receiverID)
|
||||
if err != nil {
|
||||
@@ -127,7 +145,7 @@ func (s *MessageService) SendMessage(ctx context.Context, senderID, receiverID s
|
||||
}
|
||||
|
||||
// cacheMessage 缓存消息(内部方法)
|
||||
func (s *MessageService) cacheMessage(ctx context.Context, convID string, msg *model.Message) error {
|
||||
func (s *messageService) cacheMessage(ctx context.Context, convID string, msg *model.Message) error {
|
||||
if s.conversationCache == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -140,7 +158,7 @@ func (s *MessageService) cacheMessage(ctx context.Context, convID string, msg *m
|
||||
|
||||
// GetConversations 获取会话列表(带缓存)
|
||||
// userID 参数为 string 类型(UUID格式),与JWT中user_id保持一致
|
||||
func (s *MessageService) GetConversations(ctx context.Context, userID string, page, pageSize int) ([]*model.Conversation, int64, error) {
|
||||
func (s *messageService) GetConversations(ctx context.Context, userID string, page, pageSize int) ([]*model.Conversation, int64, error) {
|
||||
// 优先使用 ConversationCache
|
||||
if s.conversationCache != nil {
|
||||
return s.conversationCache.GetConversationList(ctx, userID, page, pageSize)
|
||||
@@ -190,7 +208,7 @@ func (s *MessageService) GetConversations(ctx context.Context, userID string, pa
|
||||
}
|
||||
|
||||
// GetMessages 获取消息列表(带缓存)
|
||||
func (s *MessageService) GetMessages(ctx context.Context, conversationID string, page, pageSize int) ([]*model.Message, int64, error) {
|
||||
func (s *messageService) GetMessages(ctx context.Context, conversationID string, page, pageSize int) ([]*model.Message, int64, error) {
|
||||
// 优先使用 ConversationCache
|
||||
if s.conversationCache != nil {
|
||||
return s.conversationCache.GetMessages(ctx, conversationID, page, pageSize)
|
||||
@@ -201,13 +219,13 @@ func (s *MessageService) GetMessages(ctx context.Context, conversationID string,
|
||||
}
|
||||
|
||||
// GetMessagesAfterSeq 获取指定seq之后的消息(增量同步)
|
||||
func (s *MessageService) GetMessagesAfterSeq(ctx context.Context, conversationID string, afterSeq int64, limit int) ([]*model.Message, error) {
|
||||
func (s *messageService) GetMessagesAfterSeq(ctx context.Context, conversationID string, afterSeq int64, limit int) ([]*model.Message, error) {
|
||||
return s.messageRepo.GetMessagesAfterSeq(conversationID, afterSeq, limit)
|
||||
}
|
||||
|
||||
// MarkAsRead 标记为已读(使用 Cache-Aside 模式)
|
||||
// userID 参数为 string 类型(UUID格式),与JWT中user_id保持一致
|
||||
func (s *MessageService) MarkAsRead(ctx context.Context, conversationID string, userID string, lastReadSeq int64) error {
|
||||
func (s *messageService) MarkAsRead(ctx context.Context, conversationID string, userID string, lastReadSeq int64) error {
|
||||
// 1. 先写入DB(保证数据一致性,DB是唯一数据源)
|
||||
err := s.messageRepo.UpdateLastReadSeq(conversationID, userID, lastReadSeq)
|
||||
if err != nil {
|
||||
@@ -230,7 +248,7 @@ func (s *MessageService) MarkAsRead(ctx context.Context, conversationID string,
|
||||
|
||||
// GetUnreadCount 获取未读消息数(带缓存)
|
||||
// userID 参数为 string 类型(UUID格式),与JWT中user_id保持一致
|
||||
func (s *MessageService) GetUnreadCount(ctx context.Context, conversationID string, userID string) (int64, error) {
|
||||
func (s *messageService) GetUnreadCount(ctx context.Context, conversationID string, userID string) (int64, error) {
|
||||
// 优先使用 ConversationCache
|
||||
if s.conversationCache != nil {
|
||||
return s.conversationCache.GetUnreadCount(ctx, userID, conversationID)
|
||||
@@ -268,7 +286,7 @@ func (s *MessageService) GetUnreadCount(ctx context.Context, conversationID stri
|
||||
|
||||
// GetOrCreateConversation 获取或创建私聊会话
|
||||
// user1ID 和 user2ID 参数为 string 类型(UUID格式),与JWT中user_id保持一致
|
||||
func (s *MessageService) GetOrCreateConversation(ctx context.Context, user1ID, user2ID string) (*model.Conversation, error) {
|
||||
func (s *messageService) GetOrCreateConversation(ctx context.Context, user1ID, user2ID string) (*model.Conversation, error) {
|
||||
conv, err := s.messageRepo.GetOrCreatePrivateConversation(user1ID, user2ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -282,7 +300,7 @@ func (s *MessageService) GetOrCreateConversation(ctx context.Context, user1ID, u
|
||||
}
|
||||
|
||||
// GetConversationParticipants 获取会话参与者列表
|
||||
func (s *MessageService) GetConversationParticipants(conversationID string) ([]*model.ConversationParticipant, error) {
|
||||
func (s *messageService) GetConversationParticipants(conversationID string) ([]*model.ConversationParticipant, error) {
|
||||
// 优先使用缓存
|
||||
if s.conversationCache != nil {
|
||||
return s.conversationCache.GetParticipants(context.Background(), conversationID)
|
||||
@@ -291,12 +309,12 @@ func (s *MessageService) GetConversationParticipants(conversationID string) ([]*
|
||||
}
|
||||
|
||||
// GetParticipantsBatch 批量获取多个会话的参与者列表
|
||||
func (s *MessageService) GetParticipantsBatch(ctx context.Context, convIDs []string) (map[string][]*model.ConversationParticipant, error) {
|
||||
func (s *messageService) GetParticipantsBatch(ctx context.Context, convIDs []string) (map[string][]*model.ConversationParticipant, error) {
|
||||
return s.messageRepo.GetParticipantsBatch(ctx, convIDs)
|
||||
}
|
||||
|
||||
// GetMyParticipantsBatch 批量获取用户在多个会话中的参与者信息
|
||||
func (s *MessageService) GetMyParticipantsBatch(ctx context.Context, convIDs []string, userID string) (map[string]*model.ConversationParticipant, error) {
|
||||
func (s *messageService) GetMyParticipantsBatch(ctx context.Context, convIDs []string, userID string) (map[string]*model.ConversationParticipant, error) {
|
||||
return s.messageRepo.GetMyParticipantsBatch(ctx, convIDs, userID)
|
||||
}
|
||||
|
||||
@@ -306,23 +324,23 @@ func ParseConversationID(idStr string) (string, error) {
|
||||
}
|
||||
|
||||
// InvalidateUserConversationCache 失效用户会话相关缓存(供外部调用)
|
||||
func (s *MessageService) InvalidateUserConversationCache(userID string) {
|
||||
func (s *messageService) InvalidateUserConversationCache(userID string) {
|
||||
s.conversationCache.InvalidateConversationList(userID)
|
||||
cache.InvalidateUnreadConversation(s.baseCache, userID)
|
||||
}
|
||||
|
||||
// InvalidateUserUnreadCache 失效用户未读数缓存(供外部调用)
|
||||
func (s *MessageService) InvalidateUserUnreadCache(userID, conversationID string) {
|
||||
func (s *messageService) InvalidateUserUnreadCache(userID, conversationID string) {
|
||||
cache.InvalidateUnreadConversation(s.baseCache, userID)
|
||||
s.conversationCache.InvalidateUnreadCount(userID, conversationID)
|
||||
}
|
||||
|
||||
// GetMessagesByCursor 游标分页获取会话消息
|
||||
func (s *MessageService) GetMessagesByCursor(ctx context.Context, conversationID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Message], error) {
|
||||
func (s *messageService) GetMessagesByCursor(ctx context.Context, conversationID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Message], error) {
|
||||
return s.messageRepo.GetMessagesByCursor(ctx, conversationID, req)
|
||||
}
|
||||
|
||||
// GetConversationsByCursor 游标分页获取用户会话列表
|
||||
func (s *MessageService) GetConversationsByCursor(ctx context.Context, userID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Conversation], error) {
|
||||
func (s *messageService) GetConversationsByCursor(ctx context.Context, userID string, req *cursor.PageRequest) (*cursor.CursorPageResult[*model.Conversation], error) {
|
||||
return s.messageRepo.GetConversationsByCursor(ctx, userID, req)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user