Skip to content

Goular/IM

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

#如何安全接入这个聊天主页面 通过登录api user/login 获得id和token 返回json到前端, 前端拼接url /chat/index.shtml?id=1&token=123 通过location.href 跳转

#如何添加/显示好友 添加/显示群

/contact/addfriend 自动添加好友,参数userid,dstid
//
用户10000添加好友10086,往contact表中添加俩条记录

//一条ownerid =10000,dstobj=10086 
//一条ownerid =10086,dstobj=10000

/contact/loadfriend 显示全部好友,参数userid

/contact/createcommunity 建群,头像pic,名称name,备注memo,创建者userid
/contact/loadcommunity 显示全部群 参数userid
//加群逻辑特殊一点
/contact/joincommunity 加群,参数userid,dstid

##创建模型(实体)

const (
		CONCAT_CATE_USER = 0x01  //用户
	    CONCAT_CATE_COMUNITY = 0x02 //群组
	)
type Contact struct {
	Id         int64     `xorm:"pk autoincr bigint(20)" form:"id" json:"id"`
	Ownerid       int64	`xorm:"bigint(20)" form:"ownerid" json:"ownerid"`   // 什么角色
	Dstobj       int64	`xorm:"bigint(20)" form:"dstobj" json:"dstobj"`   // 什么角色
	Cate      int	`xorm:"int(11)" form:"cate" json:"cate"`   // 什么角色
	Memo    string	`xorm:"varchar(120)" form:"memo" json:"memo"`   // 什么角色
	Createat   time.Time	`xorm:"datetime" form:"createat" json:"createat"`   // 什么角色
}

//同步表结构
DbEngin.Sync2(new(model.Contact))

##创建控制器ctrl

func Addfriend(w http.ResponseWriter, req *http.Request) {
	//request.ParseForm()
    //mobile := request.PostForm.Get("mobile")
    //passwd := request.PostForm.Get("passwd")
	//str->int
	//
	var arg args.ContactArg
	//对象绑定
	util.Bind(req,&arg)
	//
	err := contactService.AddFriend(arg.Userid,arg.Dstid)
	
	if err!=nil{
		util.RespFail(w,err.Error())
	}else{
		util.RespOk(w,msgs)
	}
}

##配置路由 http.HandleFunc("/contact/addfriend", ctrl.Addfriend) ##配置service

//自动添加好友
func (service *ContactService) AddFriend(
	userid,//用户id 10086,
	dstid int64 ) error{
	
	//判断是否已经存在
	//如果存在记录说明已经是好友了不加
	if tmp.Id>0{
		return errors.New("该用户已经被添加过啦")
	}
	//启动事务
	session := DbEngin.NewSession();
	session.Begin()
	//
	_,e2 := session.InsertOne()
	//
	_,e3 := session.InsertOne()
	//
	if  e2==nil && e3==nil{
		session.Commit()
		//
	}else{
		session.Rollback()
		//
	}
}

##前端js

addfriend:function(){
    
    //弹窗提示用户输入
    // mui.prompt()
}
_addfriend:function(){
   //网络请求
}

var contactService service.ContactService
func LoadFriend(w http.ResponseWriter, req *http.Request){
	var arg args.ContactArg
	//如果这个用的上,那么可以直接
	util.Bind(req,&arg)
	users := contactService.SearchFriend(arg.Userid)
	util.RespOkList(w,users,len(users))
}

func Loadcommunity(w http.ResponseWriter, req *http.Request){
	var arg args.ContactArg
	//如果这个用的上,那么可以直接
	util.Bind(req,&arg)
	comunitys := contactService.SearchComunity(arg.Userid)
	util.RespOkList(w,comunitys,len(comunitys))
}
func JoinCommunity(w http.ResponseWriter, req *http.Request){
	var arg args.ContactArg

	//如果这个用的上,那么可以直接
	util.Bind(req,&arg)
	err := contactService.JoinCommunity(arg.Userid,arg.Dstid);
	if err!=nil{
		util.RespFail(w,err.Error())
	}else {
		util.RespOk(w,"")
	}
}


http.HandleFunc("/contact/addfriend", ctrl.Loadcommunity)
http.HandleFunc("/contact/community", ctrl.Loadcommunity)
http.HandleFunc("/contact/friend", ctrl.LoadFriend)

##创建服务层service

//搜索群组
func (service *ContactService) SearchComunity(userId int64) ([]model.Community){
	 conconts := make([]model.Contact,0)
	 comIds :=make([]int64,0)

	 DBengin.Where("ownerid = ? and cate = ?",userId,model.CONCAT_CATE_COMUBITY).Find(&conconts)
     for _,v := range conconts{
		 comIds = append(comIds,v.Dstobj);
	 }
     coms := make([]model.Community,0)
     if len(comIds)== 0{
     	return coms
	 }
	DBengin.In("id",comIds).Find(&coms)
	return coms
}

//加群
func (service *ContactService) JoinCommunity(userId,comId int64) error{
	cot := model.Contact{
		Ownerid:userId,
		Dstobj:comId,
		Cate:model.CONCAT_CATE_COMUBITY,
	}
	DBengin.Get(&cot)
	if(cot.Id==0){
		cot.Createat = time.Now()
		_,err := DBengin.InsertOne(cot)
		return err
	}else{
		return nil
	}


}
//建群
func (service *ContactService) CreateCommunity(comm model.Community) (ret model.Community,err error){
	if len(comm.Name)==0{
		err = errors.New("缺少群名称")
		return ret,err
	}
	if comm.Ownerid==0{
		err = errors.New("请先登录")
		return ret,err
	}
	com := model.Community{
		Ownerid:comm.Ownerid,
	}
	num,err := DBengin.Count(&com)

	if(num>5){
		err = errors.New("一个用户最多只能创见5个群")
		return com,err
	}else{
		comm.Createat=time.Now()
		session := DBengin.NewSession()
		session.Begin()
		_,err = session.InsertOne(&comm)
		if err!=nil{
			session.Rollback();
			return com,err
		}
		_,err =session.InsertOne(
			model.Contact{
				Ownerid:comm.Ownerid,
				Dstobj:comm.Id,
				Cate:model.CONCAT_CATE_COMUBITY,
				Createat:time.Now(),
			})
		if err!=nil{
			session.Rollback();
		}else{
			session.Commit()
		}
		return com,err
	}
}
//加好友
func (service *ContactService) AddFriend(userId,dstId int64) error{
	cot := model.Contact{
		Ownerid:userId,
		Dstobj:dstId,
		Cate:model.CONCAT_CATE_USER,
	}
	DBengin.Get(&cot)
	if(cot.Id==0){
		cot.Createat = time.Now()
		_,err := DBengin.InsertOne(cot)
		return err
	}else{
		return nil
	}

}
//查找好友
func (service *ContactService) SearchFriend(userId int64) ([]model.User){
	conconts := make([]model.Contact,0)
	objIds :=make([]int64,0)
	DBengin.Where("ownerid = ? and cate = ?",userId,model.CONCAT_CATE_USER).Find(&conconts)
	for _,v := range conconts{
		objIds = append(objIds,v.Dstobj);
	}
	coms := make([]model.User,0)
	if len(objIds)== 0{
		return coms
	}
	DBengin.In("id",objIds).Find(&coms)
	return coms
}
//自动添加好友
func (service *ContactService) AddFriendAuto(userid,dstid int64){
	//事务的使用
	session := DBengin.NewSession();
	session.Begin()
	
	_,e2 := session.Insert(model.Contact{
		Ownerid:userid,
		Dstobj:dstid,
		Cate:model.CONCAT_CATE_USER,
		Createat:time.Now(),
	})
	_,e3 := session.Insert(model.Contact{
		Ownerid:dstid,
		Dstobj:userid,
		Cate:model.CONCAT_CATE_USER,
		Createat:time.Now(),
	})
	if  e2==nil && e3==nil{
		session.Commit()
	}else{
		session.Rollback()
	}
}

##前端调用实现

//核心工具包解析
//解析query,/chat/index.shtml?id=1&token=x
//token = parseQuery("token")
function parseQuery (name){	
  var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); //构造一个含有目标参数的正则表达式对象
  var r = window.location.search.substr(1).match(reg);  //匹配目标参数
  if (r != null) return decodeURI(unescape(r[2])); 
  return null; //返回参数值
 }
 //获取用户ID用户ID
 function userId(){
    var id = parseQuery("id")
    if (id==null){
        return 0
    }else{
        return parseInt(id)
    }
 }
 //核心网络请求函数
 function post(uri,data,fn){
                
            var xhr = new XMLHttpRequest();
            xhr.open("POST",url, true);
            // 添加http头,发送信息至服务器时内容编码类型
            xhr.setRequestHeader(
            	"Content-Type",
    			"application/x-www-form-urlencoded"
    		);
            xhr.onreadystatechange = function() {
                if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 304)) {
                    //resolve(JSON.parse(xhr.responseText));
                    if (typeof fn=="function"){
                        fn(JSON.parse(xhr.responseText))    
                    }
                }
            };
            xhr.onerror = function(){
            	//reject({"code":-1,"msg":"服务器繁忙"})
            	if (typeof fn=="function"){
            	    fn({"code":-1,"msg":"服务器繁忙"})
            	}
    		}
            var _data=[];
            for(var i in data){
                _data.push( i +"=" + encodeURI(data[i]));
            }
        	xhr.send(_data.join("&"));
        
 }
 //网络请求函数promis版本
 function post(uri,data,fn){
     return new Promise(function (resolve, reject) {
             var xhr = new XMLHttpRequest();
             xhr.open("POST",url, true);
             // 添加http头,发送信息至服务器时内容编码类型
             xhr.setRequestHeader(
             	"Content-Type",
     			"application/x-www-form-urlencoded"
     		);
             xhr.onreadystatechange = function() {
                 if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 304)) {
                     resolve(JSON.parse(xhr.responseText));
                     if (typeof fn=="function"){
                         fn(JSON.parse(xhr.responseText))    
                     }
                 }
             };
             xhr.onerror = function(){
             	reject({"code":-1,"msg":"服务器繁忙"})
             	if (typeof fn=="function"){
                  fn(JSON.parse(xhr.responseText))    
                }
     		}
             var _data=[];
             for(var i in data){
                 _data.push( i +"=" + encodeURI(data[i]));
             }
         	xhr.send(_data.join("&"));
         })
  }
  
 loadfriends:function(){
           var that = this;
           post("contact/friend",{userid:userId()},
           function(res){
               that.friends = res.rows ||[];
               var usermap = usermap;
               for(var i in res.rows){
                   usermap[res.rows[i].id]=res.rows[i];
               }
               this.usermap = usermap;
           }.bind(this))
       },
       loadcommunitys:function(){
           var that = this;
           post("contact/community",{userid:userId()},function(res){
               that.communitys = res.rows ||[];
           })
       },
       addfriend:function(){
           var that = this;
           mui.prompt('','请输入好友ID','加好友',['取消','确认'],function (e) {
               if (e.index == 1) {
                   if (isNaN(e.value) || e.value <= 0) {
                       mui.toast('格式错误');
                   }else{
                       //mui.toast(e.value);
                       that._addfriend(e.value)
                   }
               }else{
                   //mui.toast('您取消了入库');
               }
           },'div');
           document.querySelector('.mui-popup-input input').type = 'number';
       },
       _addfriend:function(dstobj){
           var user = userInfo();
 
           post("user/addfriend",{dstid:dstobj,userid: user.id,pic:user.avatar,content:user.nickname,memo: "请求加你为好友"},function(res){
               if(res.code==200){
                   mui.toast("添加成功");
                   that.loadfriends();
               }else{
                   mui.toast(res.msg);
               }
           })
       },
       _joincomunity:function(dstobj){
           var that = this;
           post("user/joincommunity",{dstid:dstobj},function(res){
               if(res.code==200){
                   mui.toast("添加成功");
 
                   that.loadcommunitys();
               }else{
                   mui.toast(res.msg);
               }
           })
       },
       joincomunity:function(){
           var that = this;
           mui.prompt('','请输入群号','加群',['取消','确认'],function (e) {
               if (e.index == 1) {
                   if (isNaN(e.value) || e.value <= 0) {
                       mui.toast('格式错误');
                   }else{
                       //mui.toast(e.value);
                       that._joincomunity(e.value)
                   }
               }else{
                   //mui.toast('您取消了入库');
               }
           },'div');
           document.querySelector('.mui-popup-input input').type = 'number';
       },

#设计可以无限扩张业务场景的消息通讯结构

func recvproc(node *Node) {
	for{
		_,data,err := node.Conn.ReadMessage()
		if err!=nil{
			log.Println(err.Error())
			return
		}
		//todo 对data进一步处理
		//dispatch(data)
		fmt.Printf("recv<=%s",data)
	}
}

##原理 前端通过websocket发送json格式的字符串 用户2向用户3发送文字消息hello

{id:1,userid:2,dstid:3,cmd:10,media:1,content:"hello"}

里面携带 谁发的-userid 要发给谁-dstid 这个消息有什么用-cmd 消息怎么展示-media 消息内容是什么-(url,amout,pic,content等) ##核心数据结构

type Message struct {
	Id      int64  `json:"id,omitempty" form:"id"` //消息ID
	//谁发的
	Userid  int64  `json:"userid,omitempty" form:"userid"` //谁发的
	//什么业务
	Cmd     int    `json:"cmd,omitempty" form:"cmd"` //群聊还是私聊
	//发给谁
	Dstid   int64  `json:"dstid,omitempty" form:"dstid"`//对端用户ID/群ID
	//怎么展示
	Media   int    `json:"media,omitempty" form:"media"` //消息按照什么样式展示
	//内容是什么
	Content string `json:"content,omitempty" form:"content"` //消息的内容
	//图片是什么
	Pic     string `json:"pic,omitempty" form:"pic"` //预览图片
	//连接是什么
	Url     string `json:"url,omitempty" form:"url"` //服务的URL
	//简单描述
	Memo    string `json:"memo,omitempty" form:"memo"` //简单描述
	//其他的附加数据,语音长度/红包金额
	Amount  int    `json:"amount,omitempty" form:"amount"` //其他和数字相关的
}
const (
    //点对点单聊,dstid是用户ID
	CMD_SINGLE_MSG = 10
	//群聊消息,dstid是群id
	CMD_ROOM_MSG   = 11
	//心跳消息,不处理
	CMD_HEART      = 0
	
)
const (
    //文本样式
	MEDIA_TYPE_TEXT=1
	//新闻样式,类比图文消息
	MEDIA_TYPE_News=2
	//语音样式
	MEDIA_TYPE_VOICE=3
	//图片样式
	MEDIA_TYPE_IMG=4
	
	//红包样式
	MEDIA_TYPE_REDPACKAGR=5
	//emoj表情样式
	MEDIA_TYPE_EMOJ=6
	//超链接样式
	MEDIA_TYPE_LINK=7
	//视频样式
	MEDIA_TYPE_VIDEO=8
	//名片样式
	MEDIA_TYPE_CONCAT=9
	//其他自己定义,前端做相应解析即可
	MEDIA_TYPE_UDEF=100
)
/**
消息发送结构体,点对点单聊为例
1、MEDIA_TYPE_TEXT
{id:1,userid:2,dstid:3,cmd:10,media:1,
content:"hello"}

3、MEDIA_TYPE_VOICE,amount单位秒
{id:1,userid:2,dstid:3,cmd:10,media:3,
url:"http://www.a,com/dsturl.mp3",
amount:40}

4、MEDIA_TYPE_IMG
{id:1,userid:2,dstid:3,cmd:10,media:4,
url:"http://www.baidu.com/a/log.jpg"}


2、MEDIA_TYPE_News
{id:1,userid:2,dstid:3,cmd:10,media:2,
content:"标题",
pic:"http://www.baidu.com/a/log,jpg",
url:"http://www.a,com/dsturl",
"memo":"这是描述"}


5、MEDIA_TYPE_REDPACKAGR //红包amount 单位分
{id:1,userid:2,dstid:3,cmd:10,media:5,url:"http://www.baidu.com/a/b/c/redpackageaddress?id=100000","amount":300,"memo":"恭喜发财"}
6、MEDIA_TYPE_EMOJ 6
{id:1,userid:2,dstid:3,cmd:10,media:6,"content":"cry"}

7、MEDIA_TYPE_Link 7
{id:1,userid:2,dstid:3,cmd:10,media:7,
"url":"http://www.a.com/dsturl.html"
}

8、MEDIA_TYPE_VIDEO 8
{id:1,userid:2,dstid:3,cmd:10,media:8,
pic:"http://www.baidu.com/a/log,jpg",
url:"http://www.a,com/a.mp4"
}

9、MEDIA_TYPE_CONTACT 9
{id:1,userid:2,dstid:3,cmd:10,media:9,
"content":"10086",
"pic":"http://www.baidu.com/a/avatar,jpg",
"memo":"胡大力"}

*/

##从哪里接收数据?怎么处理这些数据呢?

func recvproc(node *Node) {
	for{
		_,data,err := node.Conn.ReadMessage()
		if err!=nil{
			log.Println(err.Error())
			return
		}
		//todo 对data进一步处理
		fmt.Printf("recv<=%s",data)
		dispatch(data)
	}
}
func dispatch(data []byte){
    //todo 转成message对象
    
    //todo 根据cmd参数处理逻辑
    
    
    
    
    
    
    msg :=Message{}
    err := json.UnMarshal(data,&msg)
    if err!=nil{
        log.Printf(err.Error())
        return ;
    }
    switch msg.Cmd {
    	case CMD_SINGLE_MSG: //如果是单对单消息,直接将消息转发出去
    		//向某个用户发回去
    		fmt.Printf("c2cmsg %d=>%d\n%s\n",msg.Userid,msg.Dstid,string(tmp))
    		SendMsgToUser(msg.Userid, msg.Dstid, tmp)
    		//fmt.Println(msg)
    	case CMD_ROOM_MSG: //群聊消息,需要知道
    		fmt.Printf("c2gmsg %d=>%d\n%s\n",msg.Userid,msg.Dstid,string(tmp))
    		SendMsgToRoom(msg.Userid, msg.Dstid, tmp)
    	case CMD_HEART:
    	default:
    	    //啥也别做
    	    
    	}
    		
}

#5.4 实现发送文字、表情包等

前端user1拼接好数据对象Message msg={id:1,userid:2,dstid:3,cmd:10,media:1,content:txt} 转化成json字符串jsonstr jsonstr = JSON.stringify(msg) 通过websocket.send(jsonstr)发送 后端S在recvproc中接收收数据data 并做相应的逻辑处理dispatch(data)-转发给user2 user2通过websocket.onmessage收到消息后做解析并显示

###5.4.1 前端处理核心方法 前端所有的操作都在拼接数据 如何拼接?

sendtxtmsg:function(txt){
//{id:1,userid:2,dstid:3,cmd:10,media:1,content:txt}
var msg =this.createmsgcontext();
//msg={"dstid":dstid,"cmd":cmd,"userid":userId()}
//选择某个好友/群的时候对dstid,cmd进行赋值
//userId()返回用户自己的id ,
// 从/chat/index.shtml?id=xx&token=yy中获得
//1文本类型
msg.media=1;msg.content=txt;
this.showmsg(userInfo(),msg);//显示自己发的文字
this.webSocket.send(JSON.stringify(msg))//发送
}

sendpicmsg:function(picurl){
    //{id:1,userid:2,dstid:3,cmd:10,media:4,
    // url:"http://www.baidu.com/a/log,jpg"}
    var msg =this.createmsgcontext();
    msg.media=4;
    msg.url=picurl;
    this.showmsg(userInfo(),msg)
    this.webSocket.send(JSON.stringify(msg))
}
sendaudiomsg:function(url,num){
    //{id:1,userid:2,dstid:3,cmd:10,media:3,url:"http://www.a,com/dsturl.mp3",anount:40}
    var msg =this.createmsgcontext();
    msg.media=3;
    msg.url=url;
    msg.amount = num;
    this.showmsg(userInfo(),msg)
    console.log("sendaudiomsg",this.msglist);
    this.webSocket.send(JSON.stringify(msg))
}

##5.4.2 后端逻辑处理函数 func dispatch(data[]byte)

func dispatch(data[]byte){
    //todo 解析data为message
    
    //todo根据message的cmd属性做相应的处理
    
}
func recvproc(node *Node) {
	for{
		_,data,err := node.Conn.ReadMessage()
		if err!=nil{
			log.Println(err.Error())
			return
		}
		//todo 对data进一步处理
		dispatch(data)
		fmt.Printf("recv<=%s",data)
	}
}

###5.4.3 对端接收到消息后处理函数

//初始化websocket的时候进行回调配置
this.webSocket.onmessage = function(evt){
     //{"data":"}",...}
     if(evt.data.indexOf("}")>-1){
         this.onmessage(JSON.parse(evt.data));
     }else{
         console.log("recv<=="+evt.data)
     }
 }.bind(this)
onmessage:function(data){
     this.loaduserinfo(data.userid,function(user){
         this.showmsg(user,data)
     }.bind(this))
 }

 //消息显示函数
showmsg:function(user,msg){
    var data={}
    data.ismine = userId()==msg.userid;
    //console.log(data.ismine,userId(),msg.userid)
    data.user = user;
    data.msg = msg;
    //vue 只需要修改数据结构即可完成页面渲染
    this.msglist = this.msglist.concat(data)
    //面板重置
    this.reset();
    var that =this;
    //滚动到新消息处
    that.timer = setTimeout(function(){
        window.scrollTo(0, document.getElementById("convo").offsetHeight);
        clearTimeout(that.timer)
    },100)
 }

###5.4.4 表情包简单逻辑 弹出一个窗口, 选择图片获得一个连接地址 调用sendpicmsg方法开始发送流程

##5.5 发送图片/拍照 弹出一个窗口, 选择图片,上传到服务器 获得一个链接地址 调用sendpicmsg方法开始发送流程 ###5.5.1 界面处理技巧

<input 
accept="image/gif,image/jpeg,,image/png" 
type="file" 
onchange="upload(this)" 
class='upload'/>

sendpicmsg方法开始发送流程 ###5.5.2 upload前端实现

function upload(dom){
        uploadfile("attach/upload",dom,function(res){
            if(res.code==0){//成功以后调用sendpicmsg
                vm.sendpicmsg(res.data)
            }
        })
    }
    
function uploadfile(uri,dom,callback){
    //H5新特性
    var formdata = new FormData();
    //获得一个文件dom.files[0]
    formdata.append("file",dom.files[0])
    //formdata.append("filetype",".png")//.mp3指定后缀
    
    var xhr = new XMLHttpRequest();//ajax初始化
    var url = "http://"+location.host+"/"+uri;
    //"http://127.0.0.1/attach/upload"
    xhr.open("POST",url, true);
    //成功时候回调
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4 && 
        xhr.status == 200) {
            //fn.call(this, JSON.parse(xhr.responseText));
            callback(JSON.parse(xhr.responseText))
        }
    };
    xhr.send(formdata);
}    

###5.5.2 upload后端实现 ####存储到本地

func UploadLocal(writer http.ResponseWriter,
	request * http.Request){
	}

###存储到alioss

func UploadLocal(writer http.ResponseWriter,
	 request * http.Request){
}
如何安装 golang.org/x/time/rate
>cd $GOPATH/src/golang.org/x/
>git clone https://github.com/golang/time.git time

###5.6 发送语音 ####5.6.1 采集语音

navigator.mediaDevices.getUserMedia(
    {audio: true, video: true}
    ).then(successfunc).catch(errfunc);


navigator.mediaDevices.getUserMedia(
    {audio: true, video: false}
    ).then(function(stream)  {
              //请求成功
              this.recorder = new MediaRecorder(stream);
              this.recorder.start();
              this.recorder.ondataavailable = (event) => {
                  uploadblob("attach/upload",event.data,".mp3",res=>{
                      var duration = Math.ceil((new Date().getTime()-this.duration)/1000);
                      this.sendaudiomsg(res.data,duration);
                  })

                  stream.getTracks().forEach(function (track) {
                      track.stop();
                  });
                  this.showprocess = false
              }
              
          }.bind(this)).catch(function(err){
                mui.toast(err.msg)
                this.showprocess = false
            }.bind(this));

####5.6.2 上传语音

function uploadblob(uri,blob,filetype,fn){
       var xhr = new XMLHttpRequest();
       xhr.open("POST","//"+location.host+"/"+uri, true);
       // 添加http头,发送信息至服务器时内容编码类型
       xhr.onreadystatechange = function() {
           if (xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 304)) {
               fn.call(this, JSON.parse(xhr.responseText));
           }
       };
       var _data=[];
       var formdata = new FormData();
       formdata.append("filetype",filetype);
       formdata.append("file",blob)
       xhr.send(formdata);
   }

###5.7 实现群聊

####5.7.1 原理 分析群id,找到加了这个群的用户,把消息发送过去 方案一、 map<qunid1,qunid2,qunid3> 优势是锁的频次低 劣势是要轮训全部map

type Node struct {
	Conn *websocket.Conn
	//并行转串行,
	DataQueue chan []byte
	GroupSets set.Interface
}
//映射关系表
var clientMap map[int64]*Node = make(map[int64]*Node,0)

方案二、 map<群id><userid1,userid2,userid3> 优势是找用户ID非常快 劣势是发送信息时需要根据userid获取node,锁的频次太高

type Node struct {
	Conn *websocket.Conn
	//并行转串行,
	DataQueue chan []byte
}
//映射关系表
var clientMap map[int64]*Node = make(map[int64]*Node,0)
var comMap map[int64]set.Interface= make(map[int64]set.Interface,0)

####5.7.2 需要处理的问题

1、当用户接入的时候初始化groupset
2、当用户加入群的时候刷新groupset
3、完成信息分发

###5.8 性能优化 1 锁的频次 2 json编码次数 3 静态资源分离

###存储到alioss

func UploadOss(writer http.ResponseWriter, request * http.Request){ } 如何安装

go get github.com/aliyun/aliyun-oss-go-sdk/oss golang.org/x/time/rate cd $GOPATH/src/golang.org/x/ git clone https://github.com/golang/time.git time


About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors