ไม่ได้รับการดูแลอีกต่อไป
โครงการนี้เขียนขึ้นเมื่อฉันกำลังศึกษาและบางแห่งก็อ่านได้ไม่ดี (เช่นการโทรกลับจำนวนมากในส่วน Mongo) นักเรียนที่ต้องการอ่านอย่างระมัดระวังขอบคุณ
รหัสสาขาออนไลน์และมีการเปลี่ยนแปลงตามนั้น หากคุณเข้าร่วมกลุ่มแชทตามค่าเริ่มต้นและแบ็กเอนด์สามารถปรับให้เข้ากับการปรับรหัส Linux บางอย่างขอแนะนำให้ดึงมาสเตอร์
โปรดทราบว่าจะต้องมีโหนด, NPM และ MongoDB ที่อยู่ MongoDB IP เริ่มต้นของโครงการคือ 127.0.0.1:27017 ซึ่งสามารถแก้ไขได้ในไฟล์การกำหนดค่า (chatserver utils database.js)
cd chatRoom
npm install 安装前端依赖
npm run build 编译前端代码
cd ..
cd chatServer
npm install 安装后端依赖
npm run create 初始化数据库(号码池、表情包)
npm start 启动服务
在浏览器中打开 localhost:9988 即可
โครงการเริ่มต้นเนื่องจากงานต้องใช้ฟังก์ชั่นห้องสนทนา แต่ด้วยเหตุผลบางอย่างในที่สุดฉันก็เลือก Strophe.js ตามโปรโตคอล XMPP ดังนั้นฉันต้องการเขียนชุดของมันด้วยตัวเองโดยใช้โหนด ตอนแรกฉันต้องการเขียนหน้าแชท แต่ฉันไม่พอใจหลังจากเขียนดังนั้นฉันจึงทำการปรับโครงสร้างใหม่ (ดูเหมือนว่าฉันสามารถเข้าใจได้ว่าทำไมผู้จัดการผลิตภัณฑ์เปลี่ยนความต้องการเสมอ)
หลายสิ่งหลายอย่างเช่น MongoDB เป็นครั้งแรกที่ฉันใช้พวกเขา ฉันเคยสัมผัสกับ MySQL มาก่อน ดังนั้นฉันจึงเรียนรู้และเขียนในเวลาเดียวกัน ฉันใช้เวลาว่างในการเขียนเป็นระยะเวลาหลายเดือน (คราวนี้ฉันได้พูดคุยเกี่ยวกับเวอร์ชัน v0.9.0 และโครงการยังคงได้รับการปรับปรุง ... ) ซึ่งรวมถึงชุดการโต้ตอบส่วนหน้าและแบ็คเอนด์ที่สมบูรณ์ UI มาจากความรู้สึกของฉันเองและไม่มีความสามารถในการออกแบบ (จากนั้นเมื่อพูดถึงการเปลี่ยนธีมมันเป็นเรื่องยากที่จะออกแบบ ~) สเปรย์เบา ๆ- ยังมีอีกหลายพื้นที่ที่จำเป็นต้องได้รับการปรับปรุงและปรับปรุงในโครงการ ทุกคนยินดีที่จะพูดถึงปัญหา (มีกลุ่ม Q ในตอนท้ายของบทความและคุณยินดีที่จะเรียนรู้และสื่อสารด้วยกัน)
นินทาน้อยลงบทความนี้ส่วนใหญ่พูดถึงกระบวนการออกแบบของโครงการและแนวคิดการใช้งานบางอย่าง นักเรียนที่มีความสนใจในโครงการโปรดย้ายไปที่ซอร์สโค้ด VCHAT - ตั้งแต่หัวจรดเท้านิ้วเท้านิ้วเท้านิ้วเท้านิ้วเท้านิ้วเท้านิ้วเท้านิ้วเท้านิ้วเท้านิ้วเท้านิ้วเท้านิ้วเท้านิ้วเท้านิ้วเท้านิ้วเท้านิ้วเท้านิ้วเท้านิ้วเท้านิ้วเท้านิ้วเท้า
* นี่คือตัวแบ่ง ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ส่วนหน้าส่วนใหญ่ใช้ถังตระกูล Vue และไม่มีอะไรจะพูดโครงการก่อสร้างนั่งร้าน, การจัดการรัฐ Vuex, การกำหนดเส้นทางการควบคุม Vue-Router และ Axios สำหรับการโต้ตอบส่วนหน้า แบ็กเอนด์เป็นบริการที่ใช้โหนดและใช้ Express ทำไมฉันไม่ใช้ KOA มันเป็นความสะดวกอย่างหมดจดเพราะ KOA ไม่คุ้นเคย (ครอบคลุมใบหน้าของเธอ) สิ่งที่สำคัญที่สุดในการแชทคือการสื่อสารที่แน่นอน โครงการใช้ socket.io เพื่อสื่อสารด้านหน้าและด้านหลัง
ฐานข้อมูลคือ MongoDB ซึ่งส่วนใหญ่รวมถึงผู้ใช้, เพื่อน, การแชทกลุ่ม, ข้อความ, นิพจน์, จำนวนพูล ฯลฯ
ภาพรวมคุณสมบัติ
โครงสร้างไดเรกทอรี
// 前端
├─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 // 主题
└─vchatเมื่อผู้ใช้ลงทะเบียนใน VCHAT หมายเลขรหัสจะถูกระบุแบบสุ่มและหมายเลขรหัสนี้นำมาจากกลุ่มหมายเลขที่สร้างไว้ล่วงหน้า (จำนวนพูลจำนวนอยู่ใน MongoDB) ส่วนหมายเลขเริ่มต้นที่ระบุเป็น 1000001-10001999 คือรหัสผู้ใช้และส่วนหมายเลขของ 10,0001-100999 คือรหัสแชทกลุ่ม ผู้ใช้สามารถเข้าสู่ระบบด้วยหมายเลขรหัสหรือบัญชี
// 号码池设计
* 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 封面列表เมื่อลงทะเบียนคุณจะต้องพิจารณาว่าบัญชีมีอยู่แล้วหรือไม่และรหัสที่ได้รับแบบสุ่มจะต้องทำเครื่องหมายว่าใช้ในพูลหมายเลขและรหัสผ่านของผู้ใช้จะถูกเข้ารหัสด้วย MD5 ฯลฯ
// md5 密码加密
const md5 = pass => { // 避免多次调用MD5报错
let md5 = crypto . createHash ( 'md5' ) ;
return md5 . update ( pass ) . digest ( "hex" ) ;
} ;เข้าสู่ระบบยังต้องพิจารณาว่าผู้ใช้ได้ลงทะเบียนหรือไม่และสนับสนุนบัญชีและรหัสเพื่อเข้าสู่ระบบหรือไม่
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 } ) ;
}
} )
} ;การจัดการการอนุญาตเข้าสู่ระบบ
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 )
} ) ;ใน VCHAT ประเภทของข้อความรวมถึงแอปพลิเคชันเพื่อนหรือกลุ่มแอปพลิเคชันตอบกลับ (ความยินยอมหรือการปฏิเสธ), การแจ้งเตือนการเข้ากลุ่ม, ข้อความแชท (ข้อความ, รูปภาพ, นิพจน์, ไฟล์)
ก่อนที่จะใช้การส่งข้อความคุณต้องมีความเข้าใจทั่วไปเกี่ยวกับ
socket.ioAPI บางตัว สำหรับเอกสาร API โดยละเอียดคุณสามารถดู 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' ) ;เข้าร่วมห้องในรายการเซสชัน รายการเซสชันจะถูกเพิ่มโดยอัตโนมัติเมื่อแอปพลิเคชันเพื่อนสำเร็จหรือเพิ่มกลุ่มสำเร็จ แต่คุณสามารถลบหรือเพิ่มด้วยตนเองและคุณจะไม่ได้รับข้อความเกี่ยวกับเซสชันที่ถูกลบอีกต่อไป (คล้ายกับการปิดกั้น)
// 前端 发起加入房间的请求
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 ) ; // 包括发送者
} ) ;
} ) ;จะมีปัญหาในการเข้าร่วมห้องสนทนาหลายห้องในเวลาเดียวกัน ซ็อกเก็ตสามารถเข้าร่วมหลายห้องและส่งข้อความไปยังห้องที่ระบุ แต่จะไม่แยกความแตกต่างของห้องเมื่อรับข้อความ กล่าวอีกนัยหนึ่งข้อความทั้งหมดจากห้องจะถูกส่งไปยังลูกค้าด้วยกัน ดังนั้นเราจำเป็นต้องแยกแยะข้อความว่ามาจากห้องไหนและแจกจ่าย สิ่งนี้ต้องใช้ตัวระบุห้องในการกรองและ VCHAT ใช้ ID ห้อง
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);
});
ข้อความทั้งหมดจะถูกเก็บไว้ใน MongoDB เมื่อเปลี่ยนห้องจะได้รับข้อความทางประวัติศาสตร์ เมื่ออยู่ในห้องปัจจุบันข่าวล่าสุดจะถูกผนวกเข้ากับ DOM เท่านั้นและจะไม่ถูกเรียกคืนจากฐานข้อมูล หน้าต่างแชทจะแสดงข้อความล่าสุด 100 ข้อความโดยค่าเริ่มต้นและสามารถดูข้อความเพิ่มเติมได้ในประวัติการแชท
// 前端 获取指定房间的历史消息
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 } ) ;
} ) ;โฮมเพจ
หน้าต่างแชทสามารถลากหรือซูมวอลล์เปเปอร์แชทและการตั้งค่าสีข้อความ
การตั้งค่าส่วนบุคคล
พื้นที่แอปพลิเคชัน
บทความนี้ส่วนใหญ่พูดถึงการออกแบบโดยรวมของ VCHAT และการใช้งานฟังก์ชั่นหลักบางอย่าง ในความเป็นจริงยังมีข้อผิดพลาดมากมายในกระบวนการเขียนโครงการเช่นการสืบค้นระหว่างตาราง Mongoose การอัปโหลดไฟล์ ฯลฯ ฉันจะไม่เข้าไปดูรายละเอียดที่นี่และฉันจะอัปเดตถ้าฉันมีเวลาในอนาคต หาก vChat เป็นประโยชน์กับคุณอย่าลืมจ้องมอง ^_ ^