口才和会议场景
This commit is contained in:
246
docs/backend-fix-tenant-id-issue.md
Normal file
246
docs/backend-fix-tenant-id-issue.md
Normal file
@@ -0,0 +1,246 @@
|
||||
# 后端修复:转文本更新时去掉tenant_id限制
|
||||
|
||||
## ⚡ 快速修复(推荐)
|
||||
|
||||
**最快解决方案**:在Mapper接口中添加自定义更新方法
|
||||
|
||||
1. 在 `AudioManagementSegmentsMapper.java` 中添加:
|
||||
|
||||
```java
|
||||
@InterceptorIgnore(tenantLine = "true")
|
||||
@Update("UPDATE audio_management_segments SET recording_text = #{recordingText}, transcribe_end_time = #{transcribeEndTime} WHERE id = #{id}")
|
||||
int updateTranscriptionWithoutTenant(
|
||||
@Param("id") String id,
|
||||
@Param("recordingText") String recordingText,
|
||||
@Param("transcribeEndTime") LocalDateTime transcribeEndTime
|
||||
);
|
||||
```
|
||||
|
||||
2. 在Controller的转文本方法中,将:
|
||||
```java
|
||||
boolean updateSuccess = audioManagementSegmentsService.update(textUpdateEntity, textUpdateWrapper);
|
||||
```
|
||||
|
||||
替换为:
|
||||
```java
|
||||
int updateCount = audioManagementSegmentsMapper.updateTranscriptionWithoutTenant(
|
||||
segmentId, transcriptionText, LocalDateTime.now()
|
||||
);
|
||||
boolean updateSuccess = updateCount > 0;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 问题描述
|
||||
|
||||
在转文本功能中,更新 `audio_management_segments` 表时,MyBatis-Plus 的多租户插件自动添加了 `AND tenant_id = null` 条件,导致更新失败。
|
||||
|
||||
从日志可以看到:
|
||||
```sql
|
||||
UPDATE audio_management_segments SET recording_text=?, transcribe_end_time=?
|
||||
WHERE (id = ?) AND tenant_id = null
|
||||
```
|
||||
|
||||
## 解决方案
|
||||
|
||||
### 方案1:在Controller层使用baseMapper直接更新(最简单有效)
|
||||
|
||||
在转文本的Controller方法中,直接使用 `baseMapper` 更新,绕过Service层的多租户拦截:
|
||||
|
||||
```java
|
||||
@PostMapping("/transcribe/{id}")
|
||||
@Operation(summary = "转文本", description = "分段音频文件转文本")
|
||||
public ResponseEntity<Map<String, Object>> transcribeSegmentById(
|
||||
@Parameter(description = "分段ID", required = true)
|
||||
@PathVariable String id) {
|
||||
try {
|
||||
// ... 转文本逻辑 ...
|
||||
|
||||
// 更新录音文本 - 直接使用baseMapper,绕过多租户拦截
|
||||
AudioManagementSegments textUpdateEntity = new AudioManagementSegments();
|
||||
textUpdateEntity.setRecordingText(transcriptionText);
|
||||
textUpdateEntity.setTranscribeEndTime(LocalDateTime.now());
|
||||
|
||||
LambdaUpdateWrapper<AudioManagementSegments> textUpdateWrapper = new LambdaUpdateWrapper<>();
|
||||
textUpdateWrapper.eq(AudioManagementSegments::getId, segmentId);
|
||||
|
||||
// 关键:使用 baseMapper 直接更新,并设置忽略多租户拦截
|
||||
// 方法1:使用 @SqlParser(filter = true) - 旧版本MyBatis-Plus
|
||||
// 方法2:直接调用 baseMapper.update,但需要在Mapper方法上添加注解
|
||||
|
||||
// 推荐:在Mapper接口中添加自定义更新方法
|
||||
int updateCount = audioManagementSegmentsMapper.updateTranscriptionWithoutTenant(
|
||||
segmentId,
|
||||
transcriptionText,
|
||||
LocalDateTime.now()
|
||||
);
|
||||
|
||||
boolean updateSuccess = updateCount > 0;
|
||||
|
||||
// ... 其他逻辑 ...
|
||||
} catch (Exception e) {
|
||||
// ... 错误处理 ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 方案2:在Mapper接口中添加自定义更新方法(推荐)
|
||||
|
||||
在 `AudioManagementSegmentsMapper` 中添加自定义更新方法,使用 `@InterceptorIgnore` 注解:
|
||||
|
||||
```java
|
||||
@Mapper
|
||||
public interface AudioManagementSegmentsMapper extends BaseMapper<AudioManagementSegments> {
|
||||
|
||||
/**
|
||||
* 更新转文本结果,忽略多租户拦截
|
||||
*/
|
||||
@InterceptorIgnore(tenantLine = "true")
|
||||
@Update("UPDATE audio_management_segments SET recording_text = #{recordingText}, transcribe_end_time = #{transcribeEndTime} WHERE id = #{id}")
|
||||
int updateTranscriptionWithoutTenant(
|
||||
@Param("id") String id,
|
||||
@Param("recordingText") String recordingText,
|
||||
@Param("transcribeEndTime") LocalDateTime transcribeEndTime
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
然后在Controller中调用:
|
||||
|
||||
```java
|
||||
// 在转文本方法中
|
||||
int updateCount = audioManagementSegmentsMapper.updateTranscriptionWithoutTenant(
|
||||
segmentId,
|
||||
transcriptionText,
|
||||
LocalDateTime.now()
|
||||
);
|
||||
boolean updateSuccess = updateCount > 0;
|
||||
```
|
||||
|
||||
### 方案3:在Service层添加专门的方法并禁用多租户拦截
|
||||
|
||||
在 `AudioManagementSegmentsService` 接口中添加新方法:
|
||||
|
||||
```java
|
||||
public interface AudioManagementSegmentsService extends IService<AudioManagementSegments> {
|
||||
/**
|
||||
* 更新转文本结果,忽略多租户拦截
|
||||
*/
|
||||
boolean updateTranscription(String id, String recordingText, LocalDateTime transcribeEndTime);
|
||||
}
|
||||
```
|
||||
|
||||
在实现类中:
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class AudioManagementSegmentsServiceImpl extends ServiceImpl<AudioManagementSegmentsMapper, AudioManagementSegments>
|
||||
implements AudioManagementSegmentsService {
|
||||
|
||||
@Override
|
||||
@InterceptorIgnore(tenantLine = "true") // 禁用多租户拦截
|
||||
public boolean updateTranscription(String id, String recordingText, LocalDateTime transcribeEndTime) {
|
||||
AudioManagementSegments entity = new AudioManagementSegments();
|
||||
entity.setId(id);
|
||||
entity.setRecordingText(recordingText);
|
||||
entity.setTranscribeEndTime(transcribeEndTime);
|
||||
|
||||
LambdaUpdateWrapper<AudioManagementSegments> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(AudioManagementSegments::getId, id);
|
||||
|
||||
// 注意:这里仍然可能被拦截,所以最好使用Mapper的自定义方法
|
||||
return this.update(entity, wrapper);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**注意**:如果方案3仍然被拦截,请使用方案2(Mapper层的自定义方法)。
|
||||
|
||||
### 方案4:配置多租户插件排除该表(全局方案)
|
||||
|
||||
在 MyBatis-Plus 的多租户配置中,排除 `audio_management_segments` 表:
|
||||
|
||||
```java
|
||||
@Configuration
|
||||
public class MybatisPlusConfig {
|
||||
|
||||
@Bean
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
|
||||
// 多租户插件
|
||||
TenantLineInnerInterceptor tenantInterceptor = new TenantLineInnerInterceptor();
|
||||
tenantInterceptor.setTenantLineHandler(new TenantLineHandler() {
|
||||
@Override
|
||||
public Expression getTenantId() {
|
||||
// 返回租户ID(从上下文获取)
|
||||
String tenantId = TenantContextHolder.getTenantId();
|
||||
return tenantId != null ? new StringValue(tenantId) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTenantIdColumn() {
|
||||
return "tenant_id";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean ignoreTable(String tableName) {
|
||||
// 排除 audio_management_segments 表,不进行多租户拦截
|
||||
return "audio_management_segments".equalsIgnoreCase(tableName);
|
||||
}
|
||||
});
|
||||
|
||||
interceptor.addInnerInterceptor(tenantInterceptor);
|
||||
return interceptor;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**注意**:这个方案会影响所有对 `audio_management_segments` 表的操作,请确保安全性。
|
||||
|
||||
### 方案4:使用自定义SQL(不推荐,但可行)
|
||||
|
||||
如果以上方案都不适用,可以使用自定义SQL,直接执行更新语句:
|
||||
|
||||
```java
|
||||
@Mapper
|
||||
public interface AudioManagementSegmentsMapper extends BaseMapper<AudioManagementSegments> {
|
||||
|
||||
@Update("UPDATE audio_management_segments SET recording_text = #{recordingText}, transcribe_end_time = #{transcribeEndTime} WHERE id = #{id}")
|
||||
int updateTranscription(@Param("id") String id,
|
||||
@Param("recordingText") String recordingText,
|
||||
@Param("transcribeEndTime") LocalDateTime transcribeEndTime);
|
||||
}
|
||||
```
|
||||
|
||||
## 推荐方案(按优先级)
|
||||
|
||||
### 最推荐:方案2 - Mapper层自定义方法
|
||||
|
||||
**强烈推荐使用方案2**,在Mapper接口中添加自定义更新方法,使用 `@InterceptorIgnore` 注解:
|
||||
|
||||
**优点**:
|
||||
1. ✅ 最直接有效,不会被拦截器拦截
|
||||
2. ✅ 只影响转文本的更新操作
|
||||
3. ✅ 不影响其他查询和更新操作
|
||||
4. ✅ 代码清晰,易于维护
|
||||
5. ✅ 性能好,直接执行SQL
|
||||
|
||||
**实现步骤**:
|
||||
1. 在 `AudioManagementSegmentsMapper` 接口中添加方法(见方案2代码)
|
||||
2. 在Controller中调用 `audioManagementSegmentsMapper.updateTranscriptionWithoutTenant()`
|
||||
3. 替换原来的 `service.update()` 调用
|
||||
|
||||
### 备选方案:方案4 - 配置排除表
|
||||
|
||||
如果方案2不适用,可以使用方案4,在配置中排除整个表:
|
||||
- ⚠️ 会影响所有对该表的操作
|
||||
- ⚠️ 需要确保安全性
|
||||
- ✅ 一次配置,全局生效
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 如果使用方案3(全局排除表),会影响所有对该表的操作,需要确保安全性
|
||||
2. 如果使用方案4(自定义SQL),需要手动处理SQL注入防护
|
||||
3. 修改后需要测试其他功能是否受影响
|
||||
|
||||
362
docs/bluetooth-recorder-implementation.md
Normal file
362
docs/bluetooth-recorder-implementation.md
Normal file
@@ -0,0 +1,362 @@
|
||||
# 蓝牙录音设备接入技术方案
|
||||
|
||||
## 一、概述
|
||||
|
||||
本文档详细说明如何在uni-app项目中实现蓝牙录音设备的连接、数据接收和文件处理功能。
|
||||
|
||||
## 二、技术架构
|
||||
|
||||
### 2.1 技术栈
|
||||
- **框架**: uni-app (Vue 3)
|
||||
- **平台**: APP端 (Android/iOS)
|
||||
- **蓝牙协议**: BLE (低功耗蓝牙) / 经典蓝牙
|
||||
- **音频格式**: PCM (可根据设备调整)
|
||||
|
||||
### 2.2 核心组件
|
||||
|
||||
1. **蓝牙工具类** (`common/bluetooth.js`)
|
||||
- 蓝牙适配器管理
|
||||
- 设备搜索与连接
|
||||
- 数据接收与处理
|
||||
- 连接状态管理
|
||||
|
||||
2. **蓝牙录音页面** (`pages/bluetooth-recorder/bluetooth-recorder.vue`)
|
||||
- 设备搜索界面
|
||||
- 连接管理界面
|
||||
- 数据监控界面
|
||||
- 文件保存与上传
|
||||
|
||||
## 三、实现步骤
|
||||
|
||||
### 步骤1: 配置权限
|
||||
|
||||
在 `manifest.json` 中添加蓝牙相关权限:
|
||||
|
||||
**Android权限**:
|
||||
```xml
|
||||
<uses-permission android:name="android.permission.BLUETOOTH"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||
```
|
||||
|
||||
**iOS权限**:
|
||||
在 `manifest.json` 的 `app-plus` -> `ios` 配置中添加:
|
||||
```json
|
||||
"privacyDescription": {
|
||||
"NSBluetoothAlwaysUsageDescription": "需要蓝牙权限以连接录音设备",
|
||||
"NSBluetoothPeripheralUsageDescription": "需要蓝牙权限以连接录音设备",
|
||||
"NSLocationWhenInUseUsageDescription": "需要位置权限以搜索蓝牙设备"
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤2: 初始化蓝牙适配器
|
||||
|
||||
```javascript
|
||||
import bluetoothRecorder from '@/common/bluetooth.js'
|
||||
|
||||
// 初始化蓝牙适配器
|
||||
await bluetoothRecorder.initBluetoothAdapter()
|
||||
```
|
||||
|
||||
### 步骤3: 搜索蓝牙设备
|
||||
|
||||
```javascript
|
||||
// 开始搜索
|
||||
await bluetoothRecorder.startScan({
|
||||
allowDuplicatesKey: false, // 不允许重复上报
|
||||
interval: 0 // 上报间隔
|
||||
})
|
||||
|
||||
// 监听设备发现
|
||||
bluetoothRecorder.onDeviceFound = (device) => {
|
||||
console.log('发现设备:', device.name, device.deviceId)
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤4: 连接蓝牙设备
|
||||
|
||||
```javascript
|
||||
// 连接设备
|
||||
await bluetoothRecorder.connectDevice(deviceId)
|
||||
|
||||
// 监听连接状态
|
||||
bluetoothRecorder.onDeviceConnected = (deviceId) => {
|
||||
console.log('设备连接成功:', deviceId)
|
||||
}
|
||||
|
||||
bluetoothRecorder.onDeviceDisconnected = (deviceId) => {
|
||||
console.log('设备断开:', deviceId)
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤5: 接收音频数据
|
||||
|
||||
连接成功后,系统会自动:
|
||||
1. 获取设备的服务列表
|
||||
2. 查找可通知的特征值
|
||||
3. 启用特征值通知
|
||||
4. 监听数据变化
|
||||
|
||||
```javascript
|
||||
// 监听音频数据接收
|
||||
bluetoothRecorder.onAudioDataReceived = (data) => {
|
||||
// data.data: Base64编码的音频数据
|
||||
// data.arrayBuffer: ArrayBuffer格式的音频数据
|
||||
// data.timestamp: 时间戳
|
||||
|
||||
console.log('收到音频数据:', data)
|
||||
}
|
||||
```
|
||||
|
||||
### 步骤6: 处理音频数据
|
||||
|
||||
#### 6.1 保存到本地文件
|
||||
|
||||
```javascript
|
||||
// 获取所有音频数据
|
||||
const audioData = bluetoothRecorder.mergeAudioData()
|
||||
|
||||
// 转换为ArrayBuffer
|
||||
const arrayBuffer = uni.base64ToArrayBuffer(audioData)
|
||||
|
||||
// 保存到文件系统
|
||||
const fs = uni.getFileSystemManager()
|
||||
const filePath = `${uni.env.USER_DATA_PATH}/recording.pcm`
|
||||
|
||||
fs.writeFile({
|
||||
filePath: filePath,
|
||||
data: arrayBuffer,
|
||||
success: () => {
|
||||
console.log('文件保存成功:', filePath)
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### 6.2 上传到服务器
|
||||
|
||||
```javascript
|
||||
// 准备上传数据
|
||||
const audioData = bluetoothRecorder.mergeAudioData()
|
||||
const arrayBuffer = uni.base64ToArrayBuffer(audioData)
|
||||
|
||||
// 上传到服务器
|
||||
const formData = {
|
||||
audio: arrayBuffer,
|
||||
fileName: 'recording.pcm',
|
||||
deviceId: deviceId
|
||||
}
|
||||
|
||||
const res = await post('/api/audioManagement/uploadBluetoothAudio', formData, {
|
||||
header: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## 四、蓝牙设备协议说明
|
||||
|
||||
### 4.1 服务UUID和特征值
|
||||
|
||||
不同的蓝牙录音设备可能使用不同的服务UUID和特征值。常见的有:
|
||||
|
||||
**标准音频服务**:
|
||||
- 服务UUID: `0000110a-0000-1000-8000-00805f9b34fb` (A2DP)
|
||||
- 特征值UUID: 设备特定
|
||||
|
||||
**自定义服务**:
|
||||
- 需要根据设备厂商提供的文档配置
|
||||
|
||||
### 4.2 数据格式
|
||||
|
||||
蓝牙设备传输的音频数据通常是:
|
||||
- **格式**: PCM (脉冲编码调制)
|
||||
- **采样率**: 8kHz, 16kHz, 44.1kHz 等
|
||||
- **位深**: 16bit
|
||||
- **声道**: 单声道或立体声
|
||||
|
||||
### 4.3 数据包处理
|
||||
|
||||
蓝牙BLE设备通常以数据包形式传输音频:
|
||||
- 每个数据包大小: 20字节 (BLE限制)
|
||||
- 需要将多个数据包合并为完整的音频流
|
||||
- 可能需要处理数据包丢失和重排序
|
||||
|
||||
## 五、使用示例
|
||||
|
||||
### 5.1 完整使用流程
|
||||
|
||||
```javascript
|
||||
import bluetoothRecorder from '@/common/bluetooth.js'
|
||||
|
||||
// 1. 初始化
|
||||
await bluetoothRecorder.initBluetoothAdapter()
|
||||
|
||||
// 2. 搜索设备
|
||||
bluetoothRecorder.onDeviceFound = (device) => {
|
||||
console.log('发现设备:', device)
|
||||
}
|
||||
|
||||
await bluetoothRecorder.startScan()
|
||||
|
||||
// 3. 连接设备
|
||||
await bluetoothRecorder.connectDevice(deviceId)
|
||||
|
||||
// 4. 接收数据
|
||||
bluetoothRecorder.onAudioDataReceived = (data) => {
|
||||
// 处理音频数据
|
||||
console.log('收到数据:', data)
|
||||
}
|
||||
|
||||
// 5. 保存数据
|
||||
const audioData = bluetoothRecorder.mergeAudioData()
|
||||
// ... 保存或上传逻辑
|
||||
|
||||
// 6. 断开连接
|
||||
await bluetoothRecorder.disconnectDevice()
|
||||
|
||||
// 7. 清理资源
|
||||
await bluetoothRecorder.closeBluetoothAdapter()
|
||||
```
|
||||
|
||||
### 5.2 页面集成
|
||||
|
||||
在需要使用的页面中:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<view>
|
||||
<button @click="connectBluetooth">连接蓝牙设备</button>
|
||||
<button @click="saveAudio">保存录音</button>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import bluetoothRecorder from '@/common/bluetooth.js'
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
async connectBluetooth() {
|
||||
await bluetoothRecorder.initBluetoothAdapter()
|
||||
await bluetoothRecorder.startScan()
|
||||
},
|
||||
|
||||
async saveAudio() {
|
||||
const audioData = bluetoothRecorder.mergeAudioData()
|
||||
// 保存逻辑
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
## 六、注意事项
|
||||
|
||||
### 6.1 权限要求
|
||||
|
||||
1. **Android 12+**: 需要 `BLUETOOTH_SCAN` 和 `BLUETOOTH_CONNECT` 权限
|
||||
2. **位置权限**: 搜索蓝牙设备需要位置权限(Android要求)
|
||||
3. **运行时权限**: 需要在代码中动态申请权限
|
||||
|
||||
### 6.2 平台差异
|
||||
|
||||
1. **iOS**:
|
||||
- 需要用户明确授权蓝牙权限
|
||||
- 后台运行限制更严格
|
||||
- 某些蓝牙功能可能受限
|
||||
|
||||
2. **Android**:
|
||||
- 权限管理更复杂
|
||||
- 不同版本API差异较大
|
||||
- 需要处理权限动态申请
|
||||
|
||||
### 6.3 性能优化
|
||||
|
||||
1. **数据缓冲**: 使用缓冲区避免频繁处理小数据包
|
||||
2. **内存管理**: 及时清理不需要的数据,避免内存溢出
|
||||
3. **连接管理**: 合理管理连接状态,避免资源泄漏
|
||||
|
||||
### 6.4 错误处理
|
||||
|
||||
常见错误及处理:
|
||||
|
||||
1. **蓝牙未开启**: 提示用户开启蓝牙
|
||||
2. **权限被拒绝**: 引导用户到设置页面授权
|
||||
3. **设备连接失败**: 检查设备是否在范围内,是否已被其他设备连接
|
||||
4. **数据接收中断**: 实现重连机制
|
||||
|
||||
## 七、调试方法
|
||||
|
||||
### 7.1 日志输出
|
||||
|
||||
工具类已内置详细日志,可通过控制台查看:
|
||||
- 设备搜索日志
|
||||
- 连接状态日志
|
||||
- 数据接收日志
|
||||
- 错误日志
|
||||
|
||||
### 7.2 测试步骤
|
||||
|
||||
1. 确保手机蓝牙已开启
|
||||
2. 确保蓝牙录音设备已开启并处于可发现状态
|
||||
3. 运行应用,点击"开始搜索"
|
||||
4. 等待发现设备后,点击设备进行连接
|
||||
5. 连接成功后,观察数据接收情况
|
||||
6. 测试保存和上传功能
|
||||
|
||||
## 八、扩展功能
|
||||
|
||||
### 8.1 音频格式转换
|
||||
|
||||
如果需要转换为其他格式(如MP3、WAV),可以使用:
|
||||
- 前端转换库(如 lamejs)
|
||||
- 后端转换服务
|
||||
|
||||
### 8.2 实时播放
|
||||
|
||||
可以结合 `uni.createInnerAudioContext()` 实现实时播放接收到的音频数据。
|
||||
|
||||
### 8.3 数据加密
|
||||
|
||||
如果需要对传输的音频数据进行加密,可以在接收数据后进行解密处理。
|
||||
|
||||
## 九、常见问题
|
||||
|
||||
### Q1: 搜索不到设备?
|
||||
- 检查蓝牙是否开启
|
||||
- 检查位置权限是否授权
|
||||
- 检查设备是否处于可发现状态
|
||||
- 尝试重启蓝牙
|
||||
|
||||
### Q2: 连接失败?
|
||||
- 检查设备是否在范围内
|
||||
- 检查设备是否已被其他应用连接
|
||||
- 检查设备是否支持BLE
|
||||
- 查看错误日志获取详细信息
|
||||
|
||||
### Q3: 接收不到数据?
|
||||
- 检查是否已启用特征值通知
|
||||
- 检查服务UUID和特征值UUID是否正确
|
||||
- 检查设备是否正在发送数据
|
||||
- 查看设备文档确认数据格式
|
||||
|
||||
### Q4: 数据不完整?
|
||||
- 检查数据包是否丢失
|
||||
- 实现数据包校验和重传机制
|
||||
- 检查缓冲区是否溢出
|
||||
|
||||
## 十、参考资料
|
||||
|
||||
- [uni-app 蓝牙API文档](https://uniapp.dcloud.net.cn/api/system/bluetooth.html)
|
||||
- [Android 蓝牙开发指南](https://developer.android.com/guide/topics/connectivity/bluetooth)
|
||||
- [iOS Core Bluetooth 文档](https://developer.apple.com/documentation/corebluetooth)
|
||||
|
||||
## 十一、更新日志
|
||||
|
||||
- **2024-01-XX**: 初始版本,实现基础蓝牙连接和数据接收功能
|
||||
- 后续根据实际使用情况进行优化和扩展
|
||||
|
||||
Reference in New Issue
Block a user