oa_app仓库初始化

This commit is contained in:
2026-09-23 16:52:48 +08:00
commit 65c5e9f66f
82 changed files with 6490 additions and 0 deletions
+252
View File
@@ -0,0 +1,252 @@
const ImType = {
login: 0,
keepAlive: 1,
commonData: 2,
logout: 3,
recived: 4,
responseLogin: 50,
responseKeepAlive: 51,
kickout: 54
}
export const ImTypeu = {
text: 1,
image: 2,
file: 3,
card: 4,
recall: 5,
read: 6,
call: 8,
group: 9
}
function frame(partial) {
return {
bridge: false,
type: 0,
dataContent: null,
from: '-1',
to: '-1',
fp: null,
QoS: false,
typeu: -1,
sm: Date.now(),
...partial
}
}
function uuid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = (Math.random() * 16) | 0
const v = c === 'x' ? r : (r & 0x3) | 0x8
return v.toString(16)
})
}
class ImClient {
constructor() {
this.userId = ''
this.token = ''
this.url = ''
this._closed = true
this._tries = 0
this._alive = 0
this._reconnect = 0
this._handlers = {}
this._bound = false
this._opened = false
this._ready = false
this._queue = []
}
on(event, fn) {
this._handlers[event] = fn
}
emit(event, payload) {
const fn = this._handlers[event]
if (fn) fn(payload)
}
connect({ userId, token, url }) {
this._closed = false
this.userId = userId
this.token = token
this.url = url
this._open()
}
_bindSocket() {
if (this._bound) return
this._bound = true
uni.onSocketOpen(() => {
this._tries = 0
this._opened = true
this._send(frame({
type: ImType.login,
from: String(this.userId || ''),
to: '0',
dataContent: JSON.stringify({
loginUserId: String(this.userId || ''),
loginToken: this.token,
extra: 'hbuilder-app',
firstLoginTime: 0
})
}))
})
uni.onSocketMessage((res) => this._onMessage(res.data))
uni.onSocketClose(() => {
this._opened = false
this._ready = false
this._clearAlive()
if (!this._closed) this._schedule()
})
uni.onSocketError(() => {
if (!this._closed) this._schedule()
})
}
_open() {
this._clear()
this.emit('status', 'reconnecting')
this._bindSocket()
uni.connectSocket({ url: this.url })
}
_onMessage(raw) {
let p
try {
p = typeof raw === 'string' ? JSON.parse(raw) : raw
} catch (_) {
return
}
if (!p || typeof p !== 'object') return
const type = p.type
if (type === ImType.responseLogin) {
let info = {}
try {
info = JSON.parse(p.dataContent || '{}')
} catch (_) {}
if (info.code === 0) {
this._ready = true
this.emit('status', 'connected')
this._keep()
this._flush()
} else {
this.emit('status', 'offline')
this.emit('loginFailed', info)
}
return
}
if (type === ImType.responseKeepAlive) return
if (type === ImType.kickout) {
this._closed = true
this.emit('kick')
try { uni.closeSocket() } catch (_) {}
return
}
if (type === ImType.recived) {
this.emit('ack', p.fp || p.dataContent)
return
}
if (type === ImType.commonData) {
if (p.QoS && p.fp) {
this._send(frame({
type: ImType.recived,
from: this.userId,
to: '0',
dataContent: p.fp,
fp: p.fp
}))
}
let data = {}
try {
const dc = p.dataContent
data = typeof dc === 'string' ? JSON.parse(dc) : (dc || {})
} catch (_) {
data = { text: p.dataContent, kind: 'text' }
}
this.emit('data', { ...p, data, typeu: p.typeu || ImTypeu.text })
}
}
sendData({ to, typeu, payload, qos = true }) {
const fp = uuid()
this._send(frame({
type: ImType.commonData,
from: String(this.userId || ''),
to: String(to || ''),
typeu,
QoS: qos,
fp,
dataContent: JSON.stringify(payload)
}))
return fp
}
logout() {
this._closed = true
try {
this._send(frame({ type: ImType.logout, from: this.userId, to: '0' }))
} catch (_) {}
try { uni.closeSocket() } catch (_) {}
this._clear()
}
_send(p) {
const raw = JSON.stringify(p)
const ctrl = p.type === ImType.login || p.type === ImType.keepAlive || p.type === ImType.logout || p.type === ImType.recived
if (!this._opened || (!this._ready && !ctrl)) {
if (!ctrl) this._queue.push(raw)
return
}
try {
uni.sendSocketMessage({
data: raw,
fail: () => {
if (!ctrl) this._queue.push(raw)
}
})
} catch (_) {
if (!ctrl) this._queue.push(raw)
}
}
_flush() {
const pending = this._queue.splice(0, this._queue.length)
pending.forEach((raw) => {
try { uni.sendSocketMessage({ data: raw }) } catch (_) {}
})
}
_keep() {
this._clearAlive()
this._alive = setInterval(() => {
this._send(frame({ type: ImType.keepAlive, from: this.userId, to: '0', dataContent: '{}' }))
}, 5000)
}
_schedule() {
this.emit('status', 'reconnecting')
const wait = Math.min(15000, 800 * Math.pow(2, this._tries))
this._tries += 1
this._reconnect = setTimeout(() => this._open(), wait)
}
_clearAlive() {
if (this._alive) {
clearInterval(this._alive)
this._alive = 0
}
}
_clear() {
this._clearAlive()
if (this._reconnect) {
clearTimeout(this._reconnect)
this._reconnect = 0
}
}
}
export const imClient = new ImClient()