Não é mais mantido
Este projeto foi escrito quando eu estava estudando, e alguns lugares são mal legíveis (como um grande número de retornos de chamada na parte Mongo). Os alunos que precisam ler com cuidado, obrigado.
O código da filial está online e foi alterado de acordo. Se você ingressar em um grupo de bate -papo por padrão, e o back -end poderá se adaptar a alguns ajustes do código Linux, é recomendável puxar o mestre.
Observe que deve haver nó, npm e mongodb. O endereço IP do MongoDB padrão do projeto é 127.0.0.1:27017, que pode ser modificado no arquivo de configuração. (ChatServer utils database.js)
cd chatRoom
npm install 安装前端依赖
npm run build 编译前端代码
cd ..
cd chatServer
npm install 安装后端依赖
npm run create 初始化数据库(号码池、表情包)
npm start 启动服务
在浏览器中打开 localhost:9988 即可
O projeto começou porque o trabalho exigia uma função da sala de bate -papo, mas, por alguns motivos, finalmente escolhi Strophe.js com base no protocolo XMPP. Então, eu queria escrever um conjunto dele usando o nó. Originalmente, eu queria simplesmente escrever uma página de bate -papo, mas não fiquei satisfeito depois de escrevê -lo, então continuei refatorando -o (parece que posso entender por que o gerente de produto sempre mudou as necessidades).
Muitas coisas, como o MongoDB, também são a primeira vez que as uso. Eu já fui exposto ao MySQL antes. Então eu aprendi e escrevi ao mesmo tempo. Usei meu tempo livre para escrever intermitentemente por vários meses (desta vez falei sobre a versão v0.9.0, e o projeto ainda está sendo atualizado ...), que inclui um conjunto completo de interações front-end e back-end. A interface do usuário vem de meus próprios sentimentos e não tem talento de design (então, quando se trata de trocar de temas, é realmente difícil de projetar ~), pulverizando levemente--. Ainda existem muitas áreas que precisam ser otimizadas e melhoradas no projeto. Todos são convidados a mencionar questões (existe um grupo Q no final do artigo, e você pode aprender e se comunicar juntos).
Menos fofocas, este artigo fala principalmente sobre o processo de design do projeto e algumas idéias de implementação funcional. Students who are interested in the project please move to the source code Vchat - from head to toe toe toe toe toe toe toe toe toe toe toe toe toe toe toe toe toe toe toe toe toe toe toe toe toe toe to chat online.
* Este é o divisor --------------------------------------------------------------------------------------------------------------------------------
O front-end usa principalmente os baldes da família Vue, e não há nada a dizer, projetos de construção de andaimes, gerenciamento de estado de Vuex, roteamento de controle de router vue e axios para interação front-end. O back -end é um serviço baseado no nó e usa o Express. Por que não uso o KOA? É puramente por conveniência, porque Koa não está familiarizado (cobrindo o rosto). A coisa mais importante no bate -papo é, é claro, comunicação. O projeto usa soquete.io para comunicar a frente e o back -end.
O banco de dados é o MongoDB, que inclui principalmente usuários, amigos, bate -papos em grupo, mensagens, expressões, conjuntos de números etc.
Visão geral do recurso
Estrutura de diretório
// 前端
├─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 // 主题
└─vchatQuando um usuário se registra no VCHAT, um número de código será especificado aleatoriamente e esse número de código é retirado de um pool de números pré-gerado (o pool de números existe em MongoDB). O segmento de número inicial especificado como 10000001-10001999 é o código do usuário, e o segmento numérico de 100001-100999 é o código de bate-papo em grupo. Os usuários podem fazer login com o número ou conta do código.
// 号码池设计
* 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 封面列表Ao se registrar, você precisa determinar se a conta já existe e o código obtido aleatoriamente precisa ser marcado como sendo usado no pool de números, e a senha do usuário é criptografada com MD5, etc.
// md5 密码加密
const md5 = pass => { // 避免多次调用MD5报错
let md5 = crypto . createHash ( 'md5' ) ;
return md5 . update ( pass ) . digest ( "hex" ) ;
} ;O login também exige determinar se o usuário registrou e suporta uma conta e código para fazer login.
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 } ) ;
}
} )
} ;Gerenciamento de permissão de login
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 )
} ) ;No vchat, os tipos de mensagens incluem amigos ou aplicativos em grupo, aplicação de resposta (consentimento ou rejeição), notificação de entrada em grupo, mensagem de bate -papo (texto, imagens, expressões, arquivos)
Antes de implementar o envio de mensagens, você precisa ter um entendimento geral de algumas APIs
socket.io. Para documentação detalhada da API, você pode ver o soquete.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' ) ;Junte -se à sala na lista de sessões. A lista de sessões será adicionada automaticamente quando o aplicativo de amigos for bem -sucedido ou o grupo for adicionado com sucesso. Mas você também pode remover ou adicionar manualmente, e não receberá mais mensagens sobre a sessão removida (semelhante ao bloqueio).
// 前端 发起加入房间的请求
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 ) ; // 包括发送者
} ) ;
} ) ;Haverá um problema em ingressar em várias salas de bate -papo ao mesmo tempo. O soquete pode ingressar em várias salas e enviar mensagens para as salas especificadas, mas não distinguirá as salas ao aceitar mensagens. Em outras palavras, todas as mensagens da sala serão enviadas juntas ao cliente. Portanto, precisamos distinguir qual mensagem é de qual sala e distribuí -la. Isso requer um identificador de sala para filtrar, e o VCHAT usa o ID da sala.
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);
});
Todas as mensagens serão armazenadas no MongoDB. Ao mudar de salas, serão obtidas mensagens históricas. Quando estiver na sala atual, as últimas notícias só serão anexadas ao DOM e não serão recuperadas do banco de dados. A janela de bate -papo exibe apenas as 100 mais recentes mensagens por padrão, e mais mensagens podem ser visualizadas no histórico de bate -papo.
// 前端 获取指定房间的历史消息
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 inicial
A janela de bate -papo, pode ser arrastada ou ampliada, papel de parede de bate -papo e configurações de cores de texto.
Configurações pessoais
Espaço de aplicação
Este artigo fala principalmente sobre o design geral do VCHAT e a implementação de algumas funções principais. De fato, ainda existem muitas armadilhas no processo de redação de projetos, como consulta entre mesa, upload de arquivos, etc. Não entrarei em detalhes aqui e a atualizarei se tiver tempo no futuro. Se o vchat for útil para você, lembre -se de olhar ^_ ^.