feat(userManagement): 接入用户同步到设备,恢复清空设备用户能力

- service 恢复 clearDeviceMembers/syncMembersToDevice,操作中断开立即失败
- syncMembersToDevice 只负责逐个下发,参数由业务层组装
- _doReconnect 增加蓝牙不可用判断
- userManagement 接入真实同步(连接状态实时同步 + 进度弹框)
- equipment 页增加用户管理入口

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
17792275749
2026-08-21 11:08:04 +08:00
co-authored by Claude Opus 4.7
parent db865748f2
commit 9a81092c99
6 changed files with 233 additions and 10 deletions
+1
View File
@@ -7,4 +7,5 @@ export type {
LeFuConfig,
DeviceSetting,
DeviceInfo,
SyncUserData,
} from './types'
+85
View File
@@ -9,6 +9,7 @@ import type {
LockData,
LeFuConfig,
DeviceInfo,
SyncUserData,
} from './types'
/** 顶层加载插件(对齐 demorequirePlugin 必须在文件顶层调用) */
@@ -358,6 +359,84 @@ class LeFuService {
})
}
// ─── 用户同步 ─────────────────────────────
/**
* 清空设备内的用户数据
* 下发清空指令,让设备清除已录入的所有成员。
*/
clearDeviceMembers(): Promise<void> {
return new Promise((resolve, reject) => {
if (!this._activeProtocol) { reject(new Error('设备未连接')); return }
let settled = false
const settle = (err: Error | null) => {
if (settled) return
settled = true
clearTimeout(timer)
this._onDisconnectedCbs.delete(onDisconnect)
err ? reject(err) : resolve()
}
// 操作中设备断开:立即失败,不等超时
const onDisconnect = () => settle(new Error('设备已断开'))
this._onDisconnectedCbs.add(onDisconnect)
const timer = setTimeout(() => settle(new Error('请求超时')), 15000)
this._activeProtocol.codeClearDeviceData('01', (res: number) => {
settle(res === 0 ? null : new Error('请求失败'))
})
})
}
/**
* 同步成员到设备(只负责逐个下发)
* 参数列表由调用方组装好,本方法不做字段转换,onProgress 回调已完成的用户数。
*/
syncMembersToDevice(list: SyncUserData[], onProgress?: (current: number, total: number) => void): Promise<void> {
return new Promise((resolve, reject) => {
if (!this._activeProtocol) { reject(new Error('设备未连接')); return }
if (!list.length) { reject(new Error('无用户可同步')); return }
let settled = false
let index = 0
let syncTimer: number | null = null
const settle = (err: Error | null) => {
if (settled) return
settled = true
if (syncTimer !== null) clearTimeout(syncTimer)
this._onDisconnectedCbs.delete(onDisconnect)
err ? reject(err) : resolve()
}
// 操作中设备断开:立即失败,不等超时
const onDisconnect = () => settle(new Error('设备已断开'))
this._onDisconnectedCbs.add(onDisconnect)
const resetTimer = () => {
if (syncTimer !== null) clearTimeout(syncTimer)
syncTimer = setTimeout(() => settle(new Error('请求超时')), 15000)
}
const syncNext = () => {
if (settled) return
if (index >= list.length) {
settle(null)
return
}
resetTimer()
this._activeProtocol.dataSyncUserInfo(list[index], (res: number) => {
if (settled) return
if (res !== 0) {
settle(new Error('请求失败'))
return
}
index++
onProgress?.(index, list.length)
syncNext()
})
}
syncNext()
})
}
// ─── 私有:保活与重连 ───────────────────────
/** 启动保活心跳:文档建议每 10s 发送一次 keepAlive,防止设备主动断开 */
private _startKeepAlive(): void {
@@ -400,6 +479,12 @@ class LeFuService {
console.log(TAG, `_doReconnect 第 ${this._reconnectCount}`)
this._stopReconnectTimer()
this._reconnectTimer = setTimeout(() => {
// 延时期间蓝牙可能已不可用,重连前再判断一次
if (this._connectState === plugin.BLUE_STATE.UNAVAILABLE) {
console.warn(TAG, '蓝牙不可用,放弃重连')
this._onDisconnectedCbs.forEach(cb => cb())
return
}
plugin.Blue.clearProtocol()
plugin.Blue.createBLEConnection(this._lastRawDevice!)
}, 2000)
+24
View File
@@ -88,3 +88,27 @@ export interface DeviceInfo {
serialNumber: string
[key: string]: any
}
/** 下发到设备的单个用户数据(dataSyncUserInfo 参数,业务层组装后传入) */
export interface SyncUserData {
/** 主用户 ID */
userID: string
/** 用户名 */
userName: string
/** 成员 ID,主用户为空 */
memberID: string
/** 年龄 */
age: number
/** 性别:1=男,0=女 */
gender: number
/** 身高(cm) */
height: number
/** 是否运动员模式 */
isAthleteMode: number
/** 设备内索引 */
deviceHeaderIndex: number
currentWeight: string
targetWeight: string
idealWeight: string
recentData: any[]
}