/
client.go
121 lines (106 loc) · 3.47 KB
/
client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// Copyright 2019-2020 Axetroy. All rights reserved. MIT license.
package ws
import (
"github.com/axetroy/go-server/internal/library/exception"
"github.com/axetroy/go-server/internal/schema"
"github.com/google/uuid"
"github.com/gorilla/websocket"
"log"
"sync"
"time"
)
// 来回传输的消息体
type Message struct {
Id string `json:"id,omitempty" validate:"omitempty" comment:"消息 ID"` // 每条消息的 ID,在写入数据库之后会有
From string `json:"from,omitempty" validate:"omitempty,uuid" comment:"发送者"` // 从谁发出来的
To string `json:"to,omitempty" validate:"omitempty,uuid" comment:"发送目标"` // 要发送的目标 ID,只有客服才需要带 target 字段,指明发送给谁
Type string `json:"type" validate:"required" comment:"会话类型"` // 会话类型
Payload interface{} `json:"payload,omitempty" validate:"omitempty" comment:"消息数据"` // 本次消息的数据
Date string `json:"date,omitempty" validate:"omitempty" comment:"时间戳"` // 消息的时间
OpID *string `json:"op_id,omitempty" validate:"omitempty,uuid" comment:"操作ID"` // 客户端发送的 UUID 用于标记该消息,服务端应该返回相同的 op_id,主要用于消息回执
}
type Client struct {
sync.RWMutex
conn *websocket.Conn // Socket 连接
UUID string // Socket 连接的唯一标识符
profile *schema.ProfilePublic // 用户的身份信息,仅用于成功身份认证的连接
LatestReceiveAt time.Time // 最近接收到的消息的时间,用于判断用户是否空闲
Closed bool // 连接是否已关闭
Ready bool // 该客户端是否已准备就绪,给客服端用的,ready = true 的时候系统才会分配用户
}
func NewClient(conn *websocket.Conn) *Client {
id, err := uuid.NewRandom()
if err != nil {
log.Printf("%+v\n", err)
}
return &Client{
conn: conn,
UUID: id.String(),
profile: nil,
LatestReceiveAt: time.Now(),
}
}
func (c *Client) GetProfile() *schema.ProfilePublic {
c.Lock()
defer c.Unlock()
return c.profile
}
func (c *Client) RegenerateUUID() {
c.Lock()
defer c.Unlock()
id, _ := uuid.NewRandom()
c.UUID = id.String()
}
func (c *Client) UpdateProfile(profile schema.ProfilePublic) {
c.Lock()
defer c.Unlock()
c.profile = &profile
}
// 向客户端写数据
func (c *Client) WriteJSON(data Message) error {
return c.conn.WriteJSON(data)
}
func (c *Client) WriteError(err error, data Message) error {
if e, ok := err.(exception.Error); ok {
if e1 := c.WriteJSON(Message{
Type: string(TypeResponseUserError),
To: c.UUID,
Payload: schema.Response{
Message: e.Error(),
Status: e.Code(),
Data: data,
},
Date: time.Now().Format(time.RFC3339Nano),
OpID: data.OpID,
}); e1 != nil {
return e1
}
} else {
if e2 := c.WriteJSON(Message{
Type: string(TypeResponseUserError),
To: c.UUID,
Payload: schema.Response{
Message: e.Error(),
Status: exception.Unknown.Code(),
Data: data,
},
Date: time.Now().Format(time.RFC3339Nano),
OpID: data.OpID,
}); e2 != nil {
return e2
}
}
return nil
}
// 关闭连接
func (c *Client) Close() error {
c.Lock()
defer c.Unlock()
c.Closed = true
return c.conn.Close()
}
func (c *Client) SetReady(ready bool) {
c.Lock()
defer c.Unlock()
c.Ready = ready
}