大项目总结(5) | 青训营笔记

120 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 13 天

这篇笔记是大项目的第五次总结,这次主要总结了对于定时轮询消息查询接口,获取最新消息列表的消息页面,如何完成消息页面的稳定获取。

直接返回messagesList

如果直接在后端将messagesList传输给前端,就会出现这种情况:刚点进消息页面的时候能正常显示消息,但由于前端页面定时轮询消息查询接口,后端的chat()函数被不断调用,messagesList被不断地传输至前端,就会导致消息列表一遍又一遍地重复呈现在前端。为了解决这种情况,需要有别的方法。

设置key为chatKey,value为消息记录的map

chatKey是用户A和用户B进行聊天时生成的一个key,这个key用于获取用户A和用户B的聊天记录,它定义如下:

func genChatKey(userIdA int64, userIdB int64) string {
   if userIdA > userIdB {
      return fmt.Sprintf("%d_%d", userIdB, userIdA)
   }
   return fmt.Sprintf("%d_%d", userIdA, userIdB)
}

每次调用chat函数时,检查用户A和B当前聊天记录的长度是否和map中记录的聊天记录长度相等,如果相等,则说明消息记录已经是最新的,不需要更新,可以传空集回前端;如果不相等,说明聊天记录有更新,需要传输最新的聊天记录集合给前端。最后,将最新的聊天记录更新至map中。为了完成这个方法,我写了个getNewMessages()函数,如下:

// 获得新的消息记录
func GetNewMessages(userIdA model.UserId, UserIdB model.UserId, newMessagesNums int) ([]model.Msg, error) {
   if newMessagesNums <= 0 {
      return []model.Msg{}, nil
   }
   res := make([]model.Msg, 0)
   rows := make([]model.Msg, 0)
   err := DB.Transaction(func(tx *gorm.DB) error {
      err := tx.Debug().Table("messages").
         Where("to_user_id IN (?) AND from_user_id IN (?)", []int64{userIdA, UserIdB}, []int64{userIdA, UserIdB}).
         Order("create_time desc").
         Limit(newMessagesNums).Find(&rows)
      if err != nil {
         return err.Error
      }
      return nil
   })
   //需要反着遍历数组,因为获得的后n个聊天记录是按时间倒叙排的,现在需要正着排
   length := len(rows)
   for i := length - 1; i >= 0; i-- {
      row := rows[i]
      if createTime_unix, err := TimeToUnix(row.CreateTime); err != nil {
         return nil, err
      } else {
         //将带有Unix时间戳的结构体放入res中
         r := model.Msg{
            Id:         row.Id,
            ToUserId:   row.ToUserId,
            FromUserId: row.FromUserId,
            Content:    row.Content,
            CreateTime: createTime_unix,
         }
         res = append(res, r)
      }
   }
   return res, err
}

其中newMessagesNums就是需要更新的聊天记录条数。

chat函数如下编写:

chatKey := genChatKey(userId, int64(userIdB))
// 读取消息
messages, _ := dal.GetMessages(userId, int64(userIdB))
ChatLength := len(messages) //得到当前聊天的长度,如果长度没变就不更新
if _, mapExist := tempChat[chatKey]; mapExist {
   //map存在时,说明已经写入过消息记录
   newMessagesNums := ChatLength - len(tempChat[chatKey])
   //获得最新的消息记录
   if newMessages, err := dal.GetNewMessages(userId, int64(userIdB), newMessagesNums); err != nil {
      return
   } else {
      finalMessages = newMessages
   }
} else {
   // map不存在
   finalMessages = messages
}

//聊天记录长度存入map
//ChatKey_ChatLength[chatKey] = len(messages)

//聊天记录存入tempChat
tempChat[chatKey] = messages

//遍历finalMessages,就是最新更新的消息
for _, message := range finalMessages {
   chatEvent := model.MessageSendEvent{
      UserId:     message.FromUserId,
      ToUserId:   message.ToUserId,
      MsgContent: message.Content,
   }
   chatData, _ := json.Marshal(chatEvent)
   _, err = conn.Write(chatData)
   if err != nil {
      fmt.Println("Error writing:", err.Error())
      return
   }
}
c.JSON(http.StatusOK, ChatResponse{Response: model.Response{StatusCode: 0}, MessageList: finalMessages})

这样就可以防止定时轮询消息查询接口中重复出现消息记录的问题。

此外,由于退出消息页面再进来时map并没有清空,所以会出现没有不显示消息记录的问题,这里可以通过检测用户在离开时,消息服务器断开,同时清空map中对应的聊天记录解决。

以上就是我对如何完成消息页面的稳定获取的总结。