zrg
13 小时以前 b298e383b1f2ddc0cb8ad1715512f2be6eb7ef02
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
import {
    CommonUtils
} from "@/utils/common.js";
 
class WebSocketServices {
    constructor() {
        this.wsInstance = null; // WebSocket 实例
        this.isConnecting = false; // 连接中状态(避免并发重连)
        this.isConnected = false; // 已连接状态
        this.isReconnectStopped = false; // 停止重连标记
        this.wsUrl = CommonUtils.httpFormatWs(); // WebSocket 基础地址
 
        // 超时校验配置
        this.businessMessageInterval = 5 * 60 * 1000;
        this.noMessageTimeout = this.businessMessageInterval + 2 * 60 * 1000;
        this.noMessageTimer = null; // 无消息超时定时器
 
        // 监听函数引用
        this.openListener = null;
        this.messageListener = null;
        this.closeListener = null;
        this.errorListener = null;
 
        // 缓存用户信息
        this.currentUserId = "";
        this.currentUserName = "";
    }
 
    /**
     * 建立 WebSocket 连接
     * @param {string/number} userId - 用户ID(必填)
     * @param {string} userName - 用户名(必填)
     * @param {number} count - 当前重连次数
     * @param {number} limit - 最大重连次数(默认3次)
     */
    createConnect(userId, userName, count = 0, limit = 3) {
        // 登录建立连接时,需要重新获取http地址,可能会变更
        this.wsUrl = CommonUtils.httpFormatWs();
        // 缓存用户信息(用于重连)
        this.currentUserId = userId;
        this.currentUserName = userName;
 
        // 前置校验:避免无效连接和并发重连
        if (this.isConnecting || this.isConnected) return;
        if (!userId && userId !== 0) { // 兼容 userId 为 0 的合法场景
            CommonUtils.showTips({
                message: "用户标识不能为空,无法建立WebSocket连接"
            });
            return;
        }
        if (count > limit) {
            CommonUtils.showTips({
                message: `WebSocket 重连次数超出最大限制(${limit}次),已停止重连`,
            });
            this.isReconnectStopped = true;
            return;
        }
 
        console.log(`[WebSocket] 开始建立连接(第 ${count || 0} 次)`, {
            wsUrl: this.wsUrl,
            userId,
            userName,
        });
 
        // 清除历史残留:监听+定时器
        this.clearAllListeners();
        this.clearNoMessageTimer();
 
        // 发起连接
        this.isConnecting = true;
        this.wsInstance = uni.connectSocket({
            url: `${this.wsUrl}?userId=${encodeURIComponent(userId)}&userName=${encodeURIComponent(userName)}`,
            fail: (error) => {
                console.error("[WebSocket] 连接发起失败", error);
                this.isConnecting = false;
                this.triggerReconnect(count);
            },
        });
 
        // 监听连接成功:启动无消息超时校验
        this.openListener = uni.onSocketOpen((res) => {
            console.log("[WebSocket] 连接建立成功", res);
            this.isConnecting = false;
            this.isConnected = true;
            this.isReconnectStopped = false;
            this.startNoMessageCheck();
            count = 0;
        });
 
        this.messageListener = uni.onSocketMessage((res) => {
            try {
                const message = JSON.parse(res.data);
                console.log("[WebSocket] 收到业务消息", message);
 
                // 重置无消息定时器(有业务消息=连接正常)
                this.resetNoMessageTimer();
 
                // 处理业务消息
                if (message.Type === "MessageUnRead") { // 未读消息
                    let payload = {
                        pagePath: "/pages/ZLGL/OA_WorkLink/OA_WorkLinkBillList",
                        itemId: 0
                    }
                    const content = JSON.parse(message.Content);
                    this.showTaskTip(`您有${content.length}条消息需要处理!`, payload);
                    // this.emit("message", content); // 支持外部监听
                } else if (message.Type === "Message") {
                    const content = JSON.parse(message.Content)
                    let payload = {
                        pagePath: "/pages/ZLGL/OA_WorkLink/OA_WorkLinkBillList",
                        itemId: 0
                    }
                    this.showTaskTip(content[0]["内容"], payload, content[0]["主题"]);
                    
                }
            } catch (error) {
                console.error("[WebSocket] 消息解析失败", error, res.data);
            }
        });
 
        // 监听连接关闭:仅异常关闭触发重连
        this.closeListener = uni.onSocketClose((res) => {
            console.log("[WebSocket] 连接关闭", res);
            this.isConnecting = false;
            this.isConnected = false;
            this.clearNoMessageTimer();
 
            // 正常关闭(code=1000)或主动停止重连时,不重连
            if (!this.isReconnectStopped && res.code !== 1000) {
                this.triggerReconnect(count);
            }
        });
 
        // 监听连接错误:触发重连
        this.errorListener = uni.onSocketError((error) => {
            console.error("[WebSocket] 连接错误", error);
            this.isConnecting = false;
            this.clearNoMessageTimer();
            if (!this.isReconnectStopped) {
                this.triggerReconnect(count);
            }
        });
    }
 
    /**
     * 统一触发重连(延迟3秒)
     * @param {number} count - 当前重连次数
     */
    triggerReconnect(count) {
        console.log(`[WebSocket] 准备第 ${count + 1} 次重连`);
        setTimeout(() => {
            this.createConnect(this.currentUserId, this.currentUserName, count + 1);
        }, 3000);
    }
 
    /**
     * 启动“无业务消息”超时校验
     */
    startNoMessageCheck() {
        this.clearNoMessageTimer();
        this.noMessageTimer = setTimeout(() => {
            console.warn("[WebSocket] 7分钟未收到业务消息,判定连接失效,主动重连");
            this.closeSocket();
            this.createConnect(this.currentUserId, this.currentUserName);
        }, this.noMessageTimeout);
    }
 
    /**
     * 收到业务消息后,重置无消息定时器
     */
    resetNoMessageTimer() {
        this.startNoMessageCheck();
    }
 
    /**
     * 清除无消息定时器
     */
    clearNoMessageTimer() {
        if (this.noMessageTimer) {
            clearTimeout(this.noMessageTimer);
            this.noMessageTimer = null;
        }
    }
 
    /**
     * 清除所有 Socket 监听 
     */
    clearAllListeners() {
        if (this.openListener) {
            uni.offSocketOpen(this.openListener);
            this.openListener = null;
        }
        if (this.messageListener) {
            uni.offSocketMessage(this.messageListener);
            this.messageListener = null;
        }
        if (this.closeListener) {
            uni.offSocketClose(this.closeListener);
            this.closeListener = null;
        }
        if (this.errorListener) {
            uni.offSocketError(this.errorListener);
            this.errorListener = null;
        }
    }
 
    /**
     * 主动关闭 WebSocket 连接 (登出时关闭WebSocket连接)
     */
    closeSocket() {
        this.isReconnectStopped = true;
        this.clearAllListeners();
        this.clearNoMessageTimer();
 
        if (this.wsInstance) {
            uni.closeSocket({
                success: () => console.log("[WebSocket] 主动关闭连接成功"),
                fail: (error) => console.error("[WebSocket] 主动关闭连接失败", error),
            });
            this.wsInstance = null;
        }
 
        this.isConnected = false;
        this.isConnecting = false;
    }
 
 
    showTaskTip(Content, payloads, Title = "重要通知") {
        // #ifdef APP-PLUS || APP
        let content = Content;
        let options = {
            title: Title,
            cover: true, // 是否覆盖上一次的通知
            when: new Date(), // 通知显示时间
            icon: "../static/logo.png",
            largeIcon: "../static/logo.png"
        };
        // TODO 跳转到指定页
        let payload = JSON.stringify(payloads);
        console.log('payload: ', payload);
 
        plus.push.createMessage(content, payload, options);
        // #endif
    }
}
 
export default new WebSocketServices()