first commit

This commit is contained in:
2026-02-17 13:06:23 +08:00
commit 7cbd3d061d
349 changed files with 126558 additions and 0 deletions
+268
View File
@@ -0,0 +1,268 @@
package wxtask
import (
"encoding/json"
"fmt"
"net/http"
"regexp"
"strconv"
"sync"
"xiawan/wx/protobuf/wechat"
"xiawan/wx/srv/wxface"
"github.com/gorilla/websocket"
)
// 全局写锁映射,保护每个连接的写操作
var connWriteMutexes sync.Map
// decodeUnicodeEscapes 解码Unicode转义字符
func decodeUnicodeEscapes(s string) string {
// 处理JSON中的Unicode转义字符,如\u003c
re := regexp.MustCompile(`\\u([0-9a-fA-F]{4})`)
result := re.ReplaceAllStringFunc(s, func(match string) string {
// 提取十六进制数字部分
hexStr := match[2:] // 去掉\u前缀
// 将十六进制转换为整数
if codePoint, err := strconv.ParseInt(hexStr, 16, 32); err == nil {
// 转换为Unicode字符
return string(rune(codePoint))
}
return match // 如果转换失败,返回原字符串
})
return result
}
// 长链接同步消息
type WXSocketMsgTask struct {
WxConn wxface.IWXConnect
// 待消费消息列表
MsgItemChan chan *wechat.AddMsg
// 可不可以消费下一条
canDoNext chan bool
// 结束标志
EndChan chan bool
// 是否正在运行
isRunning bool
// 是否启用WebSocket
wsEnabled bool
// 当前在消费的消息
CurrentMsgItem *wechat.AddMsg
// 存储所有连接的客户端
Clients map[string]*websocket.Conn
Upgrader websocket.Upgrader
// 定义一个互斥锁; 用于保护 Clients 的线程安全
//mu sync.Mutex
// 定义一个读写互斥锁; 用于保护 Clients 的线程安全
mu sync.RWMutex
}
// NewWXSocketMsgTask 新建抢红包管理器
func NewWXSocketMsgTask(wxConn wxface.IWXConnect) *WXSocketMsgTask {
return &WXSocketMsgTask{
WxConn: wxConn,
MsgItemChan: make(chan *wechat.AddMsg, 1000),
canDoNext: make(chan bool, 1),
EndChan: make(chan bool, 1),
isRunning: false,
wsEnabled: true, // 默认启用WebSocket功能,避免连接后延迟
Clients: make(map[string]*websocket.Conn),
Upgrader: websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
// 允许所有CORS跨域请求
CheckOrigin: func(r *http.Request) bool {
return true
},
},
mu: sync.RWMutex{},
}
}
func (wsmsg *WXSocketMsgTask) grapMsg() {
for {
select {
case wsmsg.CurrentMsgItem = <-wsmsg.MsgItemChan:
key := wsmsg.WxConn.GetWXAccount().GetUserInfo().UUID
// 使用消息包装器包含UUID信息
msgWrapper := NewMessageWrapper(key, wsmsg.CurrentMsgItem, "message")
jsonBytes, err := json.Marshal(msgWrapper)
if err != nil {
fmt.Println("消息序列化错误:", err)
continue // 继续处理下一条消息,而不是返回
}
conn := wsmsg.GetWebSocket(key)
if conn == nil {
fmt.Println("WebSocket连接不存在:", key)
continue // 继续处理下一条消息,而不是返回
}
// 对JSON内容进行Unicode解码处理
decodedJSON := decodeUnicodeEscapes(string(jsonBytes))
err = PushMessageToClient(decodedJSON, conn)
if err != nil {
fmt.Println("消息推送失败:", err)
wsmsg.DeleteWebSocket(key)
continue // 继续处理下一条消息,而不是返回
}
// 消息推送成功后继续处理下一条
case <-wsmsg.EndChan:
wsmsg.DeleteWebSocket(wsmsg.WxConn.GetWXAccount().GetUserInfo().UUID)
return
}
}
}
// Start 开始
func (wsmsg *WXSocketMsgTask) Start() {
if wsmsg.wsEnabled {
if wsmsg.isRunning {
return
}
wsmsg.isRunning = true
go wsmsg.grapMsg() // 直接启动消息处理循环
}
}
// Stop 结束协程
func (wsmsg *WXSocketMsgTask) Stop() {
if !wsmsg.isRunning {
return
}
wsmsg.isRunning = false
wsmsg.EndChan <- true
}
// GrapNext 抢下一个
func (wsmsg *WXSocketMsgTask) GrapNext() {
wsmsg.canDoNext <- true
}
// AddMsgItem 添加消息项
func (wsmsg *WXSocketMsgTask) AddMsgItem(msgItem *wechat.AddMsg) {
if !wsmsg.wsEnabled {
return
}
if !wsmsg.isRunning {
wsmsg.Start()
}
key := wsmsg.WxConn.GetWXAccount().GetUserInfo().UUID
conn := wsmsg.GetWebSocket(key)
// 打印日志
// fmt.Println("------------收到消息-----------\n",
// "NickName: ",wsmsg.WxConn.GetWXAccount().GetUserInfo().NickName,"\n",
// "Key: ",key,"\n",
// "Content: ",msgItem.Content.GetStr(),
// "\n",
// "----------------------------------")
if conn == nil {
// 只有 ws 长链接存在才会添加 收到的消息记录
// fmt.Println("WebSocket连接不存在,消息将不会发送:", key)
return
}
wsmsg.MsgItemChan <- msgItem
}
// GetCurrentMsgItem 获取当前正在消费的消息项
func (wsmsg *WXSocketMsgTask) GetCurrentMsgItem() *wechat.AddMsg {
return wsmsg.CurrentMsgItem
}
func PushMessageToClient(msg string, conn *websocket.Conn) error {
// 获取或创建当前连接的写锁
val, _ := connWriteMutexes.LoadOrStore(conn, &sync.Mutex{})
mutex := val.(*sync.Mutex)
// 使用互斥锁保护写操作
mutex.Lock()
defer mutex.Unlock()
// 使用 WebSocket 连接向客户端发送消息
fmt.Println("发送消息:", msg)
err := conn.WriteMessage(websocket.TextMessage, []byte(msg))
if err != nil {
// 如果发送错误,清理锁
connWriteMutexes.Delete(conn)
}
return err
}
// PushTypedMessageToClient 安全地发送特定类型的WebSocket消息
func PushTypedMessageToClient(messageType int, data []byte, conn *websocket.Conn) error {
// 获取或创建当前连接的写锁
val, _ := connWriteMutexes.LoadOrStore(conn, &sync.Mutex{})
mutex := val.(*sync.Mutex)
// 使用互斥锁保护写操作
mutex.Lock()
defer mutex.Unlock()
// 使用 WebSocket 连接向客户端发送消息
fmt.Printf("发送类型[%d]消息\n", messageType)
err := conn.WriteMessage(messageType, data)
if err != nil {
// 如果发送错误,清理锁
connWriteMutexes.Delete(conn)
}
return err
}
// GetWebSocket 获取 Clients 缓存中的 ws 长链接
func (wsmsg *WXSocketMsgTask) GetWebSocket(key string) (conn *websocket.Conn) {
wsmsg.mu.RLock() // 读取 Clients 时使用读锁
defer wsmsg.mu.RUnlock() // 解锁读锁
conn = wsmsg.Clients[key] // 读取 ws 长链接; 若 key 不存在则返回 nil
return conn
}
// UpdateWebSocket 更新 Clients 缓存中的 ws 长链接
func (wsmsg *WXSocketMsgTask) UpdateWebSocket(key string, conn *websocket.Conn) {
wsmsg.mu.Lock() // 写入或删除 Clients 时使用写锁
defer wsmsg.mu.Unlock() // 解锁
wsmsg.Clients[key] = conn // 更新 ws 长链接
wsmsg.wsEnabled = true // 当连接建立时启用WebSocket功能
fmt.Println("WebSocket连接已更新:", key)
}
// DeleteWebSocket 删除 Clients 缓存中的 ws 长链接
func (wsmsg *WXSocketMsgTask) DeleteWebSocket(key string) {
wsmsg.mu.Lock() // 写入或删除 Clients 时使用写锁
defer wsmsg.mu.Unlock() // 解锁
if conn, ok := wsmsg.Clients[key]; ok {
// 清理连接的写锁
connWriteMutexes.Delete(conn)
delete(wsmsg.Clients, key) // 删除 ws 长链接
fmt.Println("WebSocket连接已删除:", key)
// 如果没有连接,禁用WebSocket功能并停止任务
if len(wsmsg.Clients) == 0 {
wsmsg.wsEnabled = false
if wsmsg.isRunning {
wsmsg.Stop()
}
}
}
}
// 获取WebSocket启用状态
func (wsmsg *WXSocketMsgTask) IsWebSocketEnabled() bool {
return wsmsg.wsEnabled
}
// 设置WebSocket启用状态
func (wsmsg *WXSocketMsgTask) SetWebSocketEnabled(enabled bool) {
wsmsg.wsEnabled = enabled
// 如果禁用而且正在运行,停止任务
if !enabled && wsmsg.isRunning {
wsmsg.Stop()
} else if enabled && !wsmsg.isRunning && len(wsmsg.Clients) > 0 {
// 如果启用而且有客户端连接但未运行,启动任务
wsmsg.Start()
}
}