refactor(server): decouple services and improve architecture
All checks were successful
Build Backend / build (push) Successful in 4m55s
Build Backend / build-docker (push) Successful in 10m34s

- 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:
2026-06-15 03:41:59 +08:00
parent 9951043034
commit d9aa4b46c3
78 changed files with 2156 additions and 1640 deletions

View File

@@ -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)
}