用户聊天消息处理(消息分组、未读消息、已读清除等)

每条消息都要设置一个是否已读的属性

消息分组:
	后台返回和当前用户相关的所有消息,根据当前用户和不同用户直接的聊天标识,
	返回对应的分组消息列表
	
未读消息:
	后台返回所有的消息后,根据和不同用户的聊天标识,获取相应的聊天内容,
	遍历每一条聊天内容,读取每一条的是否已读属性,来确定未读的总消息

已读消息:
	当进入聊天界面后,就通过后台修改所有的读取状态属性为已读的消息,
	然后返回修改的条数,前台根据修改的条数和总条数来计算剩余未读的总消息

代码示例:
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);
            })
        })
    })
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值