Compare commits

..

No commits in common. "5706c238a443227c604f23ec7130cede396436b9" and "cef7e50112ea95b11cc1250f00392702e06d58e7" have entirely different histories.

20 changed files with 2321 additions and 2922 deletions

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,8 @@
syntax = "proto3"; syntax = "proto3";
package accountFiee; package accountFiee;
import "github.com/mwitkow/go-proto-validators/validator.proto"; import "github.com/mwitkow/go-proto-validators@v0.3.2/validator.proto";
option go_package = "./;accountFiee"; option go_package = "./;accountFiee";
//protoc -I . -I C:\Users\lenovo\go\src --go_out=. --go-triple_out=. ./accountFiee.proto //protoc -I . -I C:\Users\lenovo\go\src --go_out=. --go-triple_out=. ./accountFiee.proto
@ -988,8 +989,8 @@ message ChatAutoReplyRulerData{
int64 deletedAt = 4; // int64 deletedAt = 4; //
string title = 5; // string title = 5; //
string ruler = 6; // string ruler = 6; //
int32 status = 7; //: 1= 2= int32 rulerStatus = 7; //: 1= 2=
string response =8; //
} }
message CreateChatAutoReplyRulerResp{ message CreateChatAutoReplyRulerResp{

View File

@ -1,8 +1,8 @@
// Code generated by protoc-gen-go-triple. DO NOT EDIT. // Code generated by protoc-gen-go-triple. DO NOT EDIT.
// versions: // versions:
// - protoc-gen-go-triple v1.0.8 // - protoc-gen-go-triple v1.0.8
// - protoc v4.22.0--rc2 // - protoc v4.24.0--rc1
// source: accountFiee.proto // source: api/accountFiee/accountFiee.proto
package accountFiee package accountFiee
@ -35,7 +35,7 @@ type AccountFieeClient interface {
OnlineLog(ctx context.Context, in *LoginInfosByUserIdRequest, opts ...grpc_go.CallOption) (*LoginLogsResponse, common.ErrorWithAttachment) OnlineLog(ctx context.Context, in *LoginInfosByUserIdRequest, opts ...grpc_go.CallOption) (*LoginLogsResponse, common.ErrorWithAttachment)
OnlineLogById(ctx context.Context, in *OnlineLogByIdRequest, opts ...grpc_go.CallOption) (*LoginLog, common.ErrorWithAttachment) OnlineLogById(ctx context.Context, in *OnlineLogByIdRequest, opts ...grpc_go.CallOption) (*LoginLog, common.ErrorWithAttachment)
CheckPwd(ctx context.Context, in *CheckPwdRequest, opts ...grpc_go.CallOption) (*UpdateResponse, common.ErrorWithAttachment) CheckPwd(ctx context.Context, in *CheckPwdRequest, opts ...grpc_go.CallOption) (*UpdateResponse, common.ErrorWithAttachment)
// rpc RegisterOrExist (RegistRequest) returns (RequestStatus) {} // rpc RegisterOrExist (RegistRequest) returns (RequestStatus) {}
SendMsg(ctx context.Context, in *SendMsgRequest, opts ...grpc_go.CallOption) (*SendMsgStatusResponse, common.ErrorWithAttachment) SendMsg(ctx context.Context, in *SendMsgRequest, opts ...grpc_go.CallOption) (*SendMsgStatusResponse, common.ErrorWithAttachment)
SendCustomMsg(ctx context.Context, in *SendCustomMsgRequest, opts ...grpc_go.CallOption) (*SendMsgStatusResponse, common.ErrorWithAttachment) SendCustomMsg(ctx context.Context, in *SendCustomMsgRequest, opts ...grpc_go.CallOption) (*SendMsgStatusResponse, common.ErrorWithAttachment)
SendExCustomMsg(ctx context.Context, in *SendCustomMsgRequest, opts ...grpc_go.CallOption) (*SendMsgStatusResponse, common.ErrorWithAttachment) SendExCustomMsg(ctx context.Context, in *SendCustomMsgRequest, opts ...grpc_go.CallOption) (*SendMsgStatusResponse, common.ErrorWithAttachment)
@ -70,7 +70,7 @@ type AccountFieeClient interface {
VerifySliderStatus(ctx context.Context, in *VerifySliderStatusRequest, opts ...grpc_go.CallOption) (*VerifySliderStatusResponse, common.ErrorWithAttachment) VerifySliderStatus(ctx context.Context, in *VerifySliderStatusRequest, opts ...grpc_go.CallOption) (*VerifySliderStatusResponse, common.ErrorWithAttachment)
// submit info // submit info
SaveSubmitInfo(ctx context.Context, in *SubmitInfoRequest, opts ...grpc_go.CallOption) (*CommonResponse, common.ErrorWithAttachment) SaveSubmitInfo(ctx context.Context, in *SubmitInfoRequest, opts ...grpc_go.CallOption) (*CommonResponse, common.ErrorWithAttachment)
// -----------------------------客服聊天系统-------------------------------- //-----------------------------客服聊天系统--------------------------------
CreateChatUser(ctx context.Context, in *ChatUserData, opts ...grpc_go.CallOption) (*CreateChatUserResp, common.ErrorWithAttachment) CreateChatUser(ctx context.Context, in *ChatUserData, opts ...grpc_go.CallOption) (*CreateChatUserResp, common.ErrorWithAttachment)
UpdateChatUser(ctx context.Context, in *ChatUserData, opts ...grpc_go.CallOption) (*CommonMsg, common.ErrorWithAttachment) UpdateChatUser(ctx context.Context, in *ChatUserData, opts ...grpc_go.CallOption) (*CommonMsg, common.ErrorWithAttachment)
SaveChatUser(ctx context.Context, in *ChatUserData, opts ...grpc_go.CallOption) (*CommonMsg, common.ErrorWithAttachment) SaveChatUser(ctx context.Context, in *ChatUserData, opts ...grpc_go.CallOption) (*CommonMsg, common.ErrorWithAttachment)
@ -591,7 +591,7 @@ type AccountFieeServer interface {
OnlineLog(context.Context, *LoginInfosByUserIdRequest) (*LoginLogsResponse, error) OnlineLog(context.Context, *LoginInfosByUserIdRequest) (*LoginLogsResponse, error)
OnlineLogById(context.Context, *OnlineLogByIdRequest) (*LoginLog, error) OnlineLogById(context.Context, *OnlineLogByIdRequest) (*LoginLog, error)
CheckPwd(context.Context, *CheckPwdRequest) (*UpdateResponse, error) CheckPwd(context.Context, *CheckPwdRequest) (*UpdateResponse, error)
// rpc RegisterOrExist (RegistRequest) returns (RequestStatus) {} // rpc RegisterOrExist (RegistRequest) returns (RequestStatus) {}
SendMsg(context.Context, *SendMsgRequest) (*SendMsgStatusResponse, error) SendMsg(context.Context, *SendMsgRequest) (*SendMsgStatusResponse, error)
SendCustomMsg(context.Context, *SendCustomMsgRequest) (*SendMsgStatusResponse, error) SendCustomMsg(context.Context, *SendCustomMsgRequest) (*SendMsgStatusResponse, error)
SendExCustomMsg(context.Context, *SendCustomMsgRequest) (*SendMsgStatusResponse, error) SendExCustomMsg(context.Context, *SendCustomMsgRequest) (*SendMsgStatusResponse, error)
@ -626,7 +626,7 @@ type AccountFieeServer interface {
VerifySliderStatus(context.Context, *VerifySliderStatusRequest) (*VerifySliderStatusResponse, error) VerifySliderStatus(context.Context, *VerifySliderStatusRequest) (*VerifySliderStatusResponse, error)
// submit info // submit info
SaveSubmitInfo(context.Context, *SubmitInfoRequest) (*CommonResponse, error) SaveSubmitInfo(context.Context, *SubmitInfoRequest) (*CommonResponse, error)
// -----------------------------客服聊天系统-------------------------------- //-----------------------------客服聊天系统--------------------------------
CreateChatUser(context.Context, *ChatUserData) (*CreateChatUserResp, error) CreateChatUser(context.Context, *ChatUserData) (*CreateChatUserResp, error)
UpdateChatUser(context.Context, *ChatUserData) (*CommonMsg, error) UpdateChatUser(context.Context, *ChatUserData) (*CommonMsg, error)
SaveChatUser(context.Context, *ChatUserData) (*CommonMsg, error) SaveChatUser(context.Context, *ChatUserData) (*CommonMsg, error)
@ -3074,5 +3074,5 @@ var AccountFiee_ServiceDesc = grpc_go.ServiceDesc{
}, },
}, },
Streams: []grpc_go.StreamDesc{}, Streams: []grpc_go.StreamDesc{},
Metadata: "accountFiee.proto", Metadata: "api/accountFiee/accountFiee.proto",
} }

View File

@ -14,7 +14,6 @@ import (
"fonchain-fiee/pkg/e" "fonchain-fiee/pkg/e"
"fonchain-fiee/pkg/service" "fonchain-fiee/pkg/service"
"fonchain-fiee/pkg/utils/secret" "fonchain-fiee/pkg/utils/secret"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@ -25,9 +24,6 @@ func ParseToChatUser(c *gin.Context) (chatUserInfo *accountFiee.ChatUserData, co
if exist { if exist {
domain = domainAny.(string) domain = domainAny.(string)
} }
if domain == "" {
domain = config.AppConfig.System.Domain
}
var err error var err error
token := c.GetHeader(e.Authorization) token := c.GetHeader(e.Authorization)
if token == "" { if token == "" {

View File

@ -13,6 +13,17 @@ import (
"strings" "strings"
) )
type WsType int
const (
RegisterType WsType = iota
ErrorType
TestType
ChatType
NewChatMsgType //新消息通知
AuthorizationType //token校验通知
)
// 消息结构 // 消息结构
type WSMessage struct { type WSMessage struct {
Type string `json:"type"` Type string `json:"type"`

View File

@ -40,8 +40,6 @@ func NewChatRoom() *ChatRoom {
register: make(clientChan), register: make(clientChan),
UnRegister: make(clientChan), UnRegister: make(clientChan),
broadcast: make(broadcastChan), broadcast: make(broadcastChan),
eventBus: []*EventListener{},
EventRwLocker: &sync.RWMutex{},
} }
go room.Run() go room.Run()
return &room return &room
@ -51,14 +49,12 @@ type broadcastMessage struct {
UserIds []int64 UserIds []int64
message []byte message []byte
} }
type ChatRoomEvent struct {
ListenEvent []ListenEvent
message []byte
SenderId int64
ReceiverIds []int64
}
type ( type (
// []byte类型管道 用于客户端接收消息数据
messageChan chan []byte
//
wsConnChan chan *websocket.Conn
// Client类型数据管道 // Client类型数据管道
clientChan chan *Client clientChan chan *Client
@ -68,7 +64,6 @@ type (
type ChatRoom struct { type ChatRoom struct {
clientsRwLocker *sync.RWMutex clientsRwLocker *sync.RWMutex
EventRwLocker *sync.RWMutex
//clients 客户端信息存储 //clients 客户端信息存储
//// 支持多客户端连接 map[userId]map[clientId]*Client //// 支持多客户端连接 map[userId]map[clientId]*Client
clients map[int64]map[string]*Client clients map[int64]map[string]*Client
@ -82,11 +77,7 @@ type ChatRoom struct {
//unRegister 注销管道 接收需要注销的客户端 //unRegister 注销管道 接收需要注销的客户端
UnRegister clientChan UnRegister clientChan
// 消息广播管道
broadcast broadcastChan broadcast broadcastChan
// 事件广播管道,向其它程序推送消息
eventBus []*EventListener
} }
func (o *ChatRoom) Run() { func (o *ChatRoom) Run() {
@ -95,9 +86,8 @@ func (o *ChatRoom) Run() {
select { select {
// 注册事件 // 注册事件
case newClient := <-o.register: case newClient := <-o.register:
o.pushEvent(EventUserJoin, EventProgressBefore, newClient) ////删除临时map中的客户户端
defer o.pushEvent(EventUserJoin, EventProgressAfter, newClient) //delete(o.tempClient, client.clientId)
o.clientsRwLocker.Lock() o.clientsRwLocker.Lock()
//添加到客户端集合中 //添加到客户端集合中
if o.clients[newClient.UserId] == nil { if o.clients[newClient.UserId] == nil {
@ -108,6 +98,15 @@ func (o *ChatRoom) Run() {
if o.Session == nil { if o.Session == nil {
o.Session = make(map[string][]*Client) o.Session = make(map[string][]*Client)
} }
//if _, ok := o.Session[newClient.SessionId]; ok {
// for i, client := range o.Session[newClient.SessionId] {
// if client.ClientId == newClient.ClientId {
// //将之前的客户端注销
// o.UnRegister <- client
// }
// o.Session[newClient.SessionId][i] = newClient
// }
//}
if newClient.Waiter { if newClient.Waiter {
//客服人员没有默认会话窗口,而是自动加入所有用户的会话 //客服人员没有默认会话窗口,而是自动加入所有用户的会话
for sessionId, _ := range o.Session { for sessionId, _ := range o.Session {
@ -119,7 +118,7 @@ func (o *ChatRoom) Run() {
} }
} }
} else { } else {
//普通用户添加会话的逻辑 //画家添加会话的逻辑
_, ok := o.Session[newClient.SessionId] _, ok := o.Session[newClient.SessionId]
if !ok { if !ok {
o.Session[newClient.SessionId] = make([]*Client, 0) o.Session[newClient.SessionId] = make([]*Client, 0)
@ -142,8 +141,6 @@ func (o *ChatRoom) Run() {
o.clientsRwLocker.Unlock() o.clientsRwLocker.Unlock()
//注销事件 //注销事件
case client := <-o.UnRegister: case client := <-o.UnRegister:
o.pushEvent(EventUserLeave, EventProgressBefore, client)
defer o.pushEvent(EventUserLeave, EventProgressAfter, client)
//panic 恢复 //panic 恢复
defer func() { defer func() {
if r := recover(); r != "" { if r := recover(); r != "" {
@ -192,10 +189,6 @@ func (o *ChatRoom) Register(c *Client) (sessionId string) {
func (o *ChatRoom) SendSessionMessage(sendUserId int64, sessionId string, msgType WsType, message any) (userIdInSession []int64, err error) { func (o *ChatRoom) SendSessionMessage(sendUserId int64, sessionId string, msgType WsType, message any) (userIdInSession []int64, err error) {
o.clientsRwLocker.Lock() o.clientsRwLocker.Lock()
defer o.clientsRwLocker.Unlock() defer o.clientsRwLocker.Unlock()
o.pushEvent(EventChatMessage, EventProgressBefore, sendUserId, sessionId, msgType, message)
defer o.pushEvent(EventChatMessage, EventProgressAfter, sendUserId, sessionId, msgType, message)
var msg = WsSessionInfo{ var msg = WsSessionInfo{
Type: msgType, Type: msgType,
Content: message, Content: message,
@ -241,7 +234,7 @@ func (o *ChatRoom) GetUserIdInSession(sessionId string, withoutUserId ...int64)
for _, userId := range withoutUserId { for _, userId := range withoutUserId {
if client.UserId == userId { if client.UserId == userId {
jump = true jump = true
break continue
} }
} }
} }
@ -275,14 +268,14 @@ func (o *ChatRoom) GetUserIdInSession(sessionId string, withoutUserId ...int64)
return return
} }
// func (o *ChatRoom) RegisterClient(c *Client) { // func (o ChatRoom) RegisterClient(c *Client) {
// o.register <- c // o.register <- c
// } // }
// //
// func (o *ChatRoom) DeleteClient(c *Client) { // func (o ChatRoom) DeleteClient(c *Client) {
// o.unRegister <- c // o.unRegister <- c
// } // }
func (o *ChatRoom) Broadcast(message []byte, userIds ...int64) { func (o ChatRoom) Broadcast(message []byte, userIds ...int64) {
// 如果userIds为空则群发,否则找到这个用户的ws对象 // 如果userIds为空则群发,否则找到这个用户的ws对象
if userIds == nil { if userIds == nil {
for _, userClients := range o.clients { for _, userClients := range o.clients {
@ -320,49 +313,3 @@ func (o *ChatRoom) Broadcast(message []byte, userIds ...int64) {
} }
} }
} }
// RegisterEventListener 注册聊天室事件监听者
func (o *ChatRoom) RegisterEventListener(listenerChan *EventListener) {
o.EventRwLocker.Lock()
defer o.EventRwLocker.Unlock()
o.eventBus = append(o.eventBus, listenerChan)
}
// 注销监听者
func (o *ChatRoom) UnRegisterEventListener(listenerChan *EventListener) {
o.EventRwLocker.Lock()
defer o.EventRwLocker.Unlock()
var registerListenerList []*EventListener
for i, listener := range o.eventBus {
if listener.Name == listenerChan.Name {
continue
}
registerListenerList = append(registerListenerList, o.eventBus[i])
}
o.eventBus = registerListenerList
}
// pushEvent 推送聊天室事件
func (o *ChatRoom) pushEvent(eventType EventType, progress EventProgress, data ...any) {
o.EventRwLocker.Lock()
defer o.EventRwLocker.Unlock()
for _, listener := range o.eventBus {
hit := false
for _, need := range listener.ListenEvents {
if need.EventType == eventType && need.ProgressType == progress {
hit = true
break
}
}
if hit == false {
continue
}
listener.Chan <- ListenEventData{
ListenEvent: ListenEvent{
EventType: eventType,
ProgressType: progress,
},
Data: data,
}
}
}

View File

@ -1,52 +0,0 @@
// Package ws -----------------------------
// @file : consts.go
// @author : JJXu
// @contact : wavingbear@163.com
// @time : 2025/6/14 09:44
// -------------------------------------------
package ws
// websocket 消息类型
type WsType int
const (
RegisterType WsType = iota //用户注册消息
ErrorType //错误消息
TestType //测试消息
ChatType //聊天消息
NewChatMsgType //新消息通知
AuthorizationType //token校验通知
)
// 事件总线中的事件类型
type EventType string
const (
EventConnection EventType = "connection" //websocket连接事件
EventUserJoin EventType = "user_join" //用户/客服加入聊天事件
EventUserLeave EventType = "user_leave" //用户离开事件
EventChatMessage EventType = "chat_message" //聊天消息传递事件
)
// before
type EventProgress string
const (
EventProgressBefore EventProgress = "before"
EventProgressAfter EventProgress = "after"
)
type ListenEvent struct {
EventType EventType `json:"type"`
ProgressType EventProgress `json:"progress"`
}
type ListenEventData struct {
ListenEvent
Data interface{}
}
type ListenEventChan chan ListenEventData
type EventListener struct {
Name string
ListenEvents []ListenEvent //需要监听的事件列表
Chan ListenEventChan
}

View File

@ -4,7 +4,7 @@
// @contact : wavingbear@163.com // @contact : wavingbear@163.com
// @time : 2024/9/11 下午5:18 // @time : 2024/9/11 下午5:18
// ------------------------------------------- // -------------------------------------------
package chatCache package asChat
import ( import (
"context" "context"
@ -12,7 +12,6 @@ import (
"fmt" "fmt"
"fonchain-fiee/api/accountFiee" "fonchain-fiee/api/accountFiee"
"fonchain-fiee/pkg/cache" "fonchain-fiee/pkg/cache"
"fonchain-fiee/pkg/service/asChat/dto"
"github.com/go-redis/redis" "github.com/go-redis/redis"
"github.com/goccy/go-json" "github.com/goccy/go-json"
"go.uber.org/zap" "go.uber.org/zap"
@ -29,7 +28,7 @@ const CacheNewMsgStatKey = "fiee:newMsgStat"
var chatCacheLocker sync.RWMutex var chatCacheLocker sync.RWMutex
type ChatCache struct { type ChatCache struct {
NewMessageStatExpireAfter time.Duration //消息统计的数据过期时间 newMessageStatExpireAfter time.Duration //消息统计的数据过期时间
} }
// ------------------------------存储用户的会话ID-------------------------------- // ------------------------------存储用户的会话ID--------------------------------
@ -42,7 +41,7 @@ func (cr ChatCache) SaveUserSession(userId int64, sessionId string) {
////var c = context.Background() ////var c = context.Background()
err := cache.RedisClient.Set(cr.GetUserSessionCacheKey(userId), sessionId, 0).Err() err := cache.RedisClient.Set(cr.GetUserSessionCacheKey(userId), sessionId, 0).Err()
if err != nil { if err != nil {
log.Print("保存用户会话失败", zap.Error(err)) log.Fatal("保存用户会话失败", zap.Error(err))
} }
} }
func (cr ChatCache) GetUserSession(userId int64) (sessionId string) { func (cr ChatCache) GetUserSession(userId int64) (sessionId string) {
@ -56,7 +55,7 @@ func (cr ChatCache) GetUserSession(userId int64) (sessionId string) {
if err.Error() == "redis: nil" { if err.Error() == "redis: nil" {
err = nil err = nil
} else { } else {
log.Print("获取用户会话失败", zap.Error(err)) log.Fatal("获取用户会话失败", zap.Error(err))
} }
} }
fmt.Println("GetUserSession-3, sessionId:", sessionId) fmt.Println("GetUserSession-3, sessionId:", sessionId)
@ -96,7 +95,7 @@ func (cr ChatCache) GetChatRecord(sessionId string) (data []*accountFiee.ChatRec
if err.Error() == "redis: nil" { if err.Error() == "redis: nil" {
err = nil err = nil
} }
//log.Print("获取聊天记录失败", zap.Error(err)) //log.Fatal("获取聊天记录失败", zap.Error(err))
return return
} }
fmt.Printf("cache data: %+v", string(messages)) fmt.Printf("cache data: %+v", string(messages))
@ -134,10 +133,10 @@ func (cr ChatCache) IncreaseNewMessageTotal(ownerId int64, sessionId string) (er
copy(data[1:], data[0:foundIndex]) copy(data[1:], data[0:foundIndex])
data[0] = elementToMove data[0] = elementToMove
} else if foundIndex == -1 { } else if foundIndex == -1 {
data = append([]dto.UserMsgStatic{{SessionId: sessionId, Total: 1}}, data...) data = append([]UserMsgStatic{{SessionId: sessionId, Total: 1}}, data...)
} }
} else { } else {
data = []dto.UserMsgStatic{{SessionId: sessionId, Total: 1}} data = []UserMsgStatic{{SessionId: sessionId, Total: 1}}
} }
return cr.coverOwnerNewMessageStat(ctx, ownerId, data) return cr.coverOwnerNewMessageStat(ctx, ownerId, data)
} }
@ -161,7 +160,7 @@ func (cr ChatCache) ResetNewMessageTotal(ownerId int64, sessionId string, total
} }
} }
if !found { if !found {
data = append(data, dto.UserMsgStatic{ data = append(data, UserMsgStatic{
SessionId: sessionId, SessionId: sessionId,
Total: tl, Total: tl,
}) })
@ -175,7 +174,7 @@ func (cr ChatCache) RecountNewMessageTotal(ownerId int64) {
var err error var err error
keys, err = cache.RedisClient.Keys(CacheChatRecordKey + "*").Result() keys, err = cache.RedisClient.Keys(CacheChatRecordKey + "*").Result()
if err != nil { if err != nil {
log.Print("获取聊天记录所有缓存KEY失败", zap.Error(err)) log.Fatal("获取聊天记录所有缓存KEY失败", zap.Error(err))
return return
} }
var countMap = make(map[string]int) var countMap = make(map[string]int)
@ -187,7 +186,7 @@ func (cr ChatCache) RecountNewMessageTotal(ownerId int64) {
if err.Error() == "redis: nil" { if err.Error() == "redis: nil" {
err = nil err = nil
} }
log.Print("获取聊天记录失败", zap.Error(err)) log.Fatal("获取聊天记录失败", zap.Error(err))
data = make([]*accountFiee.ChatRecordData, 0) data = make([]*accountFiee.ChatRecordData, 0)
continue continue
} }
@ -205,7 +204,7 @@ func (cr ChatCache) RecountNewMessageTotal(ownerId int64) {
for sessionId, count := range countMap { for sessionId, count := range countMap {
err = cr.ResetNewMessageTotal(ownerId, sessionId, int64(count)) err = cr.ResetNewMessageTotal(ownerId, sessionId, int64(count))
if err != nil { if err != nil {
log.Print("重置新消息数量统计", log.Fatal("重置新消息数量统计",
zap.String("function", "RecountNewMessageTotal"), zap.String("function", "RecountNewMessageTotal"),
zap.Int64("ownerId", ownerId), zap.Int64("ownerId", ownerId),
zap.String("sessionId", sessionId), zap.String("sessionId", sessionId),
@ -218,13 +217,13 @@ func (cr ChatCache) RecountNewMessageTotal(ownerId int64) {
} }
// erp获取最新的消息统计 // erp获取最新的消息统计
func (cr ChatCache) GetNewMessageStat(ctx context.Context, ownerId int64) (result []dto.UserMsgStatic) { func (cr ChatCache) GetNewMessageStat(ctx context.Context, ownerId int64) (result []UserMsgStatic) {
//chatCacheLocker.RLock() //chatCacheLocker.RLock()
//defer chatCacheLocker.RUnlock() //defer chatCacheLocker.RUnlock()
result = make([]dto.UserMsgStatic, 0) result = make([]UserMsgStatic, 0)
vals, err := cache.RedisClient.Get(cr.GetNewMsgStatCacheKey(ownerId)).Bytes() vals, err := cache.RedisClient.Get(cr.GetNewMsgStatCacheKey(ownerId)).Bytes()
if err != nil && errors.Is(err, redis.Nil) { if err != nil && errors.Is(err, redis.Nil) {
log.Print("从缓存获取新消息统计失败", zap.Error(err), zap.Int64("ownerId", ownerId)) log.Fatal("从缓存获取新消息统计失败", zap.Error(err), zap.Int64("ownerId", ownerId))
return return
} }
if vals != nil { if vals != nil {
@ -234,9 +233,9 @@ func (cr ChatCache) GetNewMessageStat(ctx context.Context, ownerId int64) (resul
} }
// 覆盖指定erp用户的新消息统计 // 覆盖指定erp用户的新消息统计
func (cr ChatCache) coverOwnerNewMessageStat(ctx context.Context, ownerId int64, data []dto.UserMsgStatic) (err error) { func (cr ChatCache) coverOwnerNewMessageStat(ctx context.Context, ownerId int64, data []UserMsgStatic) (err error) {
value, _ := json.Marshal(data) value, _ := json.Marshal(data)
//err = cache.RedisClient.Set(ctx, cr.GetNewMsgStatCacheKey(ownerId), value, cr.NewMessageStatExpireAfter).Err() //err = cache.RedisClient.Set(ctx, cr.GetNewMsgStatCacheKey(ownerId), value, cr.newMessageStatExpireAfter).Err()
err = cache.RedisClient.Set(cr.GetNewMsgStatCacheKey(ownerId), value, 0).Err() err = cache.RedisClient.Set(cr.GetNewMsgStatCacheKey(ownerId), value, 0).Err()
return return
} }

View File

@ -1,13 +1,11 @@
package asChat package asChat
import ( import (
"encoding/json"
"fmt" "fmt"
"fonchain-fiee/api/account" "fonchain-fiee/api/account"
"fonchain-fiee/api/accountFiee" "fonchain-fiee/api/accountFiee"
"fonchain-fiee/cmd/config" "fonchain-fiee/cmd/config"
"fonchain-fiee/pkg/service" "fonchain-fiee/pkg/service"
"fonchain-fiee/pkg/service/asChat/dto"
"fonchain-fiee/pkg/utils" "fonchain-fiee/pkg/utils"
"fonchain-fiee/pkg/utils/secret" "fonchain-fiee/pkg/utils/secret"
"fonchain-fiee/pkg/utils/stime" "fonchain-fiee/pkg/utils/stime"
@ -23,18 +21,12 @@ type ChatAutoReplyRulerHandler struct {
// 创建自动回复规则 // 创建自动回复规则
func (a *ChatAutoReplyRulerHandler) CreateChatAutoReplyRuler(c *gin.Context) { func (a *ChatAutoReplyRulerHandler) CreateChatAutoReplyRuler(c *gin.Context) {
var req dto.ChatAutoReplyData var req accountFiee.ChatAutoReplyRulerData
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
service.Error(c, err) service.Error(c, err)
return return
} }
rulerBytes, _ := json.Marshal(req.Rules) _, err := service.AccountFieeProvider.CreateChatAutoReplyRuler(c, &req)
protoReq := accountFiee.ChatAutoReplyRulerData{
Title: req.Title,
Ruler: string(rulerBytes),
Response: req.Response,
}
_, err := service.AccountFieeProvider.CreateChatAutoReplyRuler(c, &protoReq)
if err != nil { if err != nil {
service.Error(c, err) service.Error(c, err)
return return
@ -59,13 +51,12 @@ func (a *ChatAutoReplyRulerHandler) DeleteChatAutoReplyRuler(c *gin.Context) {
// 更新自动回复规则 // 更新自动回复规则
func (a *ChatAutoReplyRulerHandler) UpdateChatAutoReplyRuler(c *gin.Context) { func (a *ChatAutoReplyRulerHandler) UpdateChatAutoReplyRuler(c *gin.Context) {
var req dto.ChatAutoReplyData var req accountFiee.ChatAutoReplyRulerData
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
service.Error(c, err) service.Error(c, err)
return return
} }
protoReq := req.ToProtoData() _, err := service.AccountFieeProvider.UpdateChatAutoReplyRuler(c, &req)
_, err := service.AccountFieeProvider.UpdateChatAutoReplyRuler(c, protoReq)
if err != nil { if err != nil {
service.Error(c, err) service.Error(c, err)
return return
@ -85,14 +76,12 @@ func (a *ChatAutoReplyRulerHandler) GetChatAutoReplyRulerDetail(c *gin.Context)
service.Error(c, err) service.Error(c, err)
return return
} }
tmp := dto.ChatAutoReplyData{} service.Success(c, resp)
tmp.Parse(resp)
service.Success(c, tmp)
} }
// 批量查询自动回复规则 // 批量查询自动回复规则
func (a *ChatAutoReplyRulerHandler) GetChatAutoReplyRulerList(c *gin.Context) { func (a *ChatAutoReplyRulerHandler) GetChatAutoReplyRulerList(c *gin.Context) {
var req dto.GetChatAutoReplyRulerListRequest var req GetChatAutoReplyRulerListRequest
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
service.Error(c, err) service.Error(c, err)
return return
@ -104,21 +93,10 @@ func (a *ChatAutoReplyRulerHandler) GetChatAutoReplyRulerList(c *gin.Context) {
service.Error(c, err) service.Error(c, err)
return return
} }
var data []dto.ChatAutoReplyData service.Success(c, resp.List)
for _, v := range resp.List {
tmp := dto.ChatAutoReplyData{}
tmp.Parse(v)
data = append(data, tmp)
}
service.Success(c, map[string]interface{}{
"data": data,
"page": resp.Page,
"pagesize": resp.PageSize,
"total": resp.Total,
})
} }
func (a *ChatAutoReplyRulerHandler) ErpLoginDemo(c *gin.Context) { func (a *ChatAutoReplyRulerHandler) ErpLoginDemo(c *gin.Context) {
var req dto.ErpLoginDemoReq var req ErpLoginDemoReq
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
service.Error(c, err) service.Error(c, err)
return return
@ -163,7 +141,7 @@ func (a *ChatAutoReplyRulerHandler) ErpLoginDemo(c *gin.Context) {
service.Success(c, loginRes) service.Success(c, loginRes)
} }
func (a *ChatAutoReplyRulerHandler) FieeLoginDemo(c *gin.Context) { func (a *ChatAutoReplyRulerHandler) FieeLoginDemo(c *gin.Context) {
var req dto.ErpLoginDemoReq var req ErpLoginDemoReq
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
service.Error(c, err) service.Error(c, err)
return return

View File

@ -4,7 +4,7 @@
// @contact : wavingbear@163.com // @contact : wavingbear@163.com
// @time : 2022/10/21 18:17:17 // @time : 2022/10/21 18:17:17
// ------------------------------------------- // -------------------------------------------
package consts package asChat
import ( import (
"encoding/json" "encoding/json"

View File

@ -1,7 +0,0 @@
// Package consts -----------------------------
// @file : consts.go
// @author : JJXu
// @contact : wavingbear@163.com
// @time : 2025/6/13 17:40
// -------------------------------------------
package consts

View File

@ -4,12 +4,11 @@
// @contact : wavingbear@163.com // @contact : wavingbear@163.com
// @time : 2024/9/10 下午6:28 // @time : 2024/9/10 下午6:28
// ------------------------------------------- // -------------------------------------------
package dto package asChat
import ( import (
"encoding/json" "encoding/json"
"fonchain-fiee/api/accountFiee" "fonchain-fiee/api/accountFiee"
"log"
"time" "time"
) )
@ -141,43 +140,3 @@ type ErpLoginDemoReq struct {
Nickname string `json:"nickname"` Nickname string `json:"nickname"`
RealName string `json:"realName"` RealName string `json:"realName"`
} }
type ChatAutoReplyData struct {
ID int64 `json:"id"`
Title string `json:"title"`
Rules map[string]*AutoReplyRule `json:"rules"`
Response string `json:"response"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
Status int32 `json:"status"`
}
func (r *ChatAutoReplyData) ToProtoData() (data *accountFiee.ChatAutoReplyRulerData) {
jsonBytes, _ := json.Marshal(r.Rules)
data = &accountFiee.ChatAutoReplyRulerData{
ID: r.ID,
CreatedAt: r.CreatedAt,
UpdatedAt: r.UpdatedAt,
Title: r.Title,
Ruler: string(jsonBytes),
Status: r.Status,
Response: r.Response,
}
return
}
func (r *ChatAutoReplyData) Parse(data *accountFiee.ChatAutoReplyRulerData) {
err := json.Unmarshal([]byte(data.Ruler), &r.Rules)
log.Printf("ChatAutoReplyData parse err:%v\n", err)
r.ID = data.ID
r.CreatedAt = data.CreatedAt
r.UpdatedAt = data.UpdatedAt
r.Title = data.Title
r.Status = data.Status
r.Response = data.Response
}
type AutoReplyRule struct {
Enable bool `json:"enable"`
Content string `json:"content,omitempty"`
SecondDuration time.Duration `json:"secondDuration,omitempty"`
}

View File

@ -18,11 +18,6 @@ import (
"fonchain-fiee/pkg/common/ws" "fonchain-fiee/pkg/common/ws"
"fonchain-fiee/pkg/e" "fonchain-fiee/pkg/e"
"fonchain-fiee/pkg/service" "fonchain-fiee/pkg/service"
"fonchain-fiee/pkg/service/asChat/chatCache"
"fonchain-fiee/pkg/service/asChat/consts"
"fonchain-fiee/pkg/service/asChat/dto"
"fonchain-fiee/pkg/service/asChat/logic"
"fonchain-fiee/pkg/service/asChat/robot"
"fonchain-fiee/pkg/service/upload" "fonchain-fiee/pkg/service/upload"
"fonchain-fiee/pkg/utils" "fonchain-fiee/pkg/utils"
"fonchain-fiee/pkg/utils/stime" "fonchain-fiee/pkg/utils/stime"
@ -42,20 +37,18 @@ import (
) )
var ChatHandlerIns = ChatHandler{ var ChatHandlerIns = ChatHandler{
cache: chatCache.ChatCache{NewMessageStatExpireAfter: 10 * time.Minute}, cache: ChatCache{newMessageStatExpireAfter: 10 * time.Minute},
robot: robot.NewRobot(),
} }
type ChatHandler struct { type ChatHandler struct {
cache chatCache.ChatCache cache ChatCache
robot *robot.Robot
} }
func (cr ChatHandler) Connection(c *gin.Context) { func (cr ChatHandler) Connection(c *gin.Context) {
conn, err := ws.UpGrader.Upgrade(c.Writer, c.Request, nil) conn, err := ws.UpGrader.Upgrade(c.Writer, c.Request, nil)
conn.SetReadDeadline(time.Now().Add(time.Second * 10)) conn.SetReadDeadline(time.Now().Add(time.Second * 10))
if err != nil { if err != nil {
log.Print("无法升级为websocket连接", zap.Error(err)) log.Fatal("无法升级为websocket连接", zap.Error(err))
c.String(500, "无法转为websocket连接") c.String(500, "无法转为websocket连接")
return return
} }
@ -87,16 +80,16 @@ func (cr ChatHandler) Connection(c *gin.Context) {
fmt.Println("44444444444444,ws.NewClient") fmt.Println("44444444444444,ws.NewClient")
//注册ws客户端并发送clientId给ws客户端 //注册ws客户端并发送clientId给ws客户端
var cli = ws.NewClient(userInfo.ID, "", conn, consts.ChatRoom) var cli = ws.NewClient(userInfo.ID, "", conn, ChatRoom)
cli.Waiter = userInfo.Role == 2 cli.Waiter = userInfo.Role == 2
fmt.Println("55555555555555,GetUserSession") fmt.Println("55555555555555,GetUserSession")
//查询是否有历史的sessionId //查询是否有历史的sessionId
cli.SessionId = cr.cache.GetUserSession(userInfo.ID) cli.SessionId = cr.cache.GetUserSession(userInfo.ID)
consts.ChatRoom.Register(cli) ChatRoom.Register(cli)
cr.cache.SaveUserSession(userInfo.ID, cli.SessionId) cr.cache.SaveUserSession(userInfo.ID, cli.SessionId)
fmt.Println("66666666666666666666666666") fmt.Println("66666666666666666666666666")
go cli.WriteWait() go cli.WriteWait()
cli.Send <- consts.WsMessageRegisterCallback(cli.ClientId, cli.SessionId) cli.Send <- WsMessageRegisterCallback(cli.ClientId, cli.SessionId)
fmt.Println("777777777777777777777777") fmt.Println("777777777777777777777777")
// 处理websocket连接的逻辑 // 处理websocket连接的逻辑
ctx, _ := context.WithCancel(context.Background()) ctx, _ := context.WithCancel(context.Background())
@ -109,7 +102,7 @@ func (cr ChatHandler) Connection(c *gin.Context) {
} }
func (cr ChatHandler) NewMessage(c *gin.Context) { func (cr ChatHandler) NewMessage(c *gin.Context) {
var request dto.NewMessageRequest var request NewMessageRequest
if err := c.ShouldBindJSON(&request); err != nil { if err := c.ShouldBindJSON(&request); err != nil {
service.Error(c, err) service.Error(c, err)
return return
@ -129,97 +122,90 @@ func (cr ChatHandler) NewMessage(c *gin.Context) {
service.ErrWithCode(c, code) service.ErrWithCode(c, code)
return return
} }
err := logic.NewMessage(c, &cr.cache, chatUser, request) fmt.Println("NewMessage 22222222222222222222222222222222222")
//存储入库
if chatUser.NickName != "" {
chatUser.NickName = fmt.Sprintf("未知用户(%d)", chatUser.ID)
}
fmt.Println("NewMessage 3333333333333333333333333333333333")
var data = accountFiee.ChatRecordData{
SessionId: request.SessionId,
UserId: chatUser.ID,
Name: chatUser.NickName,
Avatar: "",
MsgType: request.MsgType,
Content: request.Message.Text,
LocalStamp: request.LocalStamp,
Medias: nil,
}
if len(request.Message.Media) > 0 {
for _, media := range request.Message.Media {
data.Medias = append(data.Medias, &accountFiee.ChatMediaData{
ID: media.MediaId,
})
}
}
fmt.Println("NewMessage 4444444444444444444444444444444444")
resp, err := service.AccountFieeProvider.CreateChatRecord(c, &data)
if err != nil { if err != nil {
service.Error(c, err) service.Error(c, errors.New("创建失败"))
return return
} }
fmt.Printf("CreateChatRecord resp:%+v\n", resp)
//fmt.Println("NewMessage 22222222222222222222222222222222222") //录入缓存
////存储入库 err = cr.cache.AddChatRecord(request.SessionId, resp.Data)
//if chatUser.NickName != "" { if err != nil {
// chatUser.NickName = fmt.Sprintf("未知用户(%d)", chatUser.ID) service.Error(c, errors.New("创建失败"))
//} return
//fmt.Println("NewMessage 3333333333333333333333333333333333") }
//var data = accountFiee.ChatRecordData{ fmt.Println("NewMessage 5 消息数量+1")
// SessionId: request.SessionId, //新消息数量统计+1
// UserId: chatUser.ID, noticeUserId := ChatRoom.GetUserIdInSession(request.SessionId, chatUser.ID)
// Name: chatUser.NickName, fmt.Println("NewMessage 5.1 消息数量配置结束")
// Avatar: "", fmt.Printf("noticeUserId %+v\n", noticeUserId)
// MsgType: request.MsgType, for _, userId := range noticeUserId {
// Content: request.Message.Text, fmt.Println("userId")
// LocalStamp: request.LocalStamp, cr.cache.IncreaseNewMessageTotal(userId, request.SessionId)
// Medias: nil, }
//} fmt.Println("NewMessage 6")
//if len(request.Message.Media) > 0 { //发送websocket消息提醒通知
// for _, media := range request.Message.Media { var notice = MessageListType{}
// data.Medias = append(data.Medias, &accountFiee.ChatMediaData{ notice.BuildMessage(resp.Data)
// ID: media.MediaId, fmt.Printf("ws消息提醒:%+v\n", notice)
// }) _, err = ChatRoom.SendSessionMessage(chatUser.ID, request.SessionId, ws.NewChatMsgType, notice)
if err != nil {
log.Fatal("发送新消息通知失败", zap.Error(err), zap.Any("notice", notice))
}
fmt.Println("NewMessage 7 -end")
//发送app推送(无横幅推送)
//go func() {
// omitMessage := ""
// switch request.MsgType {
// case accountFiee.MsgType_TextMsgType:
// runMsg := []rune(request.Text)
// if len(runMsg) > 15 {
// omitMessage = string(runMsg[:15]) + "..."
// } else {
// omitMessage = request.Text
// }
// case accountFiee.MsgType_ImageMsgType:
// omitMessage = "[图片]"
// case accountFiee.MsgType_AudioMsgType:
// omitMessage = "[音频]"
// case accountFiee.MsgType_VideoMsgType:
// omitMessage = "[视频]"
// default:
// omitMessage = "新消息请查收"
// } // }
//} // for _, userId := range noticeUserId {
//fmt.Println("NewMessage 4444444444444444444444444444444444") // _ = asPusher.NewArtistinfoUniPush().NewChatMessageNotice(userId, omitMessage)
//resp, err := service.AccountFieeProvider.CreateChatRecord(c, &data) // }
//if err != nil { //}()
// service.Error(c, errors.New("创建失败"))
// return
//}
//fmt.Printf("CreateChatRecord resp:%+v\n", resp)
////录入缓存
//err = cr.cache.AddChatRecord(request.SessionId, resp.Data)
//if err != nil {
// service.Error(c, errors.New("创建失败"))
// return
//}
//fmt.Println("NewMessage 5 消息数量+1")
////新消息数量统计+1
//noticeUserId := consts.ChatRoom.GetUserIdInSession(request.SessionId, chatUser.ID)
//fmt.Println("NewMessage 5.1 消息数量配置结束")
//fmt.Printf("noticeUserId %+v\n", noticeUserId)
//for _, userId := range noticeUserId {
// fmt.Println("userId")
// cr.cache.IncreaseNewMessageTotal(userId, request.SessionId)
//}
//fmt.Println("NewMessage 6")
////发送websocket消息提醒通知
//var notice = dto.MessageListType{}
//notice.BuildMessage(resp.Data)
//fmt.Printf("ws消息提醒:%+v\n", notice)
//_, err = consts.ChatRoom.SendSessionMessage(chatUser.ID, request.SessionId, ws.NewChatMsgType, notice)
//if err != nil {
// log.Print("发送新消息通知失败", zap.Error(err), zap.Any("notice", notice))
//}
//cr.robot.Listen(&data)
//fmt.Println("NewMessage 7 -end")
////发送app推送(无横幅推送)
////go func() {
//// omitMessage := ""
//// switch request.MsgType {
//// case accountFiee.MsgType_TextMsgType:
//// runMsg := []rune(request.Text)
//// if len(runMsg) > 15 {
//// omitMessage = string(runMsg[:15]) + "..."
//// } else {
//// omitMessage = request.Text
//// }
//// case accountFiee.MsgType_ImageMsgType:
//// omitMessage = "[图片]"
//// case accountFiee.MsgType_AudioMsgType:
//// omitMessage = "[音频]"
//// case accountFiee.MsgType_VideoMsgType:
//// omitMessage = "[视频]"
//// default:
//// omitMessage = "新消息请查收"
//// }
//// for _, userId := range noticeUserId {
//// _ = asPusher.NewArtistinfoUniPush().NewChatMessageNotice(userId, omitMessage)
//// }
////}()
service.Success(c) service.Success(c)
} }
func (cr ChatHandler) MessageList(c *gin.Context) { func (cr ChatHandler) MessageList(c *gin.Context) {
var request dto.MessageListRequest var request MessageListRequest
if err := c.ShouldBindJSON(&request); err != nil { if err := c.ShouldBindJSON(&request); err != nil {
service.Error(c, err) service.Error(c, err)
return return
@ -237,7 +223,7 @@ func (cr ChatHandler) MessageList(c *gin.Context) {
service.Error(c, errors.New("pageSize校验错误")) service.Error(c, errors.New("pageSize校验错误"))
return return
} }
var resp = make([]*dto.MessageListType, 0) var resp = make([]*MessageListType, 0)
if request.CurrentId == 0 && request.Direction == 1 { if request.CurrentId == 0 && request.Direction == 1 {
service.Success(c, resp) service.Success(c, resp)
return return
@ -273,12 +259,12 @@ func (cr ChatHandler) MessageList(c *gin.Context) {
} }
err := cr.cache.CoverChatRecord(request.SessionId, messages) err := cr.cache.CoverChatRecord(request.SessionId, messages)
if err != nil { if err != nil {
log.Print("设置消息已读失败", zap.Error(err)) log.Fatal("设置消息已读失败", zap.Error(err))
} }
for _, v := range messages { for _, v := range messages {
_, err = service.AccountFieeProvider.SaveChatRecord(context.Background(), v) _, err = service.AccountFieeProvider.SaveChatRecord(context.Background(), v)
if err != nil { if err != nil {
log.Print("设置消息已读失败", zap.Error(err)) log.Fatal("设置消息已读失败", zap.Error(err))
} }
} }
} }
@ -298,7 +284,7 @@ func (cr ChatHandler) MessageList(c *gin.Context) {
messages = recordResp.List messages = recordResp.List
err = cr.cache.CoverChatRecord(request.SessionId, messages) err = cr.cache.CoverChatRecord(request.SessionId, messages)
if err != nil { if err != nil {
log.Print("覆盖聊天记录失败", zap.Error(err)) log.Fatal("覆盖聊天记录失败", zap.Error(err))
} }
} }
if request.Recent { if request.Recent {
@ -314,7 +300,7 @@ func (cr ChatHandler) MessageList(c *gin.Context) {
} }
} }
returnDataIdList = append(returnDataIdList, message.ID) returnDataIdList = append(returnDataIdList, message.ID)
var msg = &dto.MessageListType{} var msg = &MessageListType{}
msg.BuildMessage(message) msg.BuildMessage(message)
resp = append(resp, msg) resp = append(resp, msg)
} }
@ -346,7 +332,7 @@ func (cr ChatHandler) MessageList(c *gin.Context) {
} }
total++ total++
returnDataIdList = append(returnDataIdList, message.ID) returnDataIdList = append(returnDataIdList, message.ID)
var msg = &dto.MessageListType{} var msg = &MessageListType{}
msg.BuildMessage(message) msg.BuildMessage(message)
resp = append(resp, msg) resp = append(resp, msg)
} }
@ -358,7 +344,7 @@ func (cr ChatHandler) MessageList(c *gin.Context) {
//优化空列表 //优化空列表
for i, v := range resp { for i, v := range resp {
if v.Message.Media == nil { if v.Message.Media == nil {
resp[i].Message.Media = []dto.MessageMedia{} resp[i].Message.Media = []MessageMedia{}
} }
} }
service.Success(c, resp) service.Success(c, resp)
@ -375,7 +361,7 @@ func (cr ChatHandler) Upload(c *gin.Context) {
//获取文件对象 //获取文件对象
file, err := c.FormFile("file") file, err := c.FormFile("file")
if err != nil { if err != nil {
log.Print("ERROR: upload file failed. ", zap.Error(err)) log.Fatal("ERROR: upload file failed. ", zap.Error(err))
return return
} }
duration := c.PostForm("duration") duration := c.PostForm("duration")
@ -414,7 +400,7 @@ func (cr ChatHandler) Upload(c *gin.Context) {
//检查文件是否存在 //检查文件是否存在
checkResp, err := service.AccountFieeProvider.GetChatMediaList(c, &accountFiee.GetChatMediaListRequest{Query: &accountFiee.ChatMediaData{Md5: md5String}, Page: 1, PageSize: 1}) checkResp, err := service.AccountFieeProvider.GetChatMediaList(c, &accountFiee.GetChatMediaListRequest{Query: &accountFiee.ChatMediaData{Md5: md5String}, Page: 1, PageSize: 1})
if err != nil { if err != nil {
log.Print("md5查询附件失败", zap.Error(err)) log.Fatal("md5查询附件失败", zap.Error(err))
} }
if checkResp.Total > 0 { if checkResp.Total > 0 {
service.Success(c, checkResp.List[0]) service.Success(c, checkResp.List[0])
@ -497,13 +483,13 @@ func (cr ChatHandler) UserMessageStat(c *gin.Context) {
reverse(result) reverse(result)
service.Success(c, result) service.Success(c, result)
} }
func reverse(slice []dto.UserMsgStatic) { func reverse(slice []UserMsgStatic) {
for i, j := 0, len(slice)-1; i < j; i, j = i+1, j-1 { for i, j := 0, len(slice)-1; i < j; i, j = i+1, j-1 {
slice[i], slice[j] = slice[j], slice[i] slice[i], slice[j] = slice[j], slice[i]
} }
} }
func (cr ChatHandler) VoiceToText(c *gin.Context) { func (cr ChatHandler) VoiceToText(c *gin.Context) {
var req dto.VoiceToTextRequest var req VoiceToTextRequest
if err := c.ShouldBindJSON(&req); err != nil { if err := c.ShouldBindJSON(&req); err != nil {
service.Error(c, err) service.Error(c, err)
return return

View File

@ -1,13 +0,0 @@
// Package autoReply -----------------------------
// @file : intreface.go
// @author : JJXu
// @contact : wavingbear@163.com
// @time : 2025/6/13 16:15
// -------------------------------------------
package asChat
type IReplyRuler interface {
Name() string //规则名称
Check()
RunScript() string //运行脚本
}

View File

@ -1,86 +0,0 @@
// Package service -----------------------------
// @file : chat.go
// @author : JJXu
// @contact : wavingbear@163.com
// @time : 2025/6/13 19:04
// -------------------------------------------
package logic
import (
"context"
"errors"
"fmt"
"fonchain-fiee/api/accountFiee"
"fonchain-fiee/pkg/common/ws"
"fonchain-fiee/pkg/service"
"fonchain-fiee/pkg/service/asChat/chatCache"
"fonchain-fiee/pkg/service/asChat/consts"
"fonchain-fiee/pkg/service/asChat/dto"
"go.uber.org/zap"
"log"
)
func NewMessage(ctx context.Context, cache *chatCache.ChatCache, chatUser *accountFiee.ChatUserData, request dto.NewMessageRequest) (err error) {
if request.SessionId == "" {
return errors.New("sessionId不能为空")
}
if request.MsgType == 0 {
return errors.New("msgType不能为空")
}
fmt.Println("NewMessage 1111111111111111111111111111111")
fmt.Println("NewMessage 22222222222222222222222222222222222")
//存储入库
if chatUser.NickName != "" {
chatUser.NickName = fmt.Sprintf("未知用户(%d)", chatUser.ID)
}
fmt.Println("NewMessage 3333333333333333333333333333333333")
var data = accountFiee.ChatRecordData{
SessionId: request.SessionId,
UserId: chatUser.ID,
Name: chatUser.NickName,
Avatar: "",
MsgType: request.MsgType,
Content: request.Message.Text,
LocalStamp: request.LocalStamp,
Medias: nil,
}
if len(request.Message.Media) > 0 {
for _, media := range request.Message.Media {
data.Medias = append(data.Medias, &accountFiee.ChatMediaData{
ID: media.MediaId,
})
}
}
fmt.Println("NewMessage 4444444444444444444444444444444444")
resp, err := service.AccountFieeProvider.CreateChatRecord(ctx, &data)
if err != nil {
return errors.New("消息发送失败")
}
fmt.Printf("CreateChatRecord resp:%+v\n", resp)
//录入缓存
err = cache.AddChatRecord(request.SessionId, resp.Data)
if err != nil {
log.Printf("cache.AddChatRecord 失败:%v", err)
return errors.New("消息发送失败")
}
fmt.Println("NewMessage 5 消息数量+1")
//新消息数量统计+1
noticeUserId := consts.ChatRoom.GetUserIdInSession(request.SessionId, chatUser.ID)
fmt.Println("NewMessage 5.1 消息数量配置结束")
fmt.Printf("noticeUserId %+v\n", noticeUserId)
for _, userId := range noticeUserId {
fmt.Println("userId")
cache.IncreaseNewMessageTotal(userId, request.SessionId)
}
fmt.Println("NewMessage 6")
//发送websocket消息提醒通知
var notice = dto.MessageListType{}
notice.BuildMessage(resp.Data)
fmt.Printf("ws消息提醒:%+v\n", notice)
_, err = consts.ChatRoom.SendSessionMessage(chatUser.ID, request.SessionId, ws.NewChatMsgType, notice)
if err != nil {
log.Print("发送新消息通知失败", zap.Error(err), zap.Any("notice", notice))
}
fmt.Println("NewMessage 7 -end")
return nil
}

View File

@ -1,39 +0,0 @@
// Package autoReply -----------------------------
// @file : KeywordsReplyRuler.go
// @author : JJXu
// @contact : wavingbear@163.com
// @time : 2025/6/13 16:21
// -------------------------------------------
package robot
import (
"time"
)
// 使用go开发一个自动回复功能
// 一个自动回复消息有多种触发条件:
// 1. 关键词触发
// 2. 进入聊天系统后直接发送
// 3. 若干秒不回复自动发送
//func (k KeywordsRuleChecker) Do(sessionId string, response string, chatRoom *ws.ChatRoom) (err error) {
// var notice = dto.MessageListType{}
// notice.BuildMessage(response)
// _, err = chatRoom.SendSessionMessage(1, sessionId, ws.NewChatMsgType, notice)
// return nil
//}
type AutoReply struct {
Response string `json:"response"`
Rules map[string]IRule `json:"rules"`
}
type AutoReplyRule struct {
Enable bool `json:"enable"`
Keywords []string `json:"keywords"`
ReplyTimeout int `json:"replyTimeout"` // 回复超时时间
}
type AutoReplyManager struct {
replies []AutoReply
lastMessage time.Time
}

View File

@ -1,84 +0,0 @@
// Package robot -----------------------------
// @file : replyRuler.go
// @author : JJXu
// @contact : wavingbear@163.com
// @time : 2025/6/13 17:39
// -------------------------------------------
package robot
import (
"context"
"fonchain-fiee/api/accountFiee"
"fonchain-fiee/pkg/common/ws"
"fonchain-fiee/pkg/service"
"fonchain-fiee/pkg/service/asChat/consts"
"fonchain-fiee/pkg/service/asChat/dto"
"strings"
"time"
)
type Reply struct {
Response string
Rules []IRule
}
type IRule interface {
Hit(msg *accountFiee.ChatRecordData) (hit bool, runTime time.Time, logic func(robotId int64, response string) error)
}
// KeywordsRuleChecker 关键字回复
type KeywordsRuleChecker struct {
Keywords []string `json:"keywords"`
}
func (k KeywordsRuleChecker) Hit(record *accountFiee.ChatRecordData) (hit bool, runTime time.Time, logic func(robotId int64, response string) error) {
for _, v := range k.Keywords {
if strings.Contains(record.Content, v) {
hit = true
break
}
}
logic = func(robotId int64, response string) error {
var notice = dto.MessageListType{}
notice.BuildMessage(record)
_, err := consts.ChatRoom.SendSessionMessage(robotId, record.SessionId, ws.NewChatMsgType, notice)
return err
}
return
}
// 用户打开聊天会话直接发送
type ReplyWhenUserJoinSession struct {
}
func (k ReplyWhenUserJoinSession) Hit(record *accountFiee.ChatRecordData, robotId int64) (hit bool, runTime time.Time, logic func(robotId int64, response string) error) {
queryRes, err := service.AccountFieeProvider.GetChatRecordList(context.Background(), &accountFiee.GetChatRecordListRequest{
Query: &accountFiee.ChatRecordData{
SessionId: record.SessionId,
},
Page: 1,
PageSize: 1,
Order: "created_at desc",
})
if err != nil {
return
}
//如果最近一次的消息也是机器人发送的,就不再发送了
for i, v := range queryRes.List {
if i == 0 {
if v.UserId == robotId {
return
} else {
break
}
}
}
logic = func(robotId int64, response string) error {
var notice = dto.MessageListType{}
notice.BuildMessage(record)
_, err = consts.ChatRoom.SendSessionMessage(robotId, record.SessionId, ws.NewChatMsgType, notice)
return err
}
return
}

View File

@ -1,167 +0,0 @@
// Package robot -----------------------------
// @file : robot.go
// @author : JJXu
// @contact : wavingbear@163.com
// @time : 2025/6/13 17:41
// -------------------------------------------
package robot
import (
"context"
"fmt"
"fonchain-fiee/api/accountFiee"
"fonchain-fiee/pkg/common/ws"
"fonchain-fiee/pkg/service"
"fonchain-fiee/pkg/service/asChat/consts"
"log"
"sync"
"time"
)
func NewRobot() *Robot {
ctx := context.Background()
robotQuery, err := service.AccountFieeProvider.GetChatUserList(ctx, &accountFiee.GetChatUserListRequest{
Query: &accountFiee.ChatUserData{Role: 3},
Page: 1, PageSize: 1,
})
if err != nil {
panic("聊天机器人初始化失败,err:" + err.Error())
}
var robotInfo *accountFiee.ChatUserData
if robotQuery.Total > 0 {
robotInfo = robotQuery.List[0]
} else {
robotInfo = &accountFiee.ChatUserData{
NickName: "阿泰",
Role: 3,
Origin: "fontree",
}
createChatUserResp, errs := service.AccountFieeProvider.CreateChatUser(ctx, robotInfo)
if errs != nil {
panic("聊天机器人创建失败,err:" + errs.Error())
}
robotInfo = createChatUserResp.Data
}
r := &Robot{
Info: robotInfo,
EventListener: &ws.EventListener{
Name: "robot1",
ListenEvents: []ws.ListenEvent{ //只监听消息推送事件
{ws.EventChatMessage, ws.EventProgressAfter},
},
Chan: make(ws.ListenEventChan),
},
}
consts.ChatRoom.RegisterEventListener(r.EventListener)
go r.Run()
return r
}
type Robot struct {
Info *accountFiee.ChatUserData //机器人信息
Rules []Reply //回复规则
DelayTask []RobotTask //演示任务
ticker *time.Ticker //定时器
stopChan chan struct{} //停止管道
isRunning bool //运行状态
mu sync.Mutex
*ws.EventListener
}
func (r *Robot) Listen(record *accountFiee.ChatRecordData) {
for _, replyRules := range r.Rules {
for _, rule := range replyRules.Rules {
hit, runTime, function := rule.Hit(record)
if hit && function != nil {
if runTime.IsZero() {
go func() {
err := function(r.Info.ID, replyRules.Response)
if err != nil {
log.Printf("聊天机器人[%d]回复消息失败:%v", r.Info.ID, err)
}
}()
} else {
r.mu.Lock()
r.DelayTask = append(r.DelayTask, RobotTask{
RunTime: runTime,
Run: function,
Response: replyRules.Response,
})
r.mu.Unlock()
// 添加任务后启动定时任务(如果未运行)
if !r.isRunning {
go r.Run()
}
}
break
}
}
}
}
func (r *Robot) Run() {
r.mu.Lock()
if r.isRunning {
r.mu.Unlock()
return
}
r.isRunning = true
r.ticker = time.NewTicker(time.Second)
r.stopChan = make(chan struct{})
r.mu.Unlock()
defer func() {
r.mu.Lock()
r.isRunning = false
if r.ticker != nil {
r.ticker.Stop()
r.ticker = nil
}
r.stopChan = nil
r.mu.Unlock()
}()
for {
select {
case <-r.ticker.C:
r.mu.Lock()
if len(r.DelayTask) == 0 {
r.mu.Unlock()
break
//return // 没有任务时退出
}
now := time.Now()
var remainingTasks []RobotTask
for _, task := range r.DelayTask {
if now.After(task.RunTime) {
// 执行任务
go func() {
err := task.Run(r.Info.ID, task.Response)
if err != nil {
log.Printf("聊天机器人[%d]回复消息失败:%v", r.Info.ID, err)
}
}()
} else {
// 保留未到期的任务
remainingTasks = append(remainingTasks, task)
}
}
r.DelayTask = remainingTasks
r.mu.Unlock()
case <-r.stopChan:
return
case event := <-r.EventListener.Chan:
fmt.Sprintf("listen event:%#v\n", event)
}
}
}
// Stop 主动停止机器人的定时任务
func (r *Robot) Stop() {
r.mu.Lock()
if r.stopChan != nil {
close(r.stopChan)
}
r.mu.Unlock()
}

View File

@ -1,7 +0,0 @@
// Package autoReply -----------------------------
// @file : rulerList.go
// @author : JJXu
// @contact : wavingbear@163.com
// @time : 2025/6/13 16:16
// -------------------------------------------
package robot

View File

@ -1,15 +0,0 @@
// Package robot -----------------------------
// @file : task.go
// @author : JJXu
// @contact : wavingbear@163.com
// @time : 2025/6/13 18:02
// -------------------------------------------
package robot
import "time"
type RobotTask struct {
RunTime time.Time
Run func(robotId int64, response string) error
Response string
}