fix(ble): createBLEConnection 已连接报错按幂等成功处理

Android 报 createBLEConnection:fail:already connect(iOS 为 already
connected)出现在从配网页返回后再次点击网络配置等重复连接场景,
连接实际已存在,不应按失败弹「设备连接失败」。bleCore.connect 捕获
该 errMsg 视为成功并进入 CONNECTED;其他错误原样抛出。新增单测
覆盖 Android/iOS 两种文案与普通失败不吞三条分支。
This commit is contained in:
ozh 2026-10-15 10:27:26 +08:00
parent fda1f144fc
commit ccb088ba9c
2 changed files with 69 additions and 1 deletions

View File

@ -291,7 +291,16 @@ export function createBleCore(options = {}) {
const runConnect = async () => { const runConnect = async () => {
setStage(BLE_STAGE.CONNECTING); setStage(BLE_STAGE.CONNECTING);
try {
await promisifyApi(platform, 'createBLEConnection', { deviceId, timeout }); await promisifyApi(platform, 'createBLEConnection', { deviceId, timeout });
} catch (error) {
// Android 报 already connect / iOS 报 already connected:
// 目标连接已存在(如配网页返回后再次发起),按幂等成功处理
const msg = String(error?.errMsg || '');
if (!/already\s*connect/i.test(msg)) {
throw error;
}
}
patchState({ connected: true, deviceId }); patchState({ connected: true, deviceId });
setStage(BLE_STAGE.CONNECTED); setStage(BLE_STAGE.CONNECTED);
return true; return true;

View File

@ -0,0 +1,59 @@
import { describe, it, expect, vi } from 'vitest'
// 纯 JS 资产未类型化,关闭该文件类型检查
// @ts-ignore
import { createBleCore } from '@/ble/core/bleCore'
// bleCore → adapter → @tarojs/taro 在 node 环境会触发 dom-external 的
// ENABLE_INNER_HTML 编译期常量未定义;测试直接 mock 掉该模块
vi.mock('@tarojs/taro', () => ({}))
vi.mock('@/ble/adapter', () => ({ taroPlatform: {} }))
// platform mock:按用例切换 createBLEConnection 的 fail/success 行为
// @ts-ignore 无类型平台对象,与 bleCore 注入签名一致
function makePlatform({ connectFail }: { connectFail?: { errMsg?: string; errCode?: number } }) {
return {
openBluetoothAdapter: (opt: { success?: (r: unknown) => void }) => opt.success?.({}),
closeBluetoothAdapter: (opt: { success?: (r: unknown) => void }) => opt.success?.({}),
onBluetoothAdapterStateChange: vi.fn(),
offBluetoothAdapterStateChange: vi.fn(),
onBLEConnectionStateChange: vi.fn(),
offBLEConnectionStateChange: vi.fn(),
onBLECharacteristicValueChange: vi.fn(),
offBLECharacteristicValueChange: vi.fn(),
onBluetoothDeviceFound: vi.fn(),
offBluetoothDeviceFound: vi.fn(),
createBLEConnection: vi.fn((opt: { success?: (r: unknown) => void; fail?: (e: unknown) => void }) => {
if (connectFail) opt.fail?.(connectFail)
else opt.success?.({})
})
}
}
describe('bleCore.connect already connect 幂等', () => {
it('Android already connect 形态:按已连接成功处理,状态进入 CONNECTED', async () => {
const platform = makePlatform({ connectFail: { errCode: 10003, errMsg: 'createBLEConnection:fail:already connect' } })
const core = createBleCore({ platform, reconnect: { enabled: false } })
const ok = await core.connect('dev-1')
expect(ok).toBe(true)
expect(core.getState().connected).toBe(true)
expect(core.getState().deviceId).toBe('dev-1')
expect(core.getState().stage).toBe('CONNECTED')
})
it('iOS already connected 形态:同样幂等成功', async () => {
const platform = makePlatform({ connectFail: { errMsg: 'createBLEConnection:fail:already connected' } })
const core = createBleCore({ platform, reconnect: { enabled: false } })
await core.connect('dev-2')
expect(core.getState().connected).toBe(true)
})
it('其他连接错误不吞:原样抛出且 connected 保持 false', async () => {
const platform = makePlatform({ connectFail: { errCode: 10012, errMsg: 'createBLEConnection:fail:timeout' } })
const core = createBleCore({ platform, reconnect: { enabled: false } })
await expect(core.connect('dev-3')).rejects.toThrow()
expect(core.getState().connected).toBe(false)
})
})