This commit is contained in:
richboo111
2023-04-25 16:52:01 +08:00
parent 2e8b699b4f
commit a3a663c729
9 changed files with 1032 additions and 1128 deletions

View File

@@ -1,275 +1,276 @@
package im
import (
"encoding/json"
"flag"
"fmt"
"git.rosy.net.cn/baseapi/utils"
"io"
"log"
"net"
"net/http"
"sync"
"time"
"git.rosy.net.cn/baseapi/platformapi/mtwmapi"
"git.rosy.net.cn/jx-callback/globals/api"
"github.com/gazeboxu/mapstructure"
"github.com/gorilla/websocket"
"gopkg.in/ini.v1"
)
// ClientManager 连接管理
type ClientManager struct {
ClientIdMap map[string]*Client // 全部的连接
ClientIdMapLock sync.RWMutex // 读写锁
Connect chan *Client // 连接处理
DisConnect chan *Client // 断开连接处理
GroupLock sync.RWMutex
Groups map[string][]string
SystemClientsLock sync.RWMutex
SystemClients map[string][]string
}
// Client 客户端连接信息
type Client struct {
ClientId string // 标识ID
Socket *websocket.Conn // 用户连接
ConnectTime uint64 // 首次连接时间
IsDeleted bool // 是否删除或下线
UserId string // 业务端标识用户ID
//Extend string // 扩展字段,用户可以自定义
//GroupList []string
}
//channel通道结构体
type clientInfo struct {
ClientId string `json:"clientId" validate:"required"` //链接ID
Data interface{}
SendUserId string
MessageId string
Code int
Msg string
}
// RetData 统一返回值结构体
type RetData struct {
Code int `json:"code"` //响应code
Msg string `json:"msg"` //响应msg success/fail
Data interface{} `json:"data"` //信息
//MessageType string `json:"messageType"` //消息类型 heart-心跳检测send-发送消息receive-接收消息
//MessageId string `json:"messageId"` //发送/接收信息 id
//UserId string `json:"userId"` //必须是平台方userID
}
type global struct {
LocalHost string //本机内网IP
ServerList map[string]string
ServerListLock sync.RWMutex
}
type commonConf struct {
HttpPort string
RPCPort string
Cluster bool
CryptoKey string
}
// SendData 客户端写入参数
type SendData struct {
//ClientId string `json:"clientId" validate:"required"` //链接ID
VendorID int `json:"vendorID"` //消息来源平台ID
Data interface{} `json:"data"` //发送给平台 美团/饿了么消息结构体
//返回值
//Code int `json:"code"`
//Msg string `json:"msg"`
//SendUserId string `json:"sendUserId"`
}
// JXMsg 京西消息结构体
type JXMsg struct {
SendType string `json:"sendType"` //消息发送方 jx-商家mt-美团elm-饿了么
Data interface{} `json:"data"` //美团/饿了么 单聊消息
}
// GetUserListReq 获取门店用户聊天列表
type GetUserListReq struct {
VendorStoreID string `json:"vendorStoreID"` //平台门店id
VendorID string `json:"vendorID"` //平台标识id
AppID string `json:"appID"` //应用ID
}
type GetChatDetailReq struct {
VendorStoreID string `json:"vendorStoreID"` //平台门店id
VendorID string `json:"vendorID"` //平台标识id
AppID string `json:"appID"` //应用ID
UserID string `json:"userID"` //userID/groupID
}
// UserMessageList 用户消息列表
type UserMessageList struct {
VendorID int `json:"vendorID"` //平台品牌 10-美团 11-饿了么
UserID string `json:"userID"` //用户ID
NewMessageNum int `json:"NewMessageNum"` //新消息数量
LatestMsg string `json:"latestMsg"` //最新一条消息
LatestTime int `json:"latestTime"` //最新一条消息发送时间
}
type RelInfo struct {
VendorStoreID string `json:"vendorStoreID"` //平台门店id
VendorID string `json:"vendorID"` //平台标识id
AppID string `json:"appID"` //应用ID
}
type UserRelInfo struct {
VendorStoreID string `json:"vendorStoreID"` //平台门店id
VendorID string `json:"vendorID"` //平台标识id
AppID string `json:"appID"` //应用ID
UserID string `json:"userID"` //用户id/groupID
}
var (
cfg *ini.File
rdb = api.Cacher
Manager = NewClientManager() // 管理者
CommonSetting = &commonConf{}
GlobalSetting = &global{}
ToClientChan chan clientInfo
heartbeatInterval = 60 * time.Second // 心跳间隔
HeartCheckMsg = "~#HHHBBB#~" //心跳检测消息
HeartCheckSuccess = "HB" //成功发送返回心跳消息
VendorIDMT = 10 //im美团
VendorIDELM = 11 //im饿了么
SendTypeJx = "jx" //京西客户端发送方标识
SendTypeMt = "mt" //美团用户发送方标识
SendTypeElm = "elm" //饿了么用户发送方标识符
MTIMPushUrl = "wss://wpush.meituan.com/websocket" //buildPushConnect建立长连接
)
const (
ExpireTimeDay = 24 * time.Hour //redis一天过期时间
maxMessageSize = 8192 // 最大的消息大小
)
type renderData struct {
ClientId string `json:"clientId"`
}
const (
SuccessCode = 0
SuccessMsg = "success"
Fail = -1
FailMsg = "fail"
SYSTEM_ID_ERROR = -1001
ONLINE_MESSAGE_CODE = 1001
OFFLINE_MESSAGE_CODE = 1002
)
// Render 统一返回值
func Render(conn *websocket.Conn, messageId string, code int, message string, data interface{}) error {
return conn.WriteJSON(RetData{
Code: code,
Msg: message,
Data: data,
})
}
func ClientRender(w http.ResponseWriter, code int, msg string, data interface{}) (str string) {
var retData RetData
retData.Code = code
retData.Msg = msg
retData.Data = data
retJson, _ := json.Marshal(retData)
str = string(retJson)
w.Header().Set("Content-Type", "application/json; charset=utf-8")
_, _ = io.WriteString(w, str)
return
}
func ConnRender(conn *websocket.Conn, data interface{}) (err error) {
err = conn.WriteJSON(RetData{
Code: SuccessCode,
Msg: "success",
Data: data,
})
return
}
// Default 给默认值
func Default() {
CommonSetting = &commonConf{
HttpPort: "6000",
RPCPort: "7000",
Cluster: false,
CryptoKey: "Adba723b7fe06819",
}
GlobalSetting = &global{
LocalHost: getIntranetIp(),
ServerList: make(map[string]string),
}
}
// Setup 初始化全局设置变量
func Setup() {
configFile := flag.String("c", "conf/app.ini", "-c conf/app.ini")
var err error
cfg, err = ini.Load(*configFile)
if err != nil {
log.Fatalf("setting.Setup, fail to parse 'conf/app.ini': %v", err)
}
mapTo("common", CommonSetting)
GlobalSetting = &global{
LocalHost: getIntranetIp(),
ServerList: make(map[string]string),
}
fmt.Printf("LocalHost=%s\n ServerList=%s\n", GlobalSetting.LocalHost, utils.Format4Output(GlobalSetting.ServerList, false))
}
// mapTo map section
func mapTo(section string, v interface{}) {
err := cfg.Section(section).MapTo(v)
if err != nil {
log.Fatalf("Cfg.MapTo %s err: %v", section, err)
}
}
//获取本机IP
func getIntranetIp() string {
addrs, _ := net.InterfaceAddrs()
for _, addr := range addrs {
// 检查ip地址判断是否回环地址
if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
return ipnet.IP.String()
}
}
}
return ""
}
// GenFullUrl 组装完整websocket url以及生成clientID
func GenFullUrl() (fullUrl string) {
resp, err := api.MtwmAPI.GetConnectionToken()
if err != nil {
return ""
}
retVal := mtwmapi.GetConnTokenResp{}
err = mapstructure.Decode(resp, &retVal)
fullUrl = MTIMPushUrl + "/" + retVal.AppKey + "/" + retVal.ConnectionToken
//clientID = api.MtwmAPI.GetAppID() + ":" + retVal.ConnectionToken
//打印输出
//fmt.Printf("Create websocket connect failCount:%d", retVal.UserCount)
return fullUrl
}
//
//import (
// "encoding/json"
// "flag"
// "fmt"
// "git.rosy.net.cn/baseapi/utils"
// "io"
// "log"
// "net"
// "net/http"
// "sync"
// "time"
//
// "git.rosy.net.cn/baseapi/platformapi/mtwmapi"
// "git.rosy.net.cn/jx-callback/globals/api"
// "github.com/gazeboxu/mapstructure"
// "github.com/gorilla/websocket"
// "gopkg.in/ini.v1"
//)
//
//// ClientManager 连接管理
//type ClientManager struct {
// ClientIdMap map[string]*Client // 全部的连接
// ClientIdMapLock sync.RWMutex // 读写锁
//
// Connect chan *Client // 连接处理
// DisConnect chan *Client // 断开连接处理
//
// GroupLock sync.RWMutex
// Groups map[string][]string
//
// SystemClientsLock sync.RWMutex
// SystemClients map[string][]string
//}
//
//// Client 客户端连接信息
//type Client struct {
// ClientId string // 标识ID
// Socket *websocket.Conn // 用户连接
// ConnectTime uint64 // 首次连接时间
// IsDeleted bool // 是否删除或下线
// UserId string // 业务端标识用户ID
// //Extend string // 扩展字段,用户可以自定义
// //GroupList []string
//}
//
////channel通道结构体
//type clientInfo struct {
// ClientId string `json:"clientId" validate:"required"` //链接ID
// Data interface{}
// SendUserId string
// MessageId string
// Code int
// Msg string
//}
//
//// RetData 统一返回值结构体
//type RetData struct {
// Code int `json:"code"` //响应code
// Msg string `json:"msg"` //响应msg success/fail
// Data interface{} `json:"data"` //信息
//
// //MessageType string `json:"messageType"` //消息类型 heart-心跳检测send-发送消息receive-接收消息
// //MessageId string `json:"messageId"` //发送/接收信息 id
// //UserId string `json:"userId"` //必须是平台方userID
//}
//
//type global struct {
// LocalHost string //本机内网IP
// ServerList map[string]string
// ServerListLock sync.RWMutex
//}
//type commonConf struct {
// HttpPort string
// RPCPort string
// Cluster bool
// CryptoKey string
//}
//
//// SendData 客户端写入参数
//type SendData struct {
// //ClientId string `json:"clientId" validate:"required"` //链接ID
// VendorID int `json:"vendorID"` //消息来源平台ID
// Data interface{} `json:"data"` //发送给平台 美团/饿了么消息结构体
// //返回值
// //Code int `json:"code"`
// //Msg string `json:"msg"`
// //SendUserId string `json:"sendUserId"`
//}
//
//// JXMsg 京西消息结构体
//type JXMsg struct {
// SendType string `json:"sendType"` //消息发送方 jx-商家mt-美团elm-饿了么
// Data interface{} `json:"data"` //美团/饿了么 单聊消息
//}
//
//// GetUserListReq 获取门店用户聊天列表
//type GetUserListReq struct {
// VendorStoreID string `json:"vendorStoreID"` //平台门店id
// VendorID string `json:"vendorID"` //平台标识id
// AppID string `json:"appID"` //应用ID
//}
//
//type GetChatDetailReq struct {
// VendorStoreID string `json:"vendorStoreID"` //平台门店id
// VendorID string `json:"vendorID"` //平台标识id
// AppID string `json:"appID"` //应用ID
// UserID string `json:"userID"` //userID/groupID
//}
//
//// UserMessageList 用户消息列表
//type UserMessageList struct {
// VendorID int `json:"vendorID"` //平台品牌 10-美团 11-饿了么
// UserID string `json:"userID"` //用户ID
// NewMessageNum int `json:"NewMessageNum"` //新消息数量
// LatestMsg string `json:"latestMsg"` //最新一条消息
// LatestTime int `json:"latestTime"` //最新一条消息发送时间
//}
//
//type RelInfo struct {
// VendorStoreID string `json:"vendorStoreID"` //平台门店id
// VendorID string `json:"vendorID"` //平台标识id
// AppID string `json:"appID"` //应用ID
//}
//
//type UserRelInfo struct {
// VendorStoreID string `json:"vendorStoreID"` //平台门店id
// VendorID string `json:"vendorID"` //平台标识id
// AppID string `json:"appID"` //应用ID
// UserID string `json:"userID"` //用户id/groupID
//}
//
//var (
// cfg *ini.File
// rdb = api.Cacher
// Manager = NewClientManager() // 管理者
// CommonSetting = &commonConf{}
// GlobalSetting = &global{}
// ToClientChan chan clientInfo
// heartbeatInterval = 60 * time.Second // 心跳间隔
// HeartCheckMsg = "~#HHHBBB#~" //心跳检测消息
// HeartCheckSuccess = "HB" //成功发送返回心跳消息
// VendorIDMT = 10 //im美团
// VendorIDELM = 11 //im饿了么
// SendTypeJx = "jx" //京西客户端发送方标识
// SendTypeMt = "mt" //美团用户发送方标识符
// SendTypeElm = "elm" //饿了么用户发送方标识符
// MTIMPushUrl = "wss://wpush.meituan.com/websocket" //buildPushConnect建立长连接
//)
//
//const (
// ExpireTimeDay = 24 * time.Hour //redis一天过期时间
// maxMessageSize = 8192 // 最大的消息大小
//)
//
//type renderData struct {
// ClientId string `json:"clientId"`
//}
//
//const (
// SuccessCode = 0
// SuccessMsg = "success"
// Fail = -1
// FailMsg = "fail"
//
// SYSTEM_ID_ERROR = -1001
// ONLINE_MESSAGE_CODE = 1001
// OFFLINE_MESSAGE_CODE = 1002
//)
//
//// Render 统一返回值
//func Render(conn *websocket.Conn, messageId string, code int, message string, data interface{}) error {
// return conn.WriteJSON(RetData{
// Code: code,
// Msg: message,
// Data: data,
// })
//}
//
//func ClientRender(w http.ResponseWriter, code int, msg string, data interface{}) (str string) {
// var retData RetData
//
// retData.Code = code
// retData.Msg = msg
// retData.Data = data
//
// retJson, _ := json.Marshal(retData)
// str = string(retJson)
//
// w.Header().Set("Content-Type", "application/json; charset=utf-8")
// _, _ = io.WriteString(w, str)
// return
//}
//
//func ConnRender(conn *websocket.Conn, data interface{}) (err error) {
// err = conn.WriteJSON(RetData{
// Code: SuccessCode,
// Msg: "success",
// Data: data,
// })
// return
//}
//
//// Default 给默认值
//func Default() {
// CommonSetting = &commonConf{
// HttpPort: "6000",
// RPCPort: "7000",
// Cluster: false,
// CryptoKey: "Adba723b7fe06819",
// }
//
// GlobalSetting = &global{
// LocalHost: getIntranetIp(),
// ServerList: make(map[string]string),
// }
//}
//
//// Setup 初始化全局设置变量
//func Setup() {
// configFile := flag.String("c", "conf/app.ini", "-c conf/app.ini")
//
// var err error
// cfg, err = ini.Load(*configFile)
// if err != nil {
// log.Fatalf("setting.Setup, fail to parse 'conf/app.ini': %v", err)
// }
//
// mapTo("common", CommonSetting)
//
// GlobalSetting = &global{
// LocalHost: getIntranetIp(),
// ServerList: make(map[string]string),
// }
// fmt.Printf("LocalHost=%s\n ServerList=%s\n", GlobalSetting.LocalHost, utils.Format4Output(GlobalSetting.ServerList, false))
//}
//
//// mapTo map section
//func mapTo(section string, v interface{}) {
// err := cfg.Section(section).MapTo(v)
// if err != nil {
// log.Fatalf("Cfg.MapTo %s err: %v", section, err)
// }
//}
//
////获取本机IP
//func getIntranetIp() string {
// addrs, _ := net.InterfaceAddrs()
// for _, addr := range addrs {
// // 检查ip地址判断是否回环地址
// if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
// if ipnet.IP.To4() != nil {
// return ipnet.IP.String()
// }
//
// }
// }
// return ""
//}
//
//// GenFullUrl 组装完整websocket url以及生成clientID
//func GenFullUrl() (fullUrl string) {
// resp, err := api.MtwmAPI.GetConnectionToken()
// if err != nil {
// return ""
// }
// retVal := mtwmapi.GetConnTokenResp{}
// err = mapstructure.Decode(resp, &retVal)
// fullUrl = MTIMPushUrl + "/" + retVal.AppKey + "/" + retVal.ConnectionToken
// //clientID = api.MtwmAPI.GetAppID() + ":" + retVal.ConnectionToken
// //打印输出
// //fmt.Printf("Create websocket connect failCount:%d", retVal.UserCount)
// return fullUrl
//}