用戶聊天消息處理(消息分組、未讀消息、已讀清除等)

每條消息都要設置一個是否已讀的屬性

消息分組:
	後臺返回和當前用戶相關的所有消息,根據當前用戶和不同用戶直接的聊天標識,
	返回對應的分組消息列表
	
未讀消息:
	後臺返回所有的消息後,根據和不同用戶的聊天標識,獲取相應的聊天內容,
	遍歷每一條聊天內容,讀取每一條的是否已讀屬性,來確定未讀的總消息

已讀消息:
	當進入聊天界面後,就通過後臺修改所有的讀取狀態屬性爲已讀的消息,
	然後返回修改的條數,前臺根據修改的條數和總條數來計算剩餘未讀的總消息

代碼示例:
reducer.js:

//聊天狀態
const initChat={
    users:{},
    chatMsgs:[],
    unReadCount:0 //總未讀數量
}
function chat(state=initChat,action)
{
    switch(action.type)
    {
        case RECEIVE_MSG_LIST:  // data: {users, chatMsgs}
        const {users, chatMsgs, userid} = action.data
        console.log(userid)
        return {
          users,
          chatMsgs,
          unReadCount: chatMsgs.reduce((preTotal, msg) => preTotal+(!msg.read&&msg.to===userid?1:0),0)
        }
      case RECEIVE_MSG: // data: chatMsg
        const {chatMsg} = action.data
        return {
          users: state.users,
          chatMsgs: [...state.chatMsgs, chatMsg],
          unReadCount: state.unReadCount + (!chatMsg.read&&chatMsg.to===action.data.userid?1:0)
        }

        default:
            return state
    }
}

actions.js:

import {reqRegister,reqLogin,reqUpdateUser,reqUser,reqUserList,reqChatMsgList,reqReadMsg} from '../api/index'
import {AUTH_SUCCESS,ERROR_MSG,RECEIVE_USER,RESET_USER,RECEIVE_USERLIST,RECEIVE_MSG_LIST,RECEIVE_MSG,MSG_READ} from './action-types'
import io from 'socket.io-client'


//授權成功更改狀態
const authSuccess=(user)=>({type:AUTH_SUCCESS,data:user})
//失敗更改狀態
const errorMsg=(msg)=>({type:ERROR_MSG,data:msg})
//完善信息後更新
const receiveUser=(user)=>({type:RECEIVE_USER,data:user})
//重置用戶信息
export const resetUser=(msg)=>({type:RESET_USER,data:msg})

//接收用戶列表
const receiveUserList=(userlist)=>({type:RECEIVE_USERLIST,data:userlist})

//接收消息列表
const receiveMsgList=({users,chatMsgs,userid})=>({type:RECEIVE_MSG_LIST,data:{users,chatMsgs,userid}})

//接收單個消息
const  receiveMsg=(chatMsg,userid)=>({type:RECEIVE_MSG,data:{chatMsg,userid}})

//註冊
export const register=(user)=>{ 
   const {username,password,password2,type} =user; 

   //表單前臺驗證,返回對應的同步action
   if(!username||!password||!password2)
   {
        return errorMsg('用戶名或密碼不爲空');
   }
   if(password!==password2)
   {
        return errorMsg('兩次密碼不一致');
   }
     
   return async (dispatch)=>{
    

     const response=await reqRegister({username,password,type})
     const res=response.data;
     if(res.code===0) //成功
     {
          getMsgList(dispatch,res.data._id);
          dispatch(authSuccess(res.data));
     }else{
          dispatch(errorMsg(res.msg));
     }
 }
}

//登錄
export const login=(user)=>{
     const{username,password}=user

     //表單前臺驗證,返回對應的action
     if(!username||!password)
     {
          return errorMsg('用戶名或密碼不爲空');
     }

     return async (dispatch)=>{
          const response=await reqLogin(user)
          const res=response.data;

          if(res.code===0) //成功
          {
               getMsgList(dispatch,res.data.user._id);
               dispatch(authSuccess(res.data.user));
          }else{
               dispatch(errorMsg(res.msg));
          }
  }
 }

 //用戶完善頭像等信息後更新用戶
 export const updateUser=(user)=>{
      
      return async (dispatch)=>{

          const response=await reqUpdateUser(user)
          const res=response.data;
          if(res.code===0)
          {
               dispatch(receiveUser(res.data));
          }else{
               dispatch(resetUser(res.msg));
          }
      }
 }

 //獲取用戶異步action
 export const getUser=()=>{
      return async(dispatch)=>{
           const response =await reqUser();
           const res=response.data;
           if(res.code===0){
               getMsgList(dispatch,res.data._id);
               dispatch(receiveUser(res.data))
           }else{
               dispatch(resetUser(res.msg));
           }
      }
 }

 //獲取用戶列表
 export const getUserList=(type)=>{
      return async dispatch=>{
          const response =await reqUserList(type);
          const res=response.data;
          if(res.code===0)
          {
               dispatch(receiveUserList(res.data))
          }
      }
 }
 /**
  * 單例模式創建一個socket,利用全局對象io新增屬性來實現
  */

 function initIO(dispatch,userid)
 {
      if(!io.socket){
          io.socket=io('ws://localhost:4000')
          io.socket.on('receiveMsg', function (chatMsg) {
               console.log('瀏覽器端接收到消息:', chatMsg)
               //只有當前會話才分發action保存
               if(userid==chatMsg.from||userid==chatMsg.to)
               {
                    dispatch(receiveMsg(chatMsg,userid))
               }
          })
      }
 
 }

 //異步發送消息
 export const sendMsg=({from,to,content})=>{
      return dispatch=>{
          initIO();
          console.log('瀏覽器發送消息',{from,to,content})
          io.socket.emit('sendMsg',{from,to,content})
      }
 }
//異步獲取消息函數
 async function getMsgList(dispatch,userid)
 {
     initIO(dispatch,userid);
     const response =await reqChatMsgList();
     const res=response.data

     if(res.code===0)
     {
          const{users,chatMsgs}=res.data
          dispatch(receiveMsgList({users,chatMsgs,userid}))

     }
 }

action-type.js:


//註冊/登錄成功
export const AUTH_SUCCESS='auth_success'

//錯誤提示信息
export const ERROR_MSG='error_msg'

//用戶信息完善
export const RECEIVE_USER='receive_user'

//重置用戶
export const RESET_USER='reset_user'

//接收用戶列表
export const RECEIVE_USERLIST='receive_userlist'

//接收消息列表
export const RECEIVE_MSG_LIST='receive_msg_list'

//接收到新的一條消息
export const RECEIVE_MSG='receive_msg'

//讀取消息
export const MSG_READ='msg_read'

api接口:

/*
包含n個接口請求的函數
*/
import ajax from './ajax'

//註冊接口
export const reqRegister=(user)=>ajax('/register',user,'POST');

//登錄接口
export const reqLogin=({username,password})=>ajax('/login',{username,password},'POST');

//更新用戶接口
export const reqUpdateUser=(user)=>ajax('/update',user,'POST');

//獲取用戶信息
export const reqUser=()=>ajax('/user')

//獲取用戶列表
export const reqUserList=(type)=>ajax('/userlist',{type:type})

//獲取聊天消息
export const reqChatMsgList=()=>ajax('/msglist')

//修改消息爲已讀
export const reqReadMsg=(from)=>ajax('/readMsg',{from},'POST')

api封裝:

/**
 ajax模塊,返回值爲promise對象
 */
import axios from 'axios'
import qs from 'qs'

 export default function ajax(url,data={},type='GET')
 {
    //  url='http://127.0.0.1:4000'+url;
     if(type==='GET')
     {
         let str=''
        //將對象拼成url參數對
        Object.keys(data).forEach(function(item,index){
            str+=item+'='+data[item]+'&'
        })

        //去掉最後一個&或根本無參數
        if(str)
        {
            str=str.substring(0,str.length-1)
            str='?'+str;
        }
        return axios.get(url+str)

     }else{
        return axios.post(url,qs.stringify(data))
     }
    
 }

後臺消息有關路由:

let express = require('express')
var bodyParser = require('body-parser');
var md5=require('blueimp-md5');
var cookieParser = require('cookie-parser');

var urlencodedParser = bodyParser.urlencoded({ extended: false });

const {UserModel,ChatModel} =require('./db/models');
const filter={password:0,__v:0};

var app=express();

//socket.io
var server = require('http').Server(app);
require('./socketio/test')(server);

server.listen(4000,function(){
    console.log('this express server is running at http://127.0.0.1:4000 ');
});

app.use(cookieParser());


/*
獲取當前用戶所有相關聊天信息列表
*/
app.get('/msglist', (req, res)=>{
    // 獲取 cookie 中的 userid
    const userid = req.cookies.userid
    console.log(userid);
    // 查詢得到所有 user 文檔數組
    UserModel.find(function (err, userDocs) {
    // 用對象存儲所有 user 信息: key 爲 user 的_id, val 爲 name 和 header 組成的 user 對象

        const users = {} // 對象容器
        //將數據庫的所有用戶,按照以user_id爲key,存入容器中
        userDocs.forEach(doc => {
            users[doc._id] = {username: doc.username, header: doc.header}
        })
        /*
        查詢 userid 相關的所有聊天信息
        參數 1: 查詢條件
        參數 2: 過濾條件
        參數 3: 回調函數
        */

        //根據兩個用戶其中一個userid,返回所有聊天內容
        ChatModel.find({'$or': [{from: userid}, {to: userid}]}, filter, function (err,chatMsgs) {
            // 返回包含所有用戶和當前用戶相關的所有聊天消息的數據
            res.send({code: 0, data: {users, chatMsgs}})
        })
    })
})

    /*
    修改指定消息爲已讀
    */
app.post('/readmsg',urlencodedParser, function (req, res) {
    // 得到請求中的 from 和 to
    const from = req.body.from
    const to = req.cookies.userid
    console.log(from);
    /*
    更新數據庫中的 chat 數據
    參數 1: 查詢條件
    參數 2: 更新爲指定的數據對象
    參數 3: 是否 1 次更新多條, 默認只更新一條
    參數 4: 更新完成的回調函數
    */
    ChatModel.update({from, to, read: false}, {read: true}, {multi: true}, function (err,doc) {
        console.log('/readmsg', doc)
        res.send({code: 0, data: doc.nModified}) // 更新的數量
    })
})

socket.io後臺模塊:

const {ChatModel}=require('../db/models')

module.exports = function (server) {
    // 得到 IO 對象
    const io = require('socket.io')(server)

    //監視連接(當有一個客戶連接上時回調)
    io.on('connection', function (socket) {
        console.log('soketio connected')

        // 綁定 sendMsg 監聽, 接收客戶端發送的消息
        socket.on('sendMsg', function ({from,to,content}) {
            console.log('服務器接收客戶端發送',{from,to,content})
            //保存消息進數據庫,再將保存信息返回給前臺
            const chat_id=[from,to].sort().join('-');
            const create_time=Date.now();
            new ChatModel({from,to,content,chat_id,create_time}).save(function(err,chatMsg){
                //向客戶端發送數據
                io.emit('receiveMsg',chatMsg);
            })
        })
    })
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章