Ya no mantenido
Este proyecto fue escrito cuando estudiaba, y algunos lugares son mal legibles (como una gran cantidad de devoluciones de llamada en la parte Mongo). Los estudiantes que lo necesitan lo leen con cuidado, gracias.
El código de la sucursal está en línea y se ha cambiado en consecuencia. Si se une a un grupo de chat de forma predeterminada, y el backend puede adaptarse a algunos ajustes de código de Linux, se recomienda extraer el maestro.
Tenga en cuenta que debe haber Node, NPM y MongoDB. La dirección IP MongoDB predeterminada del proyecto es 127.0.0.1:27017, que puede modificarse en el archivo de configuración. (chatserver utils database.js)
cd chatRoom
npm install 安装前端依赖
npm run build 编译前端代码
cd ..
cd chatServer
npm install 安装后端依赖
npm run create 初始化数据库(号码池、表情包)
npm start 启动服务
在浏览器中打开 localhost:9988 即可
El proyecto comenzó porque el trabajo requería una función de sala de chat, pero por algunas razones, finalmente elegí Strophe.js basado en el protocolo XMPP. Así que quería escribir un conjunto yo mismo usando el nodo. Originalmente quería simplemente escribir una página de chat, pero no estaba satisfecho después de escribirlo, así que seguí refactorizándolo (parece que puedo entender por qué el gerente de producto siempre cambió las necesidades).
Muchas cosas, como MongoDB, también son la primera vez que las uso. Solo he estado expuesto a mysql antes. Entonces aprendí y escribí al mismo tiempo. Utilicé mi tiempo libre para escribir intermitentemente durante varios meses (esta vez hablé sobre la versión V0.9.0, y el proyecto todavía se está actualizando ...), que incluye un conjunto completo de interacciones front-end y back-end. La interfaz de usuario proviene de mis propios sentimientos y no tiene talento de diseño (luego, cuando se trata de cambiar de temas, es realmente difícil de diseñar ~), a ligero rocío. Todavía hay muchas áreas que deben optimizar y mejorar en el proyecto. Todos son bienvenidos para mencionar los problemas (hay un grupo Q al final del artículo, y usted puede aprender y comunicarse juntos).
Menos chismes, este artículo habla principalmente sobre el proceso de diseño del proyecto y algunas ideas de implementación funcional. Estudiantes interesados en el proyecto Por favor, se muevan al código fuente VCHAT: desde la cabeza hasta el dedo del pie del pie del pie del pie del pie del pie del pie del pie del pie del pie del pie del pie del pie del pie del pie del pie del pie del pie del pie del pie del pie del pie del pie del pie del pie del pie del pie del pie del pie del pie del pie del pie del pie?
* Este es el divisor ----------------------------------------------------------------------------------------------------------------------
El front-end utiliza principalmente cubos de familia Vue, y no hay nada que decir, proyectos de construcción de andamios, gestión del estado VUEX, enrutamiento de control de enrutadores VUE y AXIOS para la interacción frontal. El backend es un servicio basado en el nodo y usa Express. ¿Por qué no uso KOA? Es puramente por conveniencia, porque Koa no está familiarizado con (cubriéndose la cara). Lo más importante en el chat es, por supuesto, comunicación. El proyecto usa Socket.io para comunicar el frente y la parte trasera.
La base de datos es MongoDB, que incluye principalmente usuarios, amigos, chats grupales, mensajes, expresiones, grupos de números, etc.
Descripción general
Estructura de directorio
// 前端
├─build
├─config
├─src
│ ├─api // 接口api
│ ├─assets // 静态资源
│ │ └─img
│ ├─directives // 全局指令
│ ├─libs // 全局组件
│ │ ├─bscroll
│ │ ├─dropdown
│ │ ├─icon
│ │ ├─nodata
│ │ ├─PhotoSwipe
│ │ ├─uploadPopover
│ │ └─vScroll
│ ├─router // 路由
│ ├─store // 状态管理
│ ├─utils // 方法
│ └─views
│ ├─applicationModel
│ │ ├─games // 游戏
│ │ │
│ │ └─sub // 应用
│ ├─components // 组件
│ │ ├─APlayer
│ │ ├─chat
│ │ ├─cropper
│ │ ├─DPlayer
│ │ └─header
│ ├─personalModel // 主页
│ │ ├─appModel // 天气等
│ │ ├─friendModel // 好友
│ │ └─groupModel // 群聊
│ └─settingModel // 设置
└─static
├─css // 样式文件
├─font // 字体文件
└─theme // 主题
└─vchatCuando un usuario se registra en VCHAT, se especificará aleatoriamente un número de código, y este número de código se toma de un grupo de números previamente generado (el grupo de números existe en MongoDB). El segmento de número inicial especificado como 10000001-10001999 es el código de usuario, y el segmento de números de 100001-100999 es el código de chat del grupo. Los usuarios pueden iniciar sesión con su número de código o cuenta.
// 号码池设计
* code 号码
* status 1 已使用 0 未使用
* type 1 用户 2 群聊
* random 随机数索引,用于随机查找某一条
// user表主要字段
* name 账号
* pass 密码
* avatar 头像
* signature 个性签名
* nickname 昵称
* email 邮件
* phone 手机
* sex 性别
* bubble 气泡
* projectTheme 项目主题
* wallpaper 聊天壁纸
* signUpTime 注册时间
* lastLoginTime 最后一次登录时间
* chatColor 聊天文字颜色
* province 省
* city 市
* town 县
* conversationsList 会话列表
* cover 封面列表Al registrarse, debe determinar si la cuenta ya existe, y el código obtenido aleatoriamente debe marcarse como utilizado en el grupo de números, y la contraseña del usuario está encriptada con MD5, etc.
// md5 密码加密
const md5 = pass => { // 避免多次调用MD5报错
let md5 = crypto . createHash ( 'md5' ) ;
return md5 . update ( pass ) . digest ( "hex" ) ;
} ;Iniciar sesión también requiere determinar si el usuario ha registrado y compatible con la cuenta y el código para iniciar sesión.
const login = ( params , callback ) => { // 登录
baseList . users
. find ( { // mongodb中可以直接用$or表示或关系
$or : [ { "name" : params . name } , { "code" : params . name } ]
} )
. then ( r => {
if ( r . length ) {
let pass = md5 ( params . pass ) ;
if ( r [ 0 ] [ 'pass' ] === pass ) {
//更新最后一次登录时间 此处直接写Date.now 会报错 需要Date.now()!!!;
baseList . users . update ( { name : params . name } , { lastLoginTime : Date . now ( ) } ) . then ( raw => {
console . log ( raw ) ;
} ) ;
callback ( { code : 0 , data : { name : r [ 0 ] . name , photo : r [ 0 ] . photo } } ) ;
} else {
callback ( { code : - 1 } ) ;
}
} else {
callback ( { code : - 1 } ) ;
}
} )
} ;Gestión de permiso de inicio de sesión
app . use ( '/v*' , ( req , res , next ) => {
if ( req . session . login ) {
next ( ) ;
} else {
if ( req . originalUrl === '/v/user/login' || req . originalUrl === '/v/user/signUp' ) {
next ( ) ;
} else {
res . json ( {
status : 0
} ) ;
}
}
} ) ; // http response 服务器响应拦截器,这里拦截未登录和401错误,并重新跳入登页重新获取token
instance . interceptors . response . use (
response => { // 拦截未登录
if ( response . data . status === 0 ) {
router . replace ( '/' ) ;
}
return response ;
} ,
error => {
if ( error . response ) {
switch ( error . response . status ) {
case 401 :
// 这里写清除token的代码
router . replace ( '/' ) ;
}
}
return Promise . reject ( error . response . data )
} ) ;En VCHAT, los tipos de mensajes incluyen amigos o aplicaciones grupales, aplicación de respuesta (consentimiento o rechazo), notificación de entrada grupal, mensaje de chat (texto, imágenes, expresiones, archivos)
Antes de implementar el envío de mensajes, debe tener una comprensión general de algunas API
socket.io. Para una documentación de API detallada, puede ver Socket.io
// 所有的消息请求都是建立在已连接的基础上的
io . on ( 'connect' , onConnect ) ;
// 发送给当前客户端
socket . emit ( 'hello' , 'can you hear me?' , 1 , 2 , 'abc' ) ;
// 发送给所有客户端,除了发送者
socket . broadcast . emit ( 'broadcast' , 'hello friends!' ) ;
// 发送给同在 'game' 房间的所有客户端,除了发送者
socket . to ( 'game' ) . emit ( 'nice game' , "let's play a game" ) ;
// 发送给同在 'game' 房间的所有客户端,包括发送者
io . in ( 'game' ) . emit ( 'big-announcement' , 'the game will start soon' ) ;Únase a la sala en la lista de sesiones. La lista de sesiones se agregará automáticamente cuando la aplicación de amigos sea exitosa o el grupo se agregue con éxito. Pero también puede eliminar o agregar manualmente, y ya no recibirá mensajes sobre la sesión eliminada (similar al bloqueo).
// 前端 发起加入房间的请求
this . conversationsList . forEach ( v => {
let val = {
name : this . user . name ,
time : utils . formatTime ( new Date ( ) ) ,
avatar : this . user . photo ,
roomid : v . id
} ;
this . $socket . emit ( 'join' , val ) ;
} ) ;
// 后端 接受请求后执行加入操作,记录每个房间加入的成员,以及回信告知指定房间已上线成员
socket . on ( 'join' , ( val ) => {
socket . join ( val . roomid , ( ) => {
if ( OnlineUser [ val . name ] ) {
return ;
}
OnlineUser [ val . name ] = socket . id ;
io . in ( val . roomid ) . emit ( 'joined' , OnlineUser ) ; // 包括发送者
} ) ;
} ) ;Habrá un problema para unirse a varias salas de chat al mismo tiempo. El socket puede unir varias habitaciones y enviar mensajes a las habitaciones especificadas, pero no distinguirá las habitaciones al aceptar mensajes. En otras palabras, todos los mensajes de la sala se enviarán juntos al cliente. Por lo tanto, debemos distinguir qué mensaje es de qué habitación y distribuirlo. Esto requiere un identificador de habitación para filtrar, y VCHAT usa la ID de habitación.
mes ( r ) { // 只有本房间的消息才展示
if ( r . roomid === this . currSation . id ) {
this . chatList . push ( Object . assign ( { } , r , { type : 'other' } ) ) ;
}
} // 前端
send(params, type = 'mess') { // 发送消息
if (!this.message && !params) {
return;
}
let val = {
name: this.user.name,
mes: this.message,
time: utils.formatTime(new Date()),
avatar: this.user.photo,
nickname: this.user.nickname,
read: [this.user.name],
roomid: this.currSation.id,
style: 'mess',
userM: this.user.id
};
this.chatList.push(Object.assign({},val,{type: 'mine'})); // 更新视图
this.$socket.emit('mes', val);
this.message = '';
}
// 后端 接收消息后存储到数据库,并转发给房间内其他成员,不包括发送者。
socket.on('mes', (val) => { // 聊天消息
apiList.saveMessage(val);
socket.to(val.roomid).emit('mes', val);
});
Todos los mensajes se almacenarán en MongoDB. Al cambiar de salas, se obtendrán mensajes históricos. Cuando esté en la sala actual, las últimas noticias solo se agregarán al DOM y no se recuperarán de la base de datos. La ventana de chat solo muestra los últimos 100 mensajes de forma predeterminada, y se pueden ver más mensajes en el historial de chat.
// 前端 获取指定房间的历史消息
this . $socket . emit ( 'getHistoryMessages' , { roomid : v . id , offset : 1 , limit : 100 } ) ;
// 后端 关联表、分页、排序
messages . find ( { roomid : params . roomid } )
. populate ( { path : 'userM' , select : 'signature photo nickname' } ) // 关联用户基本信息
. sort ( { 'time' : - 1 } )
. skip ( ( params . offset - 1 ) * params . limit )
. limit ( params . limit )
. then ( r => {
r . forEach ( v => { // 防止用户修改资料后,信息未更新
if ( v . userM ) {
v . nickname = v . userM . nickname ;
v . photo = v . userM . photo ;
v . signature = v . userM . signature ;
}
} ) ;
r . reverse ( ) ;
callback ( { code : 0 , data : r , count : count } ) ;
} ) . catch ( err => {
console . log ( err ) ;
callback ( { code : - 1 } ) ;
} ) ;Página de inicio
Ventana de chat, se puede arrastrar o zoom, papel tapiz de chat y configuración de color de texto.
Configuración personal
Espacio de aplicación
Este artículo habla principalmente sobre el diseño general de VCHAT y la implementación de algunas funciones principales. De hecho, todavía hay muchas dificultades en el proceso de escritura de proyectos, como consulta intermedio de mangostos, carga de archivos, etc. No entraré en detalles aquí, y lo actualizaré si tengo tiempo en el futuro. Si VCHAT es útil para usted, recuerde mirar ^_ ^.