@saiboniuma/realtime-core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1265 @@
1
+ import { HubConnectionState as I, HubConnectionBuilder as F, LogLevel as B } from "@microsoft/signalr";
2
+ const d = {
3
+ CONNECTING: 0,
4
+ CONNECTED: 1,
5
+ DISCONNECTED: 2,
6
+ FAILED: 3,
7
+ RECONNECTING: 4
8
+ };
9
+ class J {
10
+ constructor() {
11
+ this.listeners = /* @__PURE__ */ new Map();
12
+ }
13
+ /** 注册事件监听 */
14
+ on(e, t) {
15
+ this.listeners.has(e) || this.listeners.set(e, /* @__PURE__ */ new Set()), this.listeners.get(e).add(t);
16
+ }
17
+ /** 移除事件监听 */
18
+ off(e, t) {
19
+ const n = this.listeners.get(e);
20
+ n && n.delete(t);
21
+ }
22
+ /** 触发事件 */
23
+ emit(e, t) {
24
+ const n = this.listeners.get(e), i = n ? n.size : 0;
25
+ e === "message:received" && console.log("[EventManager] emit message:received, 监听器数量:", i), n && n.forEach((a) => a(t));
26
+ }
27
+ /** 清除所有事件监听 */
28
+ clear() {
29
+ this.listeners.clear();
30
+ }
31
+ /** 清除指定事件的所有监听 */
32
+ clearEvent(e) {
33
+ this.listeners.delete(e);
34
+ }
35
+ }
36
+ let v = 2;
37
+ function j(s) {
38
+ v = s;
39
+ }
40
+ function le() {
41
+ return v;
42
+ }
43
+ const r = {
44
+ verbose: (s, ...e) => v >= 4 && console.log(`[${s}]`, ...e),
45
+ debug: (s, ...e) => v >= 3 && console.log(`[${s}]`, ...e),
46
+ info: (s, ...e) => v >= 2 && console.log(`[${s}]`, ...e),
47
+ warn: (s, ...e) => v >= 1 && console.warn(`[${s}]`, ...e),
48
+ error: (s, ...e) => v >= 0 && console.error(`[${s}]`, ...e)
49
+ };
50
+ class V {
51
+ constructor(e, t, n) {
52
+ this._currentAttempt = 0, this._isRetrying = !1, this.retryTimer = null, this.stopped = !1, this.eventListeners = /* @__PURE__ */ new Map(), this.maxRetries = e.maxRetries ?? 10, this.baseDelay = e.baseDelay ?? 1e3, this.maxDelay = e.maxDelay ?? 3e4, this.jitterFactor = e.jitterFactor ?? 0.2, this.useExponentialBackoff = e.useExponentialBackoff ?? !0, this.onReconnect = t, this.onFinalFail = n;
53
+ }
54
+ /** 当前重试次数 */
55
+ get currentAttempt() {
56
+ return this._currentAttempt;
57
+ }
58
+ /** 是否正在重连中 */
59
+ get isRetrying() {
60
+ return this._isRetrying;
61
+ }
62
+ /** 注册事件监听 */
63
+ on(e, t) {
64
+ this.eventListeners.has(e) || this.eventListeners.set(e, /* @__PURE__ */ new Set()), this.eventListeners.get(e).add(t);
65
+ }
66
+ /** 移除事件监听 */
67
+ off(e, t) {
68
+ const n = this.eventListeners.get(e);
69
+ n && n.delete(t);
70
+ }
71
+ /** 触发事件 */
72
+ emit(e, t) {
73
+ const n = this.eventListeners.get(e);
74
+ n && n.forEach((i) => i(t));
75
+ }
76
+ /**
77
+ * 计算下次重试延迟
78
+ * 指数退避 + 抖动算法
79
+ */
80
+ calculateDelay(e) {
81
+ let t;
82
+ this.useExponentialBackoff ? t = this.baseDelay * Math.pow(2, e) : t = this.baseDelay * (e + 1), t = Math.min(t, this.maxDelay);
83
+ const n = t * this.jitterFactor * (Math.random() * 2 - 1);
84
+ return t = t + n, Math.max(0, Math.floor(t));
85
+ }
86
+ /** 开始重连流程 */
87
+ start() {
88
+ if (this._isRetrying) {
89
+ r.warn("ReconnectManager", "重连已在进行中,忽略重复调用");
90
+ return;
91
+ }
92
+ this._isRetrying = !0, this.stopped = !1, this._currentAttempt = 0, r.info("ReconnectManager", `开始重连流程,最大重试次数: ${this.maxRetries}`), this.scheduleNextRetry();
93
+ }
94
+ /** 安排下一次重试 */
95
+ scheduleNextRetry() {
96
+ if (this.stopped) return;
97
+ if (this._currentAttempt >= this.maxRetries) {
98
+ r.error("ReconnectManager", `已达到最大重试次数 ${this.maxRetries},重连失败`), this._isRetrying = !1, this.emit("failed", this._currentAttempt), this.onFinalFail();
99
+ return;
100
+ }
101
+ const e = this.calculateDelay(this._currentAttempt), t = this._currentAttempt + 1;
102
+ r.info("ReconnectManager", `第 ${t} 次重连将在 ${e}ms 后执行`), this.emit("reconnecting", t), this.retryTimer = setTimeout(async () => {
103
+ if (!this.stopped) {
104
+ this._currentAttempt = t;
105
+ try {
106
+ await this.onReconnect(t), r.info("ReconnectManager", `第 ${t} 次重连成功`), this._isRetrying = !1, this.emit("reconnected", t);
107
+ } catch (n) {
108
+ r.warn("ReconnectManager", `第 ${t} 次重连失败:`, n), this.scheduleNextRetry();
109
+ }
110
+ }
111
+ }, e);
112
+ }
113
+ /** 停止重连(主动关闭时调用) */
114
+ stop() {
115
+ this.stopped = !0, this._isRetrying = !1, this.retryTimer && (clearTimeout(this.retryTimer), this.retryTimer = null), r.debug("ReconnectManager", "重连已停止");
116
+ }
117
+ /** 重置重试计数(连接成功后调用) */
118
+ reset() {
119
+ this._currentAttempt = 0, this._isRetrying = !1, this.stopped = !1, this.retryTimer && (clearTimeout(this.retryTimer), this.retryTimer = null), r.debug("ReconnectManager", "重连计数器已重置");
120
+ }
121
+ }
122
+ var S = /* @__PURE__ */ ((s) => (s.Closed = "Closed", s.Open = "Open", s.HalfOpen = "HalfOpen", s))(S || {});
123
+ class G {
124
+ constructor(e = {}) {
125
+ this._state = "Closed", this.failureCount = 0, this.successCount = 0, this.openTimestamp = 0, this.stateChangeListeners = /* @__PURE__ */ new Set(), this.failureThreshold = e.failureThreshold ?? 10, this.resetTimeout = e.resetTimeout ?? 6e4, this.successThreshold = e.successThreshold ?? 3;
126
+ }
127
+ /** 当前熔断器状态 */
128
+ get state() {
129
+ return this._state === "Open" && Date.now() - this.openTimestamp >= this.resetTimeout && this.transitionTo(
130
+ "HalfOpen"
131
+ /* HalfOpen */
132
+ ), this._state;
133
+ }
134
+ /** 注册状态变化监听 */
135
+ onStateChanged(e) {
136
+ this.stateChangeListeners.add(e);
137
+ }
138
+ /** 移除状态变化监听 */
139
+ offStateChanged(e) {
140
+ this.stateChangeListeners.delete(e);
141
+ }
142
+ /** 切换状态 */
143
+ transitionTo(e) {
144
+ if (this._state === e) return;
145
+ const t = this._state;
146
+ this._state = e, r.info("CircuitBreaker", `状态变化: ${t} -> ${e}`), e === "HalfOpen" && (this.successCount = 0), e === "Closed" && (this.failureCount = 0), e === "Open" && (this.openTimestamp = Date.now()), this.stateChangeListeners.forEach((n) => n({ from: t, to: e }));
147
+ }
148
+ /** 记录成功 */
149
+ recordSuccess() {
150
+ const e = this.state;
151
+ e === "HalfOpen" ? (this.successCount++, r.debug("CircuitBreaker", `半开状态记录成功: ${this.successCount}/${this.successThreshold}`), this.successCount >= this.successThreshold && this.transitionTo(
152
+ "Closed"
153
+ /* Closed */
154
+ )) : e === "Closed" && this.failureCount > 0 && (this.failureCount = 0, r.debug("CircuitBreaker", "关闭状态记录成功,失败计数已重置"));
155
+ }
156
+ /** 记录失败,达到阈值则打开 */
157
+ recordFailure() {
158
+ const e = this.state;
159
+ e === "HalfOpen" ? (r.warn("CircuitBreaker", "半开状态记录失败,重新打开熔断器"), this.transitionTo(
160
+ "Open"
161
+ /* Open */
162
+ )) : e === "Closed" && (this.failureCount++, r.debug("CircuitBreaker", `关闭状态记录失败: ${this.failureCount}/${this.failureThreshold}`), this.failureCount >= this.failureThreshold && (r.warn("CircuitBreaker", `失败次数达到阈值 ${this.failureThreshold},熔断器打开`), this.transitionTo(
163
+ "Open"
164
+ /* Open */
165
+ )));
166
+ }
167
+ /** 是否可以尝试(半开时探测) */
168
+ canAttempt() {
169
+ return this.state !== "Open";
170
+ }
171
+ /** 重置为关闭状态 */
172
+ reset() {
173
+ this.failureCount = 0, this.successCount = 0, this.openTimestamp = 0, this._state !== "Closed" ? this.transitionTo(
174
+ "Closed"
175
+ /* Closed */
176
+ ) : r.debug("CircuitBreaker", "熔断器已重置");
177
+ }
178
+ }
179
+ const T = "sb_client_id";
180
+ function w() {
181
+ const s = Date.now().toString(36), e = Math.random().toString(36).substring(2, 15), t = Math.random().toString(36).substring(2, 15);
182
+ return `cid_${s}_${e}${t}`;
183
+ }
184
+ function K() {
185
+ try {
186
+ let s = localStorage.getItem(T);
187
+ return s || (s = w(), localStorage.setItem(T, s)), s;
188
+ } catch {
189
+ return w();
190
+ }
191
+ }
192
+ function ue() {
193
+ const s = w();
194
+ try {
195
+ localStorage.setItem(T, s);
196
+ } catch {
197
+ }
198
+ return s;
199
+ }
200
+ function de() {
201
+ try {
202
+ return !!localStorage.getItem(T);
203
+ } catch {
204
+ return !1;
205
+ }
206
+ }
207
+ function h(s, e) {
208
+ try {
209
+ return s() ?? e;
210
+ } catch {
211
+ return e;
212
+ }
213
+ }
214
+ function W() {
215
+ const s = navigator, e = s.connection || s.mozConnection || s.webkitConnection;
216
+ return {
217
+ userAgent: h(() => navigator.userAgent, ""),
218
+ platform: h(() => navigator.platform, ""),
219
+ screenWidth: h(() => screen.width, 0),
220
+ screenHeight: h(() => screen.height, 0),
221
+ viewportWidth: h(() => window.innerWidth, 0),
222
+ viewportHeight: h(() => window.innerHeight, 0),
223
+ devicePixelRatio: h(() => window.devicePixelRatio, 1),
224
+ colorDepth: h(() => screen.colorDepth, 0),
225
+ connectionType: h(() => e == null ? void 0 : e.type),
226
+ effectiveType: h(() => e == null ? void 0 : e.effectiveType),
227
+ downlink: h(() => e == null ? void 0 : e.downlink),
228
+ rtt: h(() => e == null ? void 0 : e.rtt),
229
+ language: h(() => navigator.language, ""),
230
+ languages: h(() => [...navigator.languages || []], []),
231
+ timezone: h(() => Intl.DateTimeFormat().resolvedOptions().timeZone, ""),
232
+ timezoneOffset: h(() => (/* @__PURE__ */ new Date()).getTimezoneOffset(), 0),
233
+ currentUrl: h(() => window.location.href, ""),
234
+ pageTitle: h(() => document.title, ""),
235
+ referrer: h(() => document.referrer, ""),
236
+ touchSupport: h(() => "ontouchstart" in window || navigator.maxTouchPoints > 0, !1),
237
+ maxTouchPoints: h(() => navigator.maxTouchPoints, 0),
238
+ deviceMemory: h(() => s.deviceMemory),
239
+ hardwareConcurrency: h(() => navigator.hardwareConcurrency),
240
+ cookieEnabled: h(() => navigator.cookieEnabled, !0),
241
+ prefersDarkMode: h(() => window.matchMedia("(prefers-color-scheme: dark)").matches, !1),
242
+ online: h(() => navigator.onLine, !0)
243
+ };
244
+ }
245
+ class z {
246
+ constructor(e) {
247
+ this.baseURL = e;
248
+ }
249
+ /** 获取 Token(完整参数版) */
250
+ async getToken(e) {
251
+ const t = new FormData();
252
+ t.append("platformKey", e.platformKey), e.deviceId && t.append("deviceId", e.deviceId), e.userId && t.append("userId", e.userId), e.nickname && t.append("nickname", e.nickname), e.avatar && t.append("avatar", e.avatar), e.metadata && t.append("metadata", e.metadata), t.append("platformType", (e.platformType ?? 0).toString()), e.source && t.append("source", e.source);
253
+ const n = e.clientId || K();
254
+ t.append("clientId", n);
255
+ try {
256
+ const o = e.deviceInfo || JSON.stringify(W());
257
+ t.append("deviceInfo", o);
258
+ } catch {
259
+ }
260
+ const i = await fetch(`${this.baseURL}/api/ChatAuth/GetToken`, {
261
+ method: "POST",
262
+ body: t
263
+ });
264
+ if (!i.ok)
265
+ throw new Error(`HTTP 错误: ${i.status}`);
266
+ const a = await i.json();
267
+ if (a.code !== 0)
268
+ throw new Error(a.message || "获取 Token 失败");
269
+ return a.data;
270
+ }
271
+ /** 获取 Token(简化参数版) */
272
+ async getTokenSimple(e, t, n = 0, i) {
273
+ return this.getToken({
274
+ platformKey: e,
275
+ userId: t,
276
+ platformType: n,
277
+ nickname: i == null ? void 0 : i.nickname,
278
+ avatar: i == null ? void 0 : i.avatar,
279
+ deviceId: i == null ? void 0 : i.deviceId,
280
+ metadata: i == null ? void 0 : i.metadata,
281
+ source: i == null ? void 0 : i.source
282
+ });
283
+ }
284
+ /** 更新用户信息 */
285
+ async updateUserInfo(e, t, n, i, a) {
286
+ const o = new FormData();
287
+ o.append("PlatformKey", e), o.append("DeviceId", t), n && o.append("UserId", n), i && o.append("Nickname", i), a && o.append("Avatar", a);
288
+ const l = await fetch(`${this.baseURL}/api/ChatAuth/UpdateUserInfo`, {
289
+ method: "POST",
290
+ body: o
291
+ });
292
+ if (!l.ok)
293
+ throw new Error(`HTTP 错误: ${l.status}`);
294
+ const u = await l.json();
295
+ if (u.code !== 0)
296
+ throw new Error(u.message || "更新用户信息失败");
297
+ }
298
+ }
299
+ const p = "chat_auth_data";
300
+ class Y {
301
+ /** 保存认证数据到 localStorage */
302
+ saveAuthData(e) {
303
+ try {
304
+ const t = {
305
+ ...e,
306
+ expireAt: Date.now() + e.expireTime * 1e3
307
+ };
308
+ localStorage.setItem(p, JSON.stringify(t));
309
+ } catch (t) {
310
+ r.warn("Session", "保存认证数据失败:", t);
311
+ }
312
+ }
313
+ /** 从 localStorage 加载认证数据(过期则返回 null) */
314
+ loadAuthData() {
315
+ try {
316
+ const e = localStorage.getItem(p);
317
+ if (!e) return null;
318
+ const t = JSON.parse(e);
319
+ return t.expireAt && t.expireAt - 300 * 1e3 < Date.now() ? (localStorage.removeItem(p), null) : t;
320
+ } catch (e) {
321
+ return r.warn("Session", "加载认证数据失败:", e), null;
322
+ }
323
+ }
324
+ /** 清除认证数据 */
325
+ clearAuthData() {
326
+ localStorage.removeItem(p);
327
+ }
328
+ /** 更新本地存储中的用户资料 */
329
+ updateUserProfile(e) {
330
+ try {
331
+ const t = localStorage.getItem(p);
332
+ if (t) {
333
+ const n = JSON.parse(t);
334
+ n.user && (e.nickname !== void 0 && (n.user.nickname = e.nickname), e.avatar !== void 0 && (n.user.avatar = e.avatar), localStorage.setItem(p, JSON.stringify(n)));
335
+ }
336
+ } catch (t) {
337
+ r.warn("Session", "更新本地存储失败:", t);
338
+ }
339
+ }
340
+ }
341
+ class q {
342
+ constructor(e, t) {
343
+ this.lastLoginRequest = null, this.active = !1, this.baseURL = "", this.chatUserId = "", this.clientId = "", this.platformKey = "", this.onAgentChanged = null, this.authService = e, this.client = t;
344
+ }
345
+ /** 设置上次登录请求参数(用于自动续期) */
346
+ setLastLoginRequest(e) {
347
+ this.lastLoginRequest = e, this.active = !0;
348
+ }
349
+ /** 设置容灾恢复所需的参数 */
350
+ setSessionInfo(e) {
351
+ this.baseURL = e.baseURL, this.chatUserId = e.chatUserId, this.clientId = e.clientId, this.platformKey = e.platformKey;
352
+ }
353
+ /** Token 过期时的自动刷新回调 */
354
+ async refresh() {
355
+ if (!this.lastLoginRequest)
356
+ throw new Error("无法刷新:缺少登录参数");
357
+ r.info("TokenRefresher", "正在自动刷新 Token...");
358
+ try {
359
+ const e = await this.authService.getToken(this.lastLoginRequest);
360
+ await this.client.login(e.userID, e.token), r.info("TokenRefresher", "Token 刷新成功");
361
+ } catch {
362
+ r.warn("TokenRefresher", "标准刷新失败,尝试 RefreshSession 容灾恢复..."), await this.refreshSession();
363
+ }
364
+ }
365
+ /** 容灾恢复:调用 RefreshSession 做完整重置 */
366
+ async refreshSession() {
367
+ if (!this.baseURL || !this.chatUserId)
368
+ throw new Error("无法容灾恢复:缺少 session 参数");
369
+ r.info("TokenRefresher", "正在调用 RefreshSession...");
370
+ const e = await fetch(`${this.baseURL}/api/ChatAuth/RefreshSession`, {
371
+ method: "POST",
372
+ headers: { "Content-Type": "application/json" },
373
+ body: JSON.stringify({
374
+ chatUserId: this.chatUserId,
375
+ clientId: this.clientId,
376
+ platformKey: this.platformKey
377
+ })
378
+ });
379
+ if (!e.ok)
380
+ throw new Error(`RefreshSession HTTP 错误: ${e.status}`);
381
+ const t = await e.json();
382
+ if (t.code !== 0)
383
+ throw new Error(t.message || "RefreshSession 失败");
384
+ const n = t.data;
385
+ return await this.client.login(this.chatUserId, n.token), r.info("TokenRefresher", "RefreshSession 成功"), n.agentUserId && this.onAgentChanged && this.onAgentChanged(n.agentUserId), n;
386
+ }
387
+ /** 是否已激活 */
388
+ isActive() {
389
+ return this.active;
390
+ }
391
+ /** 停止自动续期 */
392
+ stop() {
393
+ this.lastLoginRequest = null, this.active = !1, this.onAgentChanged = null;
394
+ }
395
+ }
396
+ const ge = {
397
+ Sending: 1,
398
+ Sent: 2,
399
+ /** @deprecated 请使用 MessageStatus.Sent,值相同,向后兼容 */
400
+ Success: 2,
401
+ Delivered: 3,
402
+ Read: 4,
403
+ Failed: 5,
404
+ Withdrawn: 6,
405
+ Edited: 7,
406
+ Deleted: 8
407
+ }, c = {
408
+ // —— 数字类型 ——
409
+ TextMessage: 101,
410
+ PictureMessage: 102,
411
+ VoiceMessage: 103,
412
+ VideoMessage: 104,
413
+ FileMessage: 105,
414
+ AtTextMessage: 106,
415
+ MergeMessage: 107,
416
+ CardMessage: 108,
417
+ LocationMessage: 109,
418
+ CustomMessage: 110,
419
+ TypingMessage: 113,
420
+ QuoteMessage: 114,
421
+ FaceMessage: 115,
422
+ // —— 命名空间字符串类型 ——
423
+ // 系统消息
424
+ SystemNotice: "system.notice",
425
+ SystemMember: "system.member",
426
+ SystemMute: "system.mute",
427
+ // 通话消息
428
+ CallVoice: "call.voice",
429
+ CallVideo: "call.video",
430
+ CallMissed: "call.missed",
431
+ // 互动消息
432
+ InteractPoke: "interact.poke",
433
+ InteractShake: "interact.shake",
434
+ InteractFlash: "interact.flash"
435
+ };
436
+ class R {
437
+ /** 透传数据(SignalR 自动反序列化 JSON 为对象) */
438
+ static parse(e) {
439
+ if (typeof e == "string")
440
+ try {
441
+ return JSON.parse(e);
442
+ } catch {
443
+ return e;
444
+ }
445
+ return e;
446
+ }
447
+ }
448
+ function Q(s) {
449
+ if (typeof s == "number") return s;
450
+ switch (s) {
451
+ case "text":
452
+ return c.TextMessage;
453
+ case "image":
454
+ return c.PictureMessage;
455
+ case "voice":
456
+ return c.VoiceMessage;
457
+ case "video":
458
+ return c.VideoMessage;
459
+ case "file":
460
+ return c.FileMessage;
461
+ case "custom":
462
+ return c.CustomMessage;
463
+ case "face":
464
+ return c.FaceMessage;
465
+ case "quote":
466
+ return c.QuoteMessage;
467
+ default:
468
+ return s.startsWith("sys.") ? 900 : s.includes(".") ? s : c.TextMessage;
469
+ }
470
+ }
471
+ function X(s) {
472
+ if (!s) return "";
473
+ try {
474
+ const e = JSON.parse(s);
475
+ return typeof e == "string" ? e : e.content ? e.content : s;
476
+ } catch {
477
+ return s;
478
+ }
479
+ }
480
+ function Z(s) {
481
+ if (s)
482
+ try {
483
+ const e = JSON.parse(s), t = e.url || "", n = e.width || 0, i = e.height || 0, a = e.size || 0, o = e.type || "image/jpeg";
484
+ if (!t) return;
485
+ const l = {
486
+ uuid: "",
487
+ type: o,
488
+ size: a,
489
+ width: n,
490
+ height: i,
491
+ url: t
492
+ };
493
+ return {
494
+ sourcePath: t,
495
+ sourcePicture: { ...l },
496
+ bigPicture: { ...l },
497
+ snapshotPicture: { ...l }
498
+ };
499
+ } catch {
500
+ return;
501
+ }
502
+ }
503
+ function y(s, e, t) {
504
+ if (s[e] !== void 0) return s[e];
505
+ if (s[t] !== void 0) return s[t];
506
+ }
507
+ function m(s) {
508
+ if (!s) return null;
509
+ if (s.sendID !== void 0) return s;
510
+ const e = Q(s.contentType || s.ContentType || "text"), t = s.content ?? s.Content ?? "", n = X(t), i = s.timestamp || s.Timestamp, a = i ? new Date(i).getTime() : Date.now(), o = s.seq ?? s.Seq ?? 0, l = s.id ?? s.Id ?? "", u = s.senderId ?? s.SenderId ?? "", f = String(s.conversationId ?? s.ConversationId ?? ""), C = y(s, "status", "Status") ?? 2, A = y(s, "deliveredAt", "DeliveredAt"), N = y(s, "readAt", "ReadAt"), _ = y(s, "editedAt", "EditedAt"), U = y(s, "previousContent", "PreviousContent"), x = y(s, "isWithdrawn", "IsWithdrawn"), H = typeof e == "number" && e === c.TextMessage ? { content: n } : void 0, $ = typeof e == "number" && e === c.PictureMessage ? Z(t) : void 0, P = typeof e == "number" && e === c.CustomMessage ? { data: t, extension: "", description: "" } : void 0;
511
+ return {
512
+ clientMsgID: l || `seq_${o}`,
513
+ serverMsgID: String(o),
514
+ createTime: a,
515
+ sendTime: a,
516
+ sendID: u,
517
+ recvID: "",
518
+ msgFrom: 1,
519
+ contentType: e,
520
+ senderNickname: "",
521
+ senderFaceUrl: "",
522
+ content: n,
523
+ textElem: H,
524
+ pictureElem: $,
525
+ customElem: P,
526
+ conversationID: f,
527
+ isRead: !1,
528
+ status: C,
529
+ deliveredAt: A,
530
+ readAt: N,
531
+ editedAt: _,
532
+ previousContent: U,
533
+ isWithdrawn: x
534
+ };
535
+ }
536
+ function k(s) {
537
+ if (!s) return null;
538
+ if (s.conversationID !== void 0) return s;
539
+ const e = String(s.id ?? s.Id ?? "");
540
+ return e ? {
541
+ conversationID: e,
542
+ conversationType: (s.type ?? s.Type ?? "direct") === "group" ? 3 : 1,
543
+ userID: "",
544
+ groupID: (s.type ?? s.Type) === "group" ? e : "",
545
+ showName: s.title ?? s.Title ?? "",
546
+ faceURL: "",
547
+ recvMsgOpt: 0,
548
+ unreadCount: s.unreadCount ?? s.UnreadCount ?? 0,
549
+ latestMsg: s.lastMsgPreview ?? s.LastMsgPreview ?? "",
550
+ latestMsgSendTime: 0,
551
+ draftText: "",
552
+ draftTextTime: 0,
553
+ isPinned: !1,
554
+ isPrivateChat: !1
555
+ } : null;
556
+ }
557
+ function O(s) {
558
+ return s ? (Array.isArray(s) ? s : [s]).map(k).filter((t) => t !== null) : [];
559
+ }
560
+ function D(s) {
561
+ if (!s) return null;
562
+ if (s.userID !== void 0) return s;
563
+ const e = s.userId ?? s.UserId ?? "";
564
+ return e ? {
565
+ userID: e,
566
+ nickname: s.nickname ?? s.Nickname ?? "",
567
+ faceURL: s.avatar ?? s.Avatar ?? "",
568
+ createTime: 0,
569
+ ex: ""
570
+ } : null;
571
+ }
572
+ const g = {
573
+ SessionInit: "session_init",
574
+ VisitorOnline: "visitor_online",
575
+ VisitorOffline: "visitor_offline",
576
+ UserInfoUpdated: "user_info_updated",
577
+ MessageRead: "message_read"
578
+ }, E = Object.values(g), b = {
579
+ [g.VisitorOnline]: "客户已上线",
580
+ [g.VisitorOffline]: "客户已离线",
581
+ [g.UserInfoUpdated]: "客户更新用户信息",
582
+ [g.SessionInit]: "会话已建立",
583
+ [g.MessageRead]: "对方已读"
584
+ };
585
+ function L(s) {
586
+ var e;
587
+ if (Number(s.contentType) !== c.CustomMessage || !((e = s.customElem) != null && e.data)) return !1;
588
+ try {
589
+ const t = JSON.parse(s.customElem.data);
590
+ return E.includes(t.type);
591
+ } catch {
592
+ return !1;
593
+ }
594
+ }
595
+ function M(s) {
596
+ var e;
597
+ if (Number(s.contentType) !== c.CustomMessage || !((e = s.customElem) != null && e.data)) return null;
598
+ try {
599
+ const t = JSON.parse(s.customElem.data);
600
+ return E.includes(t.type) ? t.type : null;
601
+ } catch {
602
+ return null;
603
+ }
604
+ }
605
+ function fe(s) {
606
+ const e = M(s);
607
+ return e && b[e] || "";
608
+ }
609
+ function ee(s) {
610
+ const e = M(s);
611
+ return e !== null && e !== g.SessionInit && e !== g.MessageRead;
612
+ }
613
+ function me(s) {
614
+ var i;
615
+ const e = M(s);
616
+ if (e === g.MessageRead) return "";
617
+ const t = e && b[e] || "";
618
+ if (t) return `[${t}]`;
619
+ if (e) return "";
620
+ const n = Number(s.contentType);
621
+ if (n === c.TextMessage) {
622
+ if ((i = s.textElem) != null && i.content) return s.textElem.content;
623
+ try {
624
+ return JSON.parse(s.content).content || s.content;
625
+ } catch {
626
+ return s.content || "";
627
+ }
628
+ }
629
+ return n === c.PictureMessage ? "[图片]" : n === c.VoiceMessage ? "[语音]" : n === c.VideoMessage ? "[视频]" : n === c.FileMessage ? "[文件]" : "[消息]";
630
+ }
631
+ const te = 1501, se = 1599;
632
+ function ve(s) {
633
+ const e = Number(s.contentType);
634
+ if (e >= te && e <= se)
635
+ return { showInChat: !1, showInListPreview: !1, previewText: "", systemType: null, systemLabel: "" };
636
+ const t = M(s);
637
+ if (t !== null) {
638
+ const i = b[t] || "";
639
+ return t === g.SessionInit || t === g.MessageRead ? { showInChat: !1, showInListPreview: !1, previewText: "", systemType: t, systemLabel: i } : t === g.UserInfoUpdated ? { showInChat: !0, showInListPreview: !1, previewText: "", systemType: t, systemLabel: i } : {
640
+ showInChat: !0,
641
+ showInListPreview: !0,
642
+ previewText: i ? `[${i}]` : "",
643
+ systemType: t,
644
+ systemLabel: i
645
+ };
646
+ }
647
+ return {
648
+ showInChat: !0,
649
+ showInListPreview: !0,
650
+ previewText: ne(s, e),
651
+ systemType: null,
652
+ systemLabel: ""
653
+ };
654
+ }
655
+ function ne(s, e) {
656
+ var t;
657
+ if (e === c.TextMessage) {
658
+ if ((t = s.textElem) != null && t.content) return s.textElem.content;
659
+ try {
660
+ return JSON.parse(s.content).content || s.content;
661
+ } catch {
662
+ return s.content || "";
663
+ }
664
+ }
665
+ return e === c.PictureMessage ? "[图片]" : e === c.VoiceMessage ? "[语音]" : e === c.VideoMessage ? "[视频]" : e === c.FileMessage ? "[文件]" : (e === c.CustomMessage, "[消息]");
666
+ }
667
+ class ie {
668
+ constructor() {
669
+ this.types = /* @__PURE__ */ new Map();
670
+ }
671
+ /**
672
+ * 注册一种消息类型
673
+ * @param typeName 消息类型名称(如 'custom.card')
674
+ * @param renderer 渲染器
675
+ * @param handler 处理器(可选)
676
+ */
677
+ register(e, t, n = {}) {
678
+ this.types.set(e, { renderer: t, handler: n });
679
+ }
680
+ /**
681
+ * 尝试解析已注册的消息类型
682
+ * @param typeName 消息类型名称
683
+ * @returns 渲染器与处理器,或 null(未注册)
684
+ */
685
+ tryResolve(e) {
686
+ const t = this.types.get(e);
687
+ return t ? { ...t } : null;
688
+ }
689
+ /**
690
+ * 判断某类型是否已注册
691
+ * @param typeName 消息类型名称
692
+ */
693
+ isRegistered(e) {
694
+ return this.types.has(e);
695
+ }
696
+ }
697
+ class re {
698
+ constructor(e) {
699
+ this.client = e, this.types = new ie();
700
+ }
701
+ /** 判断是否为系统消息(静态方法,供外部调用) */
702
+ static isSystemMessage(e) {
703
+ return L(e);
704
+ }
705
+ /** 发送文本消息 */
706
+ async sendText(e, t, n = !1) {
707
+ const i = this.client.getHub(), a = this.client.getConversationId();
708
+ if (console.log("[MessageService] sendText 调用, conversationId:", a, "text:", t), !a)
709
+ throw new Error("会话 ID 为空,无法发送消息。请先通过 setConversationId 设置会话 ID。");
710
+ try {
711
+ const o = JSON.stringify({ content: t }), l = await i.invoke("SendMessage", a, c.TextMessage, o);
712
+ console.log("[MessageService] sendText 服务端返回:", l);
713
+ const u = m(l);
714
+ if (!u)
715
+ throw r.warn("Message", "SendMessage 返回 null,可能是 conversationId 无效"), new Error("消息发送失败:服务端未返回消息对象");
716
+ return console.log("[MessageService] sendText 映射成功, clientMsgID:", u.clientMsgID), u;
717
+ } catch (o) {
718
+ throw console.error("[MessageService] sendText 失败:", o), r.error("Message", "发送消息失败:", o), o;
719
+ }
720
+ }
721
+ /** 发送图片消息 */
722
+ async sendImage(e, t, n, i, a = !1) {
723
+ const o = this.client.getHub(), l = this.client.getConversationId();
724
+ try {
725
+ const u = JSON.stringify({
726
+ type: "image",
727
+ url: n,
728
+ width: i.width,
729
+ height: i.height,
730
+ size: i.size
731
+ }), f = await o.invoke("SendMessage", l, c.PictureMessage, u);
732
+ return m(f) ?? R.parse(f);
733
+ } catch (u) {
734
+ throw r.error("Message", "发送图片失败:", u), u;
735
+ }
736
+ }
737
+ /** 发送自定义消息 */
738
+ async sendCustomMessage(e, t, n, i) {
739
+ const a = this.client.getHub(), o = this.client.getConversationId();
740
+ try {
741
+ const l = JSON.stringify({ data: t, extension: n, description: i }), u = await a.invoke("SendMessage", o, c.CustomMessage, l);
742
+ return m(u) ?? R.parse(u);
743
+ } catch (l) {
744
+ throw r.error("Message", "发送自定义消息失败:", l), l;
745
+ }
746
+ }
747
+ /** 发送系统消息(通用) */
748
+ async sendSystemMessage(e, t) {
749
+ const n = this.client.getHub(), i = this.client.getConversationId();
750
+ try {
751
+ const a = JSON.stringify({ ...t, type: e, timestamp: Date.now() });
752
+ await n.invoke("SendSystemMessage", i, e, a);
753
+ } catch (a) {
754
+ r.error("Message", `发送系统消息(${e})失败:`, a);
755
+ }
756
+ }
757
+ /** 发送用户信息更新系统消息 */
758
+ async sendUserInfoUpdated(e, t, n) {
759
+ await this.sendSystemMessage(g.UserInfoUpdated, { updates: t, previousValues: n });
760
+ }
761
+ /** 发送会话初始化系统消息 */
762
+ async sendSessionInit(e, t) {
763
+ await this.sendSystemMessage(g.SessionInit, t);
764
+ }
765
+ /** 发送访客离线系统消息 */
766
+ async sendVisitorOffline(e, t) {
767
+ await this.sendSystemMessage(g.VisitorOffline, t);
768
+ }
769
+ /** 发送访客上线系统消息 */
770
+ async sendVisitorOnline(e, t) {
771
+ await this.sendSystemMessage(g.VisitorOnline, t);
772
+ }
773
+ // —— 送达 / 已读回执 ——
774
+ /**
775
+ * 上报消息送达(AcknowledgeDelivery)
776
+ * @param conversationId 会话 ID
777
+ * @param seq 消息序号
778
+ */
779
+ async acknowledgeDelivery(e, t) {
780
+ const n = this.client.getHub();
781
+ try {
782
+ await n.invoke("AcknowledgeDelivery", e, t);
783
+ } catch (i) {
784
+ throw r.error("Message", `上报送达失败(conversationId=${e}, seq=${t}):`, i), i;
785
+ }
786
+ }
787
+ /**
788
+ * 上报消息已读(AcknowledgeRead)
789
+ * @param conversationId 会话 ID
790
+ * @param lastReadSeq 最后已读消息序号
791
+ */
792
+ async acknowledgeRead(e, t) {
793
+ const n = this.client.getHub();
794
+ try {
795
+ await n.invoke("AcknowledgeRead", e, t);
796
+ } catch (i) {
797
+ throw r.error("Message", `上报已读失败(conversationId=${e}, lastReadSeq=${t}):`, i), i;
798
+ }
799
+ }
800
+ // —— 消息撤回 / 编辑 / 删除 ——
801
+ /**
802
+ * 撤回消息(WithdrawMessage)
803
+ * @param conversationId 会话 ID
804
+ * @param seq 消息序号
805
+ * @returns 撤回后的消息对象(status=Withdrawn),失败返回 null
806
+ */
807
+ async withdraw(e, t) {
808
+ const n = this.client.getHub();
809
+ try {
810
+ const i = await n.invoke("WithdrawMessage", e, t);
811
+ return m(i);
812
+ } catch (i) {
813
+ throw r.error("Message", `撤回消息失败(conversationId=${e}, seq=${t}):`, i), i;
814
+ }
815
+ }
816
+ /**
817
+ * 编辑消息(EditMessage)
818
+ * @param conversationId 会话 ID
819
+ * @param seq 消息序号
820
+ * @param newContent 新内容
821
+ * @returns 编辑后的消息对象(status=Edited),失败返回 null
822
+ */
823
+ async edit(e, t, n) {
824
+ const i = this.client.getHub();
825
+ try {
826
+ const a = await i.invoke("EditMessage", e, t, n);
827
+ return m(a);
828
+ } catch (a) {
829
+ throw r.error("Message", `编辑消息失败(conversationId=${e}, seq=${t}):`, a), a;
830
+ }
831
+ }
832
+ /**
833
+ * 删除消息(DeleteMessage)
834
+ * @param conversationId 会话 ID
835
+ * @param seq 消息序号
836
+ */
837
+ async deleteMessage(e, t) {
838
+ const n = this.client.getHub();
839
+ try {
840
+ await n.invoke("DeleteMessage", e, t);
841
+ } catch (i) {
842
+ throw r.error("Message", `删除消息失败(conversationId=${e}, seq=${t}):`, i), i;
843
+ }
844
+ }
845
+ }
846
+ class ae {
847
+ constructor(e) {
848
+ this.client = e;
849
+ }
850
+ /** 获取会话列表 */
851
+ async getList(e = 100) {
852
+ const t = this.client.getHub();
853
+ try {
854
+ const n = await t.invoke("GetConversationList", e);
855
+ return O(n);
856
+ } catch (n) {
857
+ return r.error("Conversation", "获取会话列表失败:", n), [];
858
+ }
859
+ }
860
+ /** 根据用户 ID 获取单个会话 */
861
+ async getOne(e, t = 1) {
862
+ const n = this.client.getHub();
863
+ try {
864
+ console.log("[ConversationService] getOne 调用, userID:", e, "sessionType:", t);
865
+ const i = await n.invoke("GetOneConversation", e, t), a = k(i);
866
+ return console.log("[ConversationService] getOne 结果:", a ? { conversationID: a.conversationID, showName: a.showName } : "null"), a;
867
+ } catch (i) {
868
+ return console.error("[ConversationService] getOne 失败:", i), r.error("Conversation", "获取会话失败:", i), null;
869
+ }
870
+ }
871
+ /** 加入会话组(接收该会话的实时消息推送) */
872
+ async joinConversation(e) {
873
+ const t = this.client.getHub();
874
+ try {
875
+ console.log("[ConversationService] joinConversation 调用, conversationId:", e), await t.invoke("JoinConversation", e), console.log("[ConversationService] joinConversation 成功, 已加入 conv:" + e + " 组");
876
+ } catch (n) {
877
+ console.error("[ConversationService] joinConversation 失败:", n), r.error("Conversation", "加入会话组失败:", n);
878
+ }
879
+ }
880
+ /** 离开会话组 */
881
+ async leaveConversation(e) {
882
+ const t = this.client.getHub();
883
+ try {
884
+ await t.invoke("LeaveConversation", e);
885
+ } catch (n) {
886
+ r.error("Conversation", "离开会话组失败:", n);
887
+ }
888
+ }
889
+ /** 获取历史消息 */
890
+ async getHistory(e, t = 20, n = "") {
891
+ const i = this.client.getHub();
892
+ try {
893
+ const a = n && parseInt(n, 10) || 0;
894
+ console.log("[ConversationService] getHistory 调用, conversationID:", e, "beforeId:", a, "count:", t);
895
+ const o = await i.invoke("GetHistory", e, a, t), u = (Array.isArray(o) ? o : []).map((f) => m(f)).filter((f) => f !== null);
896
+ return console.log("[ConversationService] getHistory 返回, 消息数量:", u.length, "(服务端会自动加入 conv:" + e + " 组)"), u.length > 0 ? u : R.parse(o) || [];
897
+ } catch (a) {
898
+ return console.error("[ConversationService] getHistory 失败:", a), r.error("Conversation", "获取历史消息失败:", a), [];
899
+ }
900
+ }
901
+ /** 标记会话消息已读 */
902
+ async markAsRead(e) {
903
+ const t = this.client.getHub();
904
+ try {
905
+ await t.invoke("MarkRead", e, 0), r.debug("Conversation", "标记已读成功:", e);
906
+ } catch (n) {
907
+ r.error("Conversation", "标记已读失败:", n);
908
+ }
909
+ }
910
+ }
911
+ class oe {
912
+ constructor(e) {
913
+ this.baseURL = e;
914
+ }
915
+ /** 上传文件 */
916
+ async uploadFile(e) {
917
+ const t = new FormData();
918
+ t.append("file", e);
919
+ const n = await fetch(`${this.baseURL}/api/File/Upload`, {
920
+ method: "POST",
921
+ body: t
922
+ });
923
+ if (!n.ok)
924
+ throw new Error(`上传失败: HTTP ${n.status}`);
925
+ const i = await n.json();
926
+ if (i.code !== 0)
927
+ throw new Error(i.message || "上传失败");
928
+ return i.data;
929
+ }
930
+ /** 获取图片宽高 */
931
+ getImageDimensions(e) {
932
+ return new Promise((t, n) => {
933
+ const i = new Image();
934
+ i.onload = () => {
935
+ t({ width: i.width, height: i.height }), URL.revokeObjectURL(i.src);
936
+ }, i.onerror = () => {
937
+ n(new Error("无法读取图片尺寸")), URL.revokeObjectURL(i.src);
938
+ }, i.src = URL.createObjectURL(e);
939
+ });
940
+ }
941
+ }
942
+ class ce {
943
+ constructor(e) {
944
+ var t, n, i, a, o, l, u;
945
+ this.hub = null, this.currentUserId = "", this.currentToken = "", this.currentConversationId = "", this.hubListenersRegistered = !1, this.heartbeatTimer = null, this._connectionStatus = d.DISCONNECTED, this._isManualClose = !1, this.config = e, j(e.logLevel ?? 2), this.events = new J(), this.auth = new z(e.baseURL), this.session = new Y(), this.tokenRefresher = new q(this.auth, this), this.message = new re(this), this.conversation = new ae(this), this.upload = new oe(e.baseURL), this.reconnectManager = new V(
946
+ {
947
+ maxRetries: (t = e.reconnect) == null ? void 0 : t.maxRetries,
948
+ baseDelay: (n = e.reconnect) == null ? void 0 : n.baseDelay,
949
+ maxDelay: (i = e.reconnect) == null ? void 0 : i.maxDelay,
950
+ jitterFactor: (a = e.reconnect) == null ? void 0 : a.jitterFactor
951
+ },
952
+ (f) => this.handleReconnectAttempt(f),
953
+ () => this.handleReconnectFinalFail()
954
+ ), this.reconnectManager.on("reconnecting", (f) => {
955
+ this.setConnectionStatus(d.RECONNECTING), this.events.emit("connection:reconnecting", { attempt: f });
956
+ }), this.reconnectManager.on("reconnected", () => {
957
+ this.setConnectionStatus(d.CONNECTED), this.circuitBreaker.recordSuccess();
958
+ }), this.circuitBreaker = new G({
959
+ failureThreshold: (o = e.circuitBreaker) == null ? void 0 : o.failureThreshold,
960
+ resetTimeout: (l = e.circuitBreaker) == null ? void 0 : l.resetTimeout,
961
+ successThreshold: (u = e.circuitBreaker) == null ? void 0 : u.successThreshold
962
+ }), this.circuitBreaker.onStateChanged(({ from: f, to: C }) => {
963
+ r.info("Realtime", `熔断器状态变化: ${f} -> ${C}`), C === S.Open ? (this.events.emit("connection:circuit-open", void 0), this.reconnectManager.isRetrying && this.reconnectManager.stop(), this.setConnectionStatus(d.FAILED)) : C === S.HalfOpen && (this.events.emit("connection:circuit-half-open", void 0), !this._isManualClose && this._connectionStatus !== d.CONNECTED && this.startReconnect());
964
+ });
965
+ }
966
+ /** 获取当前连接状态 */
967
+ get connectionStatus() {
968
+ return this._connectionStatus;
969
+ }
970
+ /** 设置连接状态 */
971
+ setConnectionStatus(e) {
972
+ this._connectionStatus !== e && (this._connectionStatus = e, r.debug("Realtime", `连接状态变化: ${this._connectionStatus} -> ${e}`));
973
+ }
974
+ /** 获取 Hub 连接实例 */
975
+ getHub() {
976
+ if (!this.hub)
977
+ throw new Error("Hub 未初始化,请先调用 login");
978
+ return this.hub;
979
+ }
980
+ /** 获取当前会话 ID */
981
+ getConversationId() {
982
+ return this.currentConversationId;
983
+ }
984
+ /** 设置当前会话 ID(登录后由外部设置) */
985
+ setConversationId(e) {
986
+ this.currentConversationId = e;
987
+ }
988
+ /** 获取配置 */
989
+ getConfig() {
990
+ return this.config;
991
+ }
992
+ /** 动态更新 Realtime 服务器地址(用于接收服务端下发的配置) */
993
+ updateRealtimeAddrs(e) {
994
+ e && (this.config.wsAddr = e), r.debug("Realtime", `地址已更新: wsAddr=${e}`);
995
+ }
996
+ /** 检查是否已连接 */
997
+ async isConnected() {
998
+ return this.hub ? this.hub.state === I.Connected : !1;
999
+ }
1000
+ /** 登录 — 建立 SignalR 连接 */
1001
+ async login(e, t) {
1002
+ if (!this.config.wsAddr)
1003
+ throw new Error("Realtime 服务器地址未配置,请先调用 updateRealtimeAddrs 或在初始化时传入 wsAddr");
1004
+ if (!this.circuitBreaker.canAttempt()) {
1005
+ const i = new Error("熔断器已打开,暂时无法连接,请稍后再试");
1006
+ throw r.error("Realtime", i.message), this.setConnectionStatus(d.FAILED), this.events.emit("connection:failed", i), i;
1007
+ }
1008
+ if (this.setConnectionStatus(d.CONNECTING), this.events.emit("connection:connecting", void 0), this._isManualClose = !1, this.hub && this.hub.state === I.Connected) {
1009
+ if (this.currentUserId === e && this.currentToken === t)
1010
+ return r.debug("Realtime", "已连接且凭证未变,跳过重复登录"), this.setConnectionStatus(d.CONNECTED), this.circuitBreaker.recordSuccess(), !0;
1011
+ r.debug("Realtime", "检测到新凭证,重新连接");
1012
+ try {
1013
+ await this.hub.stop();
1014
+ } catch (i) {
1015
+ r.warn("Realtime", "重新连接前停止失败(忽略):", i);
1016
+ }
1017
+ }
1018
+ this.reconnectManager.isRetrying && this.reconnectManager.stop();
1019
+ const n = this.config.wsAddr.replace(/^ws:\/\//, "http://").replace(/^wss:\/\//, "https://");
1020
+ this.hub = new F().withUrl(n, {
1021
+ accessTokenFactory: () => t
1022
+ }).configureLogging(B.Information).build(), this.currentUserId = e, this.currentToken = t, this.hubListenersRegistered = !1, this.setupHubListeners(this.hub);
1023
+ try {
1024
+ return await this.hub.start(), console.log("[RealtimeClient] SignalR 连接成功, connectionId:", this.hub.connectionId), r.info("Realtime", "SignalR 连接成功"), this.setConnectionStatus(d.CONNECTED), this.circuitBreaker.recordSuccess(), this.reconnectManager.reset(), this.events.emit("connection:success", void 0), this.events.emit("sync:finished", void 0), this.startHeartbeat(), !0;
1025
+ } catch (i) {
1026
+ throw r.error("Realtime", "SignalR 连接失败:", i), this.circuitBreaker.recordFailure(), this.events.emit("connection:failed", i), this.events.emit("sync:failed", i), this.circuitBreaker.canAttempt() ? this.startReconnect() : this.setConnectionStatus(d.FAILED), i;
1027
+ }
1028
+ }
1029
+ /** 登出 */
1030
+ async logout() {
1031
+ if (this.currentUserId) {
1032
+ this._isManualClose = !0, this.stopHeartbeat(), this.reconnectManager.stop();
1033
+ try {
1034
+ this.hub && await this.hub.stop(), r.info("Realtime", "登出成功");
1035
+ } catch (e) {
1036
+ r.error("Realtime", "登出失败:", e);
1037
+ } finally {
1038
+ this.setConnectionStatus(d.DISCONNECTED), this.currentUserId = "", this.currentToken = "", this.currentConversationId = "";
1039
+ }
1040
+ }
1041
+ }
1042
+ /** 获取当前登录用户 ID */
1043
+ getCurrentUserId() {
1044
+ return this.currentUserId;
1045
+ }
1046
+ /** 启动心跳定时器(间隔从配置读取,默认 15 秒) */
1047
+ startHeartbeat() {
1048
+ this.stopHeartbeat();
1049
+ const e = this.config.heartbeatInterval ?? 15e3;
1050
+ this.heartbeatTimer = setInterval(() => {
1051
+ this.hub && this.hub.state === I.Connected && this.hub.invoke("Ping").catch((t) => {
1052
+ r.warn("Realtime", "心跳发送失败:", t);
1053
+ });
1054
+ }, e);
1055
+ }
1056
+ /** 停止心跳定时器 */
1057
+ stopHeartbeat() {
1058
+ this.heartbeatTimer && (clearInterval(this.heartbeatTimer), this.heartbeatTimer = null);
1059
+ }
1060
+ /** 启动重连流程 */
1061
+ startReconnect() {
1062
+ if (this._isManualClose) {
1063
+ r.debug("Realtime", "主动关闭,不触发重连");
1064
+ return;
1065
+ }
1066
+ if (this.reconnectManager.isRetrying) {
1067
+ r.debug("Realtime", "重连已在进行中");
1068
+ return;
1069
+ }
1070
+ if (!this.circuitBreaker.canAttempt()) {
1071
+ r.warn("Realtime", "熔断器已打开,不启动重连"), this.setConnectionStatus(d.FAILED);
1072
+ return;
1073
+ }
1074
+ r.info("Realtime", "启动重连流程"), this.reconnectManager.start();
1075
+ }
1076
+ /** 执行单次重连尝试(由 ReconnectManager 回调) */
1077
+ async handleReconnectAttempt(e) {
1078
+ if (!this.hub || this._isManualClose)
1079
+ throw new Error("连接已被主动关闭,取消重连");
1080
+ if (!this.circuitBreaker.canAttempt())
1081
+ throw new Error("熔断器已打开,取消重连");
1082
+ r.info("Realtime", `第 ${e} 次重连尝试...`);
1083
+ try {
1084
+ await this.hub.start(), r.info("Realtime", `第 ${e} 次重连成功`), this._isManualClose = !1, this.setConnectionStatus(d.CONNECTED), this.events.emit("connection:success", void 0), this.events.emit("sync:finished", void 0), this.startHeartbeat();
1085
+ } catch (t) {
1086
+ throw this.circuitBreaker.recordFailure(), t;
1087
+ }
1088
+ }
1089
+ /** 重连最终失败回调 */
1090
+ handleReconnectFinalFail() {
1091
+ r.error("Realtime", "重连次数耗尽,连接失败"), this.setConnectionStatus(d.FAILED);
1092
+ const e = new Error("重连次数耗尽,无法建立连接");
1093
+ this.events.emit("connection:failed", e), this.events.emit("sync:failed", e);
1094
+ }
1095
+ /** 获取当前登录用户信息(通过 Hub 查询) */
1096
+ async getUserInfo() {
1097
+ if (!this.hub || this.hub.state !== I.Connected) return null;
1098
+ try {
1099
+ const e = await this.hub.invoke("GetUserInfo", this.currentUserId);
1100
+ return D(typeof e == "string" ? JSON.parse(e) : e);
1101
+ } catch (e) {
1102
+ return r.error("Realtime", "获取用户信息失败:", e), null;
1103
+ }
1104
+ }
1105
+ /** 注册事件监听(快捷方法) */
1106
+ on(e, t) {
1107
+ this.events.on(e, t);
1108
+ }
1109
+ /** 移除事件监听(快捷方法) */
1110
+ off(e, t) {
1111
+ this.events.off(e, t);
1112
+ }
1113
+ /** 销毁客户端实例,清理所有资源 */
1114
+ destroy() {
1115
+ if (this._isManualClose = !0, this.hub)
1116
+ try {
1117
+ this.hub.stop();
1118
+ } catch {
1119
+ }
1120
+ this.stopHeartbeat(), this.reconnectManager.stop(), this.events.clear(), this.tokenRefresher.stop(), this.currentUserId = "", this.currentToken = "", this.currentConversationId = "", this.hubListenersRegistered = !1, this.hub = null, this.setConnectionStatus(d.DISCONNECTED);
1121
+ }
1122
+ /** 注册 Hub 事件监听器(仅注册一次) */
1123
+ setupHubListeners(e) {
1124
+ if (this.hubListenersRegistered) {
1125
+ console.warn("[RealtimeClient] setupHubListeners 跳过: hubListenersRegistered 已为 true,新 hub 实例未注册监听器!");
1126
+ return;
1127
+ }
1128
+ this.hubListenersRegistered = !0, console.log("[RealtimeClient] setupHubListeners 开始注册 hub 事件监听器 (OnMessage, OnReadReceipt, etc.)"), e.onclose((t) => {
1129
+ if (console.log("[RealtimeClient] onclose 触发, error:", (t == null ? void 0 : t.message) || "无", "isManualClose:", this._isManualClose), this.stopHeartbeat(), this._isManualClose) {
1130
+ r.info("Realtime", "连接已主动关闭"), this.setConnectionStatus(d.DISCONNECTED);
1131
+ return;
1132
+ }
1133
+ t ? (r.error("Realtime", "连接异常断开:", t), this.circuitBreaker.recordFailure(), this.events.emit("connection:failed", t), this.circuitBreaker.canAttempt() ? this.startReconnect() : this.setConnectionStatus(d.FAILED)) : (r.info("Realtime", "连接正常关闭"), this.setConnectionStatus(d.DISCONNECTED));
1134
+ }), e.on("OnMessage", (t) => {
1135
+ console.log("[RealtimeClient] OnMessage 收到原始数据:", t);
1136
+ const n = this.parseMessage(t);
1137
+ if (!n) {
1138
+ console.warn("[RealtimeClient] OnMessage 解析消息失败,data:", t);
1139
+ return;
1140
+ }
1141
+ console.log("[RealtimeClient] OnMessage 解析成功:", {
1142
+ clientMsgID: n.clientMsgID,
1143
+ sendID: n.sendID,
1144
+ contentType: n.contentType,
1145
+ content: n.content,
1146
+ conversationID: n.conversationID
1147
+ }), this.events.emit("message:received:raw", n);
1148
+ const i = L(n), a = ee(n);
1149
+ !i || a ? (console.log("[RealtimeClient] 发出 message:received 事件, isSys:", i, "isDisplayable:", a), this.events.emit("message:received", n)) : console.log("[RealtimeClient] 消息被过滤(系统消息且不可显示), contentType:", n.contentType);
1150
+ }), e.on("OnReadReceipt", (t) => {
1151
+ const n = this.parseData(t);
1152
+ this.events.emit("message:read-receipt", Array.isArray(n) ? n : n ? [n] : []);
1153
+ }), e.on("OnDeliveryReceipt", (t) => {
1154
+ const n = this.parseData(t);
1155
+ n && n.conversationId !== void 0 && this.events.emit("message:delivered", n);
1156
+ }), e.on("OnMessageWithdrawn", (t) => {
1157
+ const n = this.parseData(t);
1158
+ if (!n) return;
1159
+ const i = n.conversationId ?? n.ConversationId ?? "", a = n.seq ?? n.Seq ?? 0, o = n.message ? m(n.message) ?? void 0 : void 0, l = { conversationId: i, seq: a, message: o };
1160
+ this.events.emit("message:withdrawn", l);
1161
+ }), e.on("OnMessageEdited", (t) => {
1162
+ const n = this.parseMessage(t);
1163
+ n && this.events.emit("message:edited", n);
1164
+ }), e.on("OnMessageDeleted", (t) => {
1165
+ const n = this.parseData(t);
1166
+ if (!n) return;
1167
+ const i = n.conversationId ?? n.ConversationId ?? "", a = n.seq ?? n.Seq ?? 0, o = { conversationId: i, seq: a };
1168
+ this.events.emit("message:deleted", o);
1169
+ }), e.on("OnConversationChanged", (t) => {
1170
+ const n = Array.isArray(t) ? t : t ? [t] : [], i = O(n);
1171
+ i.length > 0 && this.events.emit("conversation:changed", i);
1172
+ }), e.on("OnKicked", () => {
1173
+ r.warn("Realtime", "被踢下线"), this._isManualClose = !0, this.reconnectManager.stop(), this.stopHeartbeat(), this.setConnectionStatus(d.DISCONNECTED), this.events.emit("kicked:offline", void 0);
1174
+ }), e.on("OnTokenExpired", () => {
1175
+ r.warn("Realtime", "Token 已过期"), this.events.emit("token:expired", void 0);
1176
+ }), e.on("OnNewConversation", (t) => {
1177
+ const n = this.parseData(t);
1178
+ if (!n) return;
1179
+ const i = n.conversationId ?? n.ConversationId ?? "", a = n.visitorName ?? n.VisitorName ?? "";
1180
+ r.info("Realtime", "收到新会话通知:", i, a), this.events.emit("conversation:new", { conversationId: i, visitorName: a });
1181
+ });
1182
+ }
1183
+ /** 解析消息数据(自动映射 C# RealtimeMessage → JS Message) */
1184
+ parseMessage(e) {
1185
+ if (!e) return null;
1186
+ if (typeof e == "string")
1187
+ try {
1188
+ return m(JSON.parse(e));
1189
+ } catch {
1190
+ return null;
1191
+ }
1192
+ return m(e);
1193
+ }
1194
+ /** 通用数据解析 */
1195
+ parseData(e) {
1196
+ if (typeof e == "string")
1197
+ try {
1198
+ return JSON.parse(e);
1199
+ } catch {
1200
+ return e;
1201
+ }
1202
+ return e;
1203
+ }
1204
+ }
1205
+ const pe = {
1206
+ Web: 0,
1207
+ iOS: 1,
1208
+ Android: 2,
1209
+ Windows: 3,
1210
+ MacOS: 4,
1211
+ Linux: 5
1212
+ }, ye = {
1213
+ Anonymous: 0,
1214
+ Authenticated: 1,
1215
+ VIP: 2
1216
+ }, Ce = {
1217
+ Offline: 0,
1218
+ Online: 1,
1219
+ Busy: 2,
1220
+ Away: 3
1221
+ };
1222
+ function Ie(s) {
1223
+ return new ce(s);
1224
+ }
1225
+ export {
1226
+ z as AuthService,
1227
+ ye as ChatUserType,
1228
+ G as CircuitBreaker,
1229
+ S as CircuitState,
1230
+ d as ConnectionStatus,
1231
+ ae as ConversationService,
1232
+ J as EventManager,
1233
+ Ce as KefuOnlineStatus,
1234
+ R as MessageParser,
1235
+ re as MessageService,
1236
+ ge as MessageStatus,
1237
+ c as MessageType,
1238
+ ie as MessageTypeRegistry,
1239
+ pe as PlatformType,
1240
+ ce as RealtimeClient,
1241
+ V as ReconnectManager,
1242
+ E as SYSTEM_MESSAGE_TYPES,
1243
+ Y as SessionManager,
1244
+ g as SystemMsg,
1245
+ q as TokenRefresher,
1246
+ oe as UploadService,
1247
+ ve as classifyMessage,
1248
+ W as collectDeviceInfo,
1249
+ Ie as createRealtimeCore,
1250
+ K as getClientId,
1251
+ le as getLogLevel,
1252
+ me as getMessagePreview,
1253
+ fe as getSystemMessageLabel,
1254
+ M as getSystemMessageType,
1255
+ de as hasClientId,
1256
+ ee as isDisplayableSystemMessage,
1257
+ L as isSystemMessage,
1258
+ r as logger,
1259
+ k as mapToConversation,
1260
+ O as mapToConversationList,
1261
+ m as mapToMessage,
1262
+ D as mapToUserInfo,
1263
+ ue as resetClientId,
1264
+ j as setLogLevel
1265
+ };