Plus maintenu
Ce projet a été écrit lorsque j'étudiais, et certains endroits sont mal lisibles (comme un grand nombre de rappels dans la partie Mongo). Les étudiants qui en ont besoin le lisent attentivement, merci.
Le code de succursale est en ligne et a été modifié en conséquence. Si vous rejoignez un groupe de discussion par défaut et que le backend peut s'adapter à certains ajustements de code Linux, il est recommandé de retirer le maître.
Notez qu'il doit y avoir du nœud, du NPM et du MongoDB. L'adresse IP MongoDB par défaut du projet est 127.0.0.1:27017, qui peut être modifiée dans le fichier de configuration. (Chatserver utils database.js)
cd chatRoom
npm install 安装前端依赖
npm run build 编译前端代码
cd ..
cd chatServer
npm install 安装后端依赖
npm run create 初始化数据库(号码池、表情包)
npm start 启动服务
在浏览器中打开 localhost:9988 即可
Le projet a commencé parce que le travail nécessitait une fonction de salle de chat, mais pour certaines raisons, j'ai finalement choisi Strophe.js basé sur le protocole XMPP. Je voulais donc écrire moi-même un ensemble en utilisant le nœud. À l'origine, je voulais simplement écrire une page de discussion, mais je n'étais pas satisfait après l'avoir écrit, alors j'ai continué à le refactoriser (il semble que je puisse comprendre pourquoi le chef de produit a toujours changé les besoins).
Beaucoup de choses, comme MongoDB, sont également la première fois que je les utilise. Je n'ai été exposé qu'auparavant à MySQL. J'ai donc appris et écrit en même temps. J'ai utilisé mon temps libre pour écrire par intermittence pendant plusieurs mois (cette fois, j'ai parlé de la version V0.9.0, et le projet est toujours mis à jour ...), qui comprend un ensemble complet d'interactions frontales et back-end. L'interface utilisateur vient de mes propres sentiments et n'a pas de talent de conception (alors quand il s'agit de changer de thèmes, il est vraiment difficile de concevoir ~), légèrement pulvérisé. Il existe encore de nombreux domaines qui doivent être optimisés et améliorés dans le projet. Tout le monde est invité à mentionner les problèmes (il y a un groupe Q à la fin de l'article, et vous êtes les bienvenus pour apprendre et communiquer ensemble).
Moins de ragots, cet article parle principalement du processus de conception du projet et de certaines idées de mise en œuvre fonctionnelle. Les étudiants qui sont intéressés par le projet, veuillez passer au code source VCHAT - De la tête à l'orteil à pied d'orteil torte à pied d'orteil Toe Toe Toe Toe Toe Toe Toe Toe Toe Toe Toe Toe Toe Toe Toe Toe Toe Toe Toe pour discuter en ligne.
* Ceci est le diviseur ----------------------------------------------------------------------------------------------------------------------
Le frontal utilise principalement des seaux de la famille Vue, et il n'y a rien à dire, des projets de construction d'échafaudage, la gestion de l'État Vuex, le routage de contrôle des routeurs Vue et Axios pour l'interaction frontale. Le backend est un service basé sur le nœud et utilise Express. Pourquoi n'utilise pas KOA? C'est purement pour la commodité, car Koa ne connaît pas (couvrant son visage). La chose la plus importante dans le chat est bien sûr la communication. Le projet utilise Socket.io pour communiquer le front et l'arrière.
La base de données est MongoDB, qui comprend principalement les utilisateurs, les amis, les chats de groupe, les messages, les expressions, les pools de numéros, etc.
Présentation des fonctionnalités
Structure de répertoire
// 前端
├─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 // 主题
└─vchatLorsqu'un utilisateur s'inscrit dans VCHAT, un numéro de code sera spécifié au hasard et ce numéro de code est tiré d'un pool de nombres pré-généré (le pool de numéros existe dans MongoDB). Le segment de numéro initial spécifié comme 10000001-10001999 est le code utilisateur, et le segment numérique de 100001-100999 est le code de chat de groupe. Les utilisateurs peuvent se connecter avec leur numéro de code ou leur compte.
// 号码池设计
* 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 封面列表Lorsque vous vous inscrivez, vous devez déterminer si le compte existe déjà et le code obtenu au hasard doit être marqué comme étant utilisé dans le pool numérique, et le mot de passe utilisateur est chiffré avec MD5, etc.
// md5 密码加密
const md5 = pass => { // 避免多次调用MD5报错
let md5 = crypto . createHash ( 'md5' ) ;
return md5 . update ( pass ) . digest ( "hex" ) ;
} ;La connexion nécessite également de déterminer si l'utilisateur s'est inscrit et de prendre en charge le compte et le code pour se connecter.
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 } ) ;
}
} )
} ;Gestion de l'autorisation de connexion
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 )
} ) ;Dans VChat, les types de messages incluent des amis ou une application de groupe, une application de réponse (consentement ou rejet), notification d'entrée de groupe, message de chat (texte, images, expressions, fichiers)
Avant d'implémenter l'envoi de messages, vous devez avoir une compréhension générale de certaines API
socket.io. Pour une documentation API détaillée, vous pouvez afficher 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' ) ;Rejoignez la salle dans la liste des sessions. La liste des sessions sera automatiquement ajoutée lorsque l'application d'amis réussit ou que le groupe soit ajouté avec succès. Mais vous pouvez également supprimer ou ajouter manuellement, et vous ne recevrez plus de messages sur la session supprimée (similaire au blocage).
// 前端 发起加入房间的请求
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 ) ; // 包括发送者
} ) ;
} ) ;Il y aura un problème pour rejoindre plusieurs salons de discussion en même temps. La prise peut rejoindre plusieurs pièces et envoyer des messages aux pièces spécifiées, mais elle ne distinguera pas les pièces lors de l'acceptation des messages. En d'autres termes, tous les messages de la salle seront envoyés au client ensemble. Nous devons donc distinguer quel message est à partir de quelle pièce et la distribuer. Cela nécessite un identifiant de pièce pour filtrer et VChat utilise l'ID de la pièce.
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);
});
Tous les messages seront stockés dans MongoDB. Lors de la commutation de salles, des messages historiques seront obtenus. Lorsque dans la salle actuelle, les dernières nouvelles ne seront annexées au DOM et ne seront pas récupérées de la base de données. La fenêtre de chat affiche uniquement les 100 derniers messages par défaut, et plus de messages peuvent être affichés dans l'historique du 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 } ) ;
} ) ;Page d'accueil
La fenêtre de chat, peut être glisser ou zoom, le papier peint de chat et les paramètres de couleur de texte.
Paramètres personnels
Espace d'application
Cet article parle principalement de la conception globale de VChat et de la mise en œuvre de certaines fonctions principales. En fait, il y a encore de nombreux pièges dans le processus d'écriture de projets, tels que la requête inter-tableur Mongoose, le téléchargement de fichiers, etc. Je ne vais pas entrer dans les détails ici, et je le mettrai à jour si j'ai du temps à l'avenir. Si Vchat vous est utile, n'oubliez pas de regarder ^ _ ^.