feat(api): implement exam and grade synchronization modules
Add new functionality for managing and synchronizing academic grades and exam schedules. This includes new models, repositories, services, and HTTP handlers, along with updated gRPC definitions and dependency injection configuration. Additionally, improve the reliability of unread message count tracking by implementing an atomic Lua script for Redis operations in the conversation cache.
This commit is contained in:
@@ -387,6 +387,36 @@ func (s *chatServiceImpl) SendMessage(ctx context.Context, senderID string, conv
|
||||
// 获取会话中的参与者并发送消息
|
||||
participants, err := s.getParticipants(ctx, conversationID)
|
||||
if err == nil {
|
||||
// 先更新未读计数器,再推送 WS 事件,确保事件中的 total_unread 已包含新消息
|
||||
if s.conversationCache != nil {
|
||||
for _, p := range participants {
|
||||
if p.UserID == senderID {
|
||||
continue
|
||||
}
|
||||
if incrErr := s.conversationCache.IncrementUnread(context.Background(), p.UserID, conversationID); incrErr != nil {
|
||||
zap.L().Warn("increment unread from redis hash failed",
|
||||
zap.String("userID", p.UserID),
|
||||
zap.String("convID", conversationID),
|
||||
zap.Error(incrErr),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
keys := make([]string, 0, len(participants)*2)
|
||||
for _, p := range participants {
|
||||
keys = append(keys,
|
||||
cache.UnreadDetailKey(p.UserID, conversationID),
|
||||
cache.UnreadConversationKey(p.UserID),
|
||||
)
|
||||
}
|
||||
if len(keys) > 0 {
|
||||
s.cache.DeleteBatch(keys)
|
||||
}
|
||||
for _, p := range participants {
|
||||
s.conversationCache.InvalidateConversationList(p.UserID)
|
||||
}
|
||||
}
|
||||
|
||||
targetIDs := make([]string, 0, len(participants))
|
||||
for _, p := range participants {
|
||||
if conv.Type == model.ConversationTypePrivate && p.UserID == senderID {
|
||||
@@ -422,36 +452,6 @@ func (s *chatServiceImpl) SendMessage(ctx context.Context, senderID string, conv
|
||||
}
|
||||
}
|
||||
|
||||
if s.conversationCache != nil {
|
||||
// 使用 Redis Hash 预计算未读数
|
||||
for _, p := range participants {
|
||||
if p.UserID == senderID {
|
||||
continue
|
||||
}
|
||||
if incrErr := s.conversationCache.IncrementUnread(context.Background(), p.UserID, conversationID); incrErr != nil {
|
||||
zap.L().Warn("increment unread from redis hash failed",
|
||||
zap.String("userID", p.UserID),
|
||||
zap.String("convID", conversationID),
|
||||
zap.Error(incrErr),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
keys := make([]string, 0, len(participants)*2)
|
||||
for _, p := range participants {
|
||||
keys = append(keys,
|
||||
cache.UnreadDetailKey(p.UserID, conversationID),
|
||||
cache.UnreadConversationKey(p.UserID),
|
||||
)
|
||||
}
|
||||
if len(keys) > 0 {
|
||||
s.cache.DeleteBatch(keys)
|
||||
}
|
||||
for _, p := range participants {
|
||||
s.conversationCache.InvalidateConversationList(p.UserID)
|
||||
}
|
||||
}
|
||||
|
||||
if s.pushSvc != nil && len(participants) > 0 {
|
||||
sender := ChatMessageSender{ID: senderID}
|
||||
if senderUser, sErr := s.userRepo.GetByID(senderID); sErr == nil {
|
||||
@@ -695,7 +695,8 @@ func (s *chatServiceImpl) GetAllUnreadCount(ctx context.Context, userID string)
|
||||
maxSeqs[conv.ID] = maxSeq
|
||||
}
|
||||
}
|
||||
if len(maxSeqs) > 0 {
|
||||
// 只有全部会话的 msg_seq 都命中时才用算术结果,避免部分缺失导致低估
|
||||
if len(maxSeqs) == len(convIDs) {
|
||||
if total, err := s.conversationCache.ComputeAllUnreadCount(ctx, userID, convIDs, maxSeqs); err == nil {
|
||||
return total, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user