Tidak lagi dipertahankan
Proyek ini ditulis ketika saya belajar, dan beberapa tempat tidak dapat dibaca dengan buruk (seperti sejumlah besar panggilan balik di bagian Mongo). Siswa yang membutuhkannya membacanya dengan cermat, terima kasih.
Kode cabang online dan telah diubah sesuai. Jika Anda bergabung dengan grup obrolan secara default, dan backend dapat beradaptasi dengan beberapa penyesuaian kode Linux, disarankan untuk menarik master.
Perhatikan bahwa harus ada Node, NPM dan MongoDB. Alamat IP MongoDB default dari proyek ini adalah 127.0.0.1:27017, yang dapat dimodifikasi dalam file konfigurasi. (ChatServer utils database.js)
cd chatRoom
npm install 安装前端依赖
npm run build 编译前端代码
cd ..
cd chatServer
npm install 安装后端依赖
npm run create 初始化数据库(号码池、表情包)
npm start 启动服务
在浏览器中打开 localhost:9988 即可
Proyek dimulai karena pekerjaan membutuhkan fungsi ruang obrolan, tetapi karena beberapa alasan, saya akhirnya memilih strophe.js berdasarkan protokol XMPP. Jadi saya ingin menulis satu set sendiri menggunakan node. Saya awalnya hanya ingin menulis halaman obrolan, tetapi saya tidak puas setelah menulisnya, jadi saya terus refactoring (sepertinya saya bisa mengerti mengapa manajer produk selalu mengubah kebutuhan).
Banyak hal, seperti MongoDB, juga merupakan pertama kalinya saya menggunakannya. Saya hanya terpapar ke MySQL sebelumnya. Jadi saya belajar dan menulis pada saat yang sama. Saya menggunakan waktu luang saya untuk menulis sebentar-sebentar selama beberapa bulan (kali ini saya berbicara tentang versi v0.9.0, dan proyek masih diperbarui ...), yang mencakup serangkaian interaksi front-end dan back-end lengkap. UI berasal dari perasaan saya sendiri dan tidak memiliki bakat desain (lalu ketika datang untuk beralih tema, sangat sulit untuk dirancang ~), semprotkan dengan ringan--. Masih ada banyak area yang perlu dioptimalkan dan ditingkatkan dalam proyek. Semua orang dipersilakan untuk menyebutkan masalah (ada grup Q di akhir artikel, dan Anda dipersilakan untuk belajar dan berkomunikasi bersama).
Lebih sedikit gosip, artikel ini terutama berbicara tentang proses desain proyek dan beberapa ide implementasi fungsional. Siswa yang tertarik dengan proyek ini, silakan pindah ke kode sumber Vchat - dari kepala ke toe toe toe toe toe toe toe toe toe toe toe toe toe toe toe toe untuk mengobrol online.
* Ini adalah pembagi --------------------------------------------------------------------------------------------------------------------------
Front-end terutama menggunakan ember keluarga Vue, dan tidak ada yang bisa dikatakan, proyek konstruksi perancah, manajemen negara bagian Vuex, rute kontrol vue-router, dan Axios untuk interaksi front-end. Backend adalah layanan berdasarkan Node dan menggunakan Express. Mengapa saya tidak menggunakan KOA? Ini murni untuk kenyamanan, karena Koa tidak terbiasa (menutupi wajahnya). Hal terpenting dalam obrolan adalah komunikasi tentu saja. Proyek ini menggunakan socket.io untuk berkomunikasi ujung depan dan belakang.
Basis data adalah MongoDB, yang terutama mencakup pengguna, teman, obrolan grup, pesan, ekspresi, kumpulan angka, dll.
Tinjauan Fitur
Struktur Direktori
// 前端
├─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 // 主题
└─vchatKetika pengguna mendaftar di VChat, nomor kode akan ditentukan secara acak, dan nomor kode ini diambil dari kumpulan angka yang telah dihasilkan sebelumnya (kumpulan jumlah ada di MongoDB). Segmen nomor awal yang ditentukan sebagai 10000001-10001999 adalah kode pengguna, dan segmen angka 100001-100999 adalah kode obrolan grup. Pengguna dapat masuk dengan nomor kode atau akun mereka.
// 号码池设计
* 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 封面列表Saat mendaftar, Anda perlu menentukan apakah akun sudah ada, dan kode yang diperoleh secara acak perlu ditandai sebagai digunakan dalam kumpulan angka, dan kata sandi pengguna dienkripsi dengan MD5, dll.
// md5 密码加密
const md5 = pass => { // 避免多次调用MD5报错
let md5 = crypto . createHash ( 'md5' ) ;
return md5 . update ( pass ) . digest ( "hex" ) ;
} ;Login juga memerlukan menentukan apakah pengguna telah mendaftar, dan mendukung akun dan kode untuk masuk.
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 } ) ;
}
} )
} ;Manajemen Izin Masuk
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 )
} ) ;Di VChat, jenis pesan termasuk aplikasi teman atau grup, aplikasi balasan (persetujuan atau penolakan), pemberitahuan entri grup, pesan obrolan (teks, gambar, ekspresi, file)
Sebelum menerapkan pengiriman pesan, Anda harus memiliki pemahaman umum tentang beberapa API
socket.io. Untuk dokumentasi API terperinci, Anda dapat melihat 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' ) ;Bergabunglah dengan ruangan dalam daftar sesi. Daftar sesi akan ditambahkan secara otomatis ketika aplikasi teman berhasil atau grup berhasil ditambahkan. Tetapi Anda juga dapat menghapus atau menambah secara manual, dan Anda tidak akan lagi menerima pesan tentang sesi yang dihapus (mirip dengan pemblokiran).
// 前端 发起加入房间的请求
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 ) ; // 包括发送者
} ) ;
} ) ;Akan ada masalah dengan bergabung dengan beberapa ruang obrolan secara bersamaan. Soket dapat bergabung dengan beberapa kamar dan mengirim pesan ke kamar yang ditentukan, tetapi tidak akan membedakan kamar saat menerima pesan. Dengan kata lain, semua pesan dari ruangan akan dikirim ke klien bersama. Jadi kita perlu membedakan pesan mana dari ruangan mana dan mendistribusikannya. Ini membutuhkan pengidentifikasi kamar untuk memfilter, dan Vchat menggunakan ID kamar.
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);
});
Semua pesan akan disimpan di MongoDB. Saat beralih kamar, pesan historis akan diperoleh. Ketika di kamar saat ini, berita terbaru hanya akan ditambahkan ke DOM dan tidak akan diambil dari database. Jendela obrolan hanya menampilkan 100 pesan terbaru secara default, dan lebih banyak pesan dapat dilihat dalam riwayat obrolan.
// 前端 获取指定房间的历史消息
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 } ) ;
} ) ;Halaman Beranda
Jendela obrolan, dapat diseret atau diperbesar, wallpaper obrolan dan pengaturan warna teks.
Pengaturan pribadi
Ruang aplikasi
Artikel ini terutama berbicara tentang desain keseluruhan VChat dan implementasi beberapa fungsi utama. Faktanya, masih ada banyak jebakan dalam proses penulisan proyek, seperti kueri antar-table mongoose, unggahan file, dll. Saya tidak akan membahas detail di sini, dan saya akan memperbaruinya jika saya punya waktu di masa depan. Jika Vchat bermanfaat bagi Anda, ingatlah untuk menatap ^_ ^.