import (
"encoding/json"
"fmt"
"github.com/gorilla/websocket"
"shopeecallback/service"
)
type User struct {
Conn *websocket.Conn
UserId int64
Msg chan []byte
}
type Message struct {
UserId int64 `json:"user_id, omitempty"`
ShopId int64 `json:"shop_id, omitempty"`
Content string `json:"content, omitempty"`
}
type ClientHandle struct {
UserList map[*User]bool
Register chan *User
Unregister chan *User
}
//初始化处理中心,以便调用
var WsClient = &ClientHandle{
UserList: make(map[*User]bool),
Register: make(chan *User),
Unregister: make(chan *User),
}
func (hand *ClientHandle)Run() {
for {
select {
case user := <-hand.Register:
hand.UserList[user] = true
list, err2 := service.Message.GetMessages(user.UserId)
if err2 == nil {
for _, msg := range list {
jsonMessage, _ := json.Marshal(&Message{ShopId: msg.ShopId, UserId: user.UserId, Content: msg.Msg})
//发送消息
user.Msg <- jsonMessage
}
}
case user := <-hand.Unregister:
//从注销列表中取数据,判断用户列表中是否存在这个用户,存在就删掉
if _, ok := hand.UserList[user]; ok {
delete(hand.UserList, user)
}
}
}
}
func ReadMsg(user *User) {
for {
_, msg, err := user.Conn.ReadMessage()
if err != nil {
fmt.Println("用户退出:",user.Conn.RemoteAddr().String())
WsClient.Unregister<-user
user.Conn.Close()
break
}
if string(msg) == "ping" {
jsonMessage, _ := json.Marshal(&Message{ShopId: 0, UserId: user.UserId, Content: "pong"})
user.Msg <- jsonMessage
}
}
}
func WriteMsg(user *User) {
for {
select {
//从send里读消息
case message, ok := <-user.Msg:
if ok {
// 发送完后就修改读取状态,正常情况下,应该是用户读了才修改状态
var res Message
err := json.Unmarshal(message, &res)
if err == nil {
go func() {
service.Message.UpdateMessageStateByShopAndUser(res.UserId, res.ShopId)
}()
}
//发送给web端
user.Conn.WriteMessage(websocket.TextMessage, message)
}
}
}
}
启动socket
global.Socket = websocket.Upgrader{
//
ReadBufferSize: 1023,
//
WriteBufferSize: 1023,
//允许跨域
CheckOrigin: func(r *http.Request) bool {
/*if r.Method != "GET" {
return false
}
if r.URL.Path != "/callback" {
return false
}*/
return true
},
}
go wsocket.WsClient.Run()
用户登录
func (it *MessageApi) Callback(c *gin.Context) {
/*ws, err := global.Socket.Upgrade(c.Writer, c.Request, nil)
if err != nil {
return
}
defer ws.Close()
for {
mt, message, err := ws.ReadMessage()
if err != nil {
ws.WriteMessage(mt, []byte("socket连接错误"))
}
var req request.RequestPushMessage
err = json.Unmarshal(message, &req)
if err != nil {
ws.WriteMessage(mt, []byte("参数错误"))
}
if list, err2 := service.Message.GetMessages(req.UserId); err2 == nil {
for _, msg := range list {
ws.WriteJSON(msg)
}
// 更新状态
service.Message.UpdateMessagesState(req.UserId)
} else {
ws.WriteMessage(mt, []byte("无数据"))
}
}*/
//wsocket.SocketHandle(c.Writer, c.Request, 1)
conn, err := (&websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}).Upgrade(c.Writer, c.Request, nil)
if err != nil {
return
}
var req request.RequestPushMessage
err = c.ShouldBindQuery(&req)
if err != nil {
return
}
userId := req.UserId
user := &wsocket.User{UserId: userId, Conn: conn, Msg: make(chan []byte)}
//注册一个新的链接
wsocket.WsClient.Register <- user
go wsocket.ReadMsg(user)
//启动协程把消息返回给web端
go wsocket.WriteMsg(user)
}
店铺发送消息
func (it *ShopeeApi) Callback(c *gin.Context) {
uuid := uuid.New().String()
jsonReq := make(map[string]interface{}) //注意该结构接受的内容
//err := c.BindJSON(&json)
var req request.RequestShopeeMessage
//err := c.ShouldBindJSON(&req)
err := c.ShouldBindBodyWith(&jsonReq, binding.JSON)
err = c.ShouldBindBodyWith(&req, binding.JSON)
if err != nil {
global.FailWithMessage(uuid, "参数错误" + err.Error(), c)
return
}
var errMsg = "店铺未定义"
if req.ShopId > 0 {
// 根据shop_id 获取user_id
err, userIds := service.Buyer.GetBuyersByShop(req.ShopId)
// 有数据则插入到message表
fmt.Println(userIds)
if err == nil {
jsonStr , _ := json.Marshal(jsonReq)
jsonStrng := string(jsonStr)
go func() {
for _, uid := range userIds {
//写入数据库
_, err = service.Message.CreateMessage(req.ShopId, uid, jsonStrng, uuid)
if err == nil {
// 发送消息
for uclient, hasClient := range wsocket.WsClient.UserList {
if hasClient && uclient.UserId == uid {
jsonMessage, _ := json.Marshal(&wsocket.Message{ShopId: req.ShopId, UserId: uclient.UserId, Content: jsonStrng})
uclient.Msg <- jsonMessage
go wsocket.WriteMsg(uclient)
}
}
}
}
}()
errMsg = "记录消息完成"
} else {
errMsg = "店铺没有用户"
}
}
global.FailWithMessage(uuid, errMsg, c)
return
}
js websocket
$(document).ready(function() {
var wsurl = "ws://127.0.0.1:8080/api/v1/callback?user_id=1";
var websocket; //用于存放客户端创建的Socket对象
var timer;
var lockReconnect = false;
createSocket();
function createSocket() {
try {
websocket = new WebSocket(wsurl);
initSocket();
} catch(e) {
alert("不支持websocket")
}
}
function initSocket() {
websocket.onopen = function() {
heartCheck.start();
$('#showMessage').append("<p>conneted success!</p>");
}
websocket.onmessage = function() {
heartCheck.start();
var msg = JSON.parse(event.data);
var shopId = msg.shop_id;
var content = msg.content;
var state = msg.state;
$('#showMessage').append("<p>" + shopId + ":" + content + "</p>");
}
websocket.onerror = function() {
reconnect(wsurl)
}
websocket.onclose = function() {
reconnect(wsurl)
}
}
function reconnect(url) {
if(lockReconnect) return;
lockReconnect = true;
setTimeout(function () { //没连接上会一直重连,设置延迟避免请求过多
createSocket(url);
lockReconnect = false;
}, 2000);
}
var heartCheck = {
timeout: 540000, //9分钟发一次心跳
timeoutObj: null,
serverTimeoutObj: null,
reset: function(){
clearTimeout(this.timeoutObj);
clearTimeout(this.serverTimeoutObj);
return this;
},
start: function(){
var self = this;
this.timeoutObj = setTimeout(function(){
//这里发送一个心跳,后端收到后,返回一个心跳消息,
//onmessage拿到返回的心跳就说明连接正常
ws.send("ping");
console.log("ping!")
self.serverTimeoutObj = setTimeout(function(){//如果超过一定时间还没重置,说明后端主动断开了
ws.close(); //如果onclose会执行reconnect,我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次
}, self.timeout)
}, this.timeout)
}
}
})