fix: 功能缺陷修复 — ImSDK login await WebSocket、fetchHistory 支持 GROUP、License 改用 apiRequest、LogQueue 重试上限
这个提交包含在:
父节点
a0c54ae191
当前提交
611f2ba95d
@ -18,6 +18,7 @@ export class ImClient {
|
|||||||
private activeWsUrl: string | null = null
|
private activeWsUrl: string | null = null
|
||||||
private activeToken: string | null = null
|
private activeToken: string | null = null
|
||||||
private activeAppKey: string | null = null
|
private activeAppKey: string | null = null
|
||||||
|
private connectedResolvers: Array<() => void> = []
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly wsUrl?: string,
|
private readonly wsUrl?: string,
|
||||||
@ -25,7 +26,7 @@ export class ImClient {
|
|||||||
private readonly appKey?: string,
|
private readonly appKey?: string,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async connect() {
|
async connect(): Promise<void> {
|
||||||
this.shouldReconnect = true
|
this.shouldReconnect = true
|
||||||
this.activeWsUrl = this.wsUrl ?? getConfig().imWsUrl
|
this.activeWsUrl = this.wsUrl ?? getConfig().imWsUrl
|
||||||
this.activeToken = this.token ?? null
|
this.activeToken = this.token ?? null
|
||||||
@ -42,6 +43,10 @@ export class ImClient {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.openSocket()
|
this.openSocket()
|
||||||
|
// 等待 STOMP CONNECTED 帧
|
||||||
|
await new Promise<void>(resolve => {
|
||||||
|
this.connectedResolvers.push(resolve)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessage(
|
sendMessage(
|
||||||
@ -222,6 +227,8 @@ export class ImClient {
|
|||||||
this.subscribe(`/topic/group/${groupId}`, `group-${groupId}`)
|
this.subscribe(`/topic/group/${groupId}`, `group-${groupId}`)
|
||||||
})
|
})
|
||||||
this.sendSync()
|
this.sendSync()
|
||||||
|
this.connectedResolvers.forEach(resolve => resolve())
|
||||||
|
this.connectedResolvers = []
|
||||||
this.listeners.forEach(listener => listener.onConnected?.())
|
this.listeners.forEach(listener => listener.onConnected?.())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -320,7 +320,7 @@ export const ImSDK = {
|
|||||||
_syncHistoryForAllConversations().catch(() => {})
|
_syncHistoryForAllConversations().catch(() => {})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
void client.connect()
|
await client.connect()
|
||||||
},
|
},
|
||||||
|
|
||||||
async reconnect(): Promise<void> {
|
async reconnect(): Promise<void> {
|
||||||
@ -328,17 +328,18 @@ export const ImSDK = {
|
|||||||
const token = await _getToken()
|
const token = await _getToken()
|
||||||
if (!token) throw new Error('[ImSDK] No active session — call login() first.')
|
if (!token) throw new Error('[ImSDK] No active session — call login() first.')
|
||||||
client = new ImClient(config.imWsUrl, token, config.appKey)
|
client = new ImClient(config.imWsUrl, token, config.appKey)
|
||||||
void client.connect()
|
await client.connect()
|
||||||
},
|
},
|
||||||
|
|
||||||
async fetchHistory(
|
async fetchHistory(
|
||||||
toId: string,
|
toId: string,
|
||||||
page = 0,
|
page = 0,
|
||||||
size = 20,
|
size = 20,
|
||||||
|
chatType: ChatType = 'SINGLE',
|
||||||
): Promise<ImMessage[]> {
|
): Promise<ImMessage[]> {
|
||||||
const config = getConfig()
|
const config = getConfig()
|
||||||
if (ImDatabase.isInitialized() && page === 0 && _currentUserId) {
|
if (ImDatabase.isInitialized() && page === 0 && _currentUserId) {
|
||||||
const local = await ImDatabase.getMessages(config.appKey, toId, 'SINGLE', _currentUserId, size)
|
const local = await ImDatabase.getMessages(config.appKey, toId, chatType, _currentUserId, size)
|
||||||
if (local.length > 0) {
|
if (local.length > 0) {
|
||||||
return local.map(model => ({
|
return local.map(model => ({
|
||||||
id: model.serverId,
|
id: model.serverId,
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { Platform } from 'react-native'
|
import { Platform } from 'react-native'
|
||||||
import { getDeviceId as getCommonDeviceId, getDeviceInfo, awaitInitialization, getConfig, getUserId } from '@xuqm/rn-common'
|
import { getDeviceId as getCommonDeviceId, getDeviceInfo, awaitInitialization, getConfig, getUserId, apiRequest } from '@xuqm/rn-common'
|
||||||
import * as store from './store'
|
import * as store from './store'
|
||||||
import type { LicenseResult, LicenseStatus, LicenseUserInfo, RegisterRequest, RegisterResponse, VerifyRequest, VerifyResponse } from './models'
|
import type { LicenseResult, LicenseStatus, LicenseUserInfo, RegisterRequest, RegisterResponse, VerifyRequest, VerifyResponse } from './models'
|
||||||
|
|
||||||
@ -101,15 +101,5 @@ async function _persistStatus(status: string): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function _post<T>(url: string, body: unknown): Promise<T> {
|
async function _post<T>(url: string, body: unknown): Promise<T> {
|
||||||
const res = await fetch(url, {
|
return apiRequest<T>(url, { method: 'POST', body })
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify(body),
|
|
||||||
})
|
|
||||||
if (!res.ok) {
|
|
||||||
const err = await res.json().catch(() => ({ message: res.statusText })) as { message?: string }
|
|
||||||
throw new Error(err.message ?? `HTTP ${res.status}`)
|
|
||||||
}
|
|
||||||
const json = await res.json() as { data?: T }
|
|
||||||
return (json.data ?? json) as T
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,9 +5,11 @@ const QUEUE_KEY = '@xuqm_log:queue'
|
|||||||
const BATCH_SIZE = 30
|
const BATCH_SIZE = 30
|
||||||
const FLUSH_INTERVAL_MS = 10_000 // 10 seconds
|
const FLUSH_INTERVAL_MS = 10_000 // 10 seconds
|
||||||
const MAX_QUEUE_SIZE = 500
|
const MAX_QUEUE_SIZE = 500
|
||||||
|
const MAX_RETRY = 3
|
||||||
|
|
||||||
export class LogQueue {
|
export class LogQueue {
|
||||||
private flushTimer: ReturnType<typeof setInterval> | null = null
|
private flushTimer: ReturnType<typeof setInterval> | null = null
|
||||||
|
private retryCount = 0
|
||||||
|
|
||||||
constructor(private cfg: { logApiUrl: string; appKey: string }) {
|
constructor(private cfg: { logApiUrl: string; appKey: string }) {
|
||||||
this.flushTimer = setInterval(() => {
|
this.flushTimer = setInterval(() => {
|
||||||
@ -35,11 +37,15 @@ export class LogQueue {
|
|||||||
try {
|
try {
|
||||||
if (issues.length > 0) await this._post('/log/v1/issues/batch', issues)
|
if (issues.length > 0) await this._post('/log/v1/issues/batch', issues)
|
||||||
if (events.length > 0) await this._post('/log/v1/events/batch', events)
|
if (events.length > 0) await this._post('/log/v1/events/batch', events)
|
||||||
|
this.retryCount = 0
|
||||||
} catch {
|
} catch {
|
||||||
// On failure, push batch back to front of queue (retry once on next flush)
|
this.retryCount++
|
||||||
|
if (this.retryCount < MAX_RETRY) {
|
||||||
const current = await this._read()
|
const current = await this._read()
|
||||||
await this._write([...batch, ...current])
|
await this._write([...batch, ...current])
|
||||||
}
|
}
|
||||||
|
// 超过重试次数,丢弃该批次
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户