import 'dart:io'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; /// 检查火山语音识别服务配置 /// /// 该工具用于验证火山语音识别服务的配置是否正确,包括: /// 1. 检查环境变量是否设置 /// 2. 检查网络连接 /// 3. 检查认证是否有效 void main() async { // 加载环境变量 await dotenv.load(); print('======== 火山语音识别服务配置检查 ========'); // 检查环境变量 final appId = dotenv.env['VOLCANO_APP_ID']; final appKey = dotenv.env['VOLCANO_APP_KEY']; final cluster = dotenv.env['VOLCANO_CLUSTER']; print('\n1. 检查环境变量:'); if (appId == null || appId.isEmpty) { print('❌ VOLCANO_APP_ID 未设置'); } else { print('✅ VOLCANO_APP_ID: ${appId.substring(0, 3)}***${appId.substring(appId.length - 3)} (长度: ${appId.length})'); } if (appKey == null || appKey.isEmpty) { print('❌ VOLCANO_APP_KEY 未设置'); } else { print('✅ VOLCANO_APP_KEY: ${appKey.substring(0, 3)}***${appKey.substring(appKey.length - 3)} (长度: ${appKey.length})'); } if (cluster == null || cluster.isEmpty) { print('❌ VOLCANO_CLUSTER 未设置,将使用默认值 cn-beijing'); } else { print('✅ VOLCANO_CLUSTER: $cluster'); } // 使用默认值 final effectiveCluster = cluster ?? 'cn-beijing'; if (appId == null || appId.isEmpty || appKey == null || appKey.isEmpty) { print('\n❌ 环境变量配置不完整,请检查 .env 文件'); exit(1); } // 检查网络连接 print('\n2. 检查网络连接:'); try { final result = await InternetAddress.lookup('openspeech.bytedance.com'); if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) { print('✅ 网络连接正常,可以访问 openspeech.bytedance.com'); } else { print('❌ 无法连接到 openspeech.bytedance.com'); } } catch (e) { print('❌ 网络连接异常: $e'); } // 检查认证是否有效 print('\n3. 检查认证有效性:'); http.Response? authResponse; try { // 构建请求URL - 更新为大模型流式识别API路径 final url = 'https://openspeech.bytedance.com/api/v3/sauc/bigmodel'; // 构建请求头 - 不再添加Bearer前缀 final headers = { 'Content-Type': 'application/json', 'Authorization': appKey, // 不再添加Bearer前缀 }; // 构建请求体 - 添加resourceId参数 final body = jsonEncode({ 'app_id': appId, 'cluster': effectiveCluster, 'resource_id': appId, // 资源ID暂时使用与APP_ID相同的值 'ping': true, // 只是ping服务,不进行实际识别 }); print('正在发送测试请求...'); print('请求URL: $url'); print('请求头: Authorization=${appKey.substring(0, 3)}***'); print('集群区域: $effectiveCluster'); // 发送请求 authResponse = await http.post( Uri.parse(url), headers: headers, body: body, ).timeout(const Duration(seconds: 5)); // 检查响应 if (authResponse.statusCode == 200) { print('✅ 认证有效,服务响应正常'); print('响应内容: ${authResponse.body}'); } else { print('❌ 认证无效或服务异常,状态码: ${authResponse.statusCode}'); print('错误信息: ${authResponse.body}'); // 解析错误信息 try { final errorJson = jsonDecode(authResponse.body); final errorCode = errorJson['code']; final errorMsg = errorJson['message']; if (errorCode == 401) { print('\n认证错误,可能的原因:'); print('1. APP_KEY 格式不正确'); print('2. APP_ID 与 APP_KEY 不匹配'); print('3. 账户未开通大模型流式语音识别服务或服务已过期'); print('4. 资源ID不正确或未授权'); } else if (errorCode == 400) { print('\nWebSocket握手错误,可能的原因:'); print('1. 集群区域设置不正确 (当前: $effectiveCluster)'); print('2. 请求参数格式不正确'); print('3. 尝试使用不同的集群区域,如 cn-shanghai 或 cn-guangzhou'); } else { print('\n未知错误:'); print('错误码: $errorCode'); print('错误信息: $errorMsg'); } } catch (e) { print('\n解析错误信息失败: $e'); if (authResponse.statusCode == 400) { print('\nWebSocket握手错误,可能的原因:'); print('1. 集群区域设置不正确 (当前: $effectiveCluster)'); print('2. 请求参数格式不正确'); print('3. 尝试使用不同的集群区域,如 cn-shanghai 或 cn-guangzhou'); } } } } catch (e) { print('❌ 请求异常: $e'); } print('\n======== 检查完成 ========'); print('\n提示: 如果您使用的是大模型流式识别SDK,请确保:'); print('1. 使用了正确的API路径: /api/v3/sauc/bigmodel'); print('2. 不要在Token前添加Bearer前缀'); print('3. 设置了正确的资源ID'); print('4. 设置了协议类型为PROTOCOL_TYPE_SEED'); print('5. 设置了正确的集群区域 (当前: $effectiveCluster)'); // 尝试其他集群区域 if (authResponse != null && authResponse.statusCode == 400) { print('\n尝试其他集群区域:'); final alternativeClusters = [ 'cn-shanghai', 'cn-guangzhou', 'cn-hongkong', 'ap-singapore', 'us-east-1', 'us-west-1' ]; bool foundWorkingCluster = false; for (final altCluster in alternativeClusters) { if (altCluster != effectiveCluster) { print('\n尝试集群区域: $altCluster'); final success = await _testCluster(appId, appKey, altCluster); if (success) { foundWorkingCluster = true; print('\n✅ 找到可用的集群区域: $altCluster'); print('建议在 .env 文件中设置 VOLCANO_CLUSTER=$altCluster'); // 尝试更新.env文件 try { await _updateEnvFile(altCluster); } catch (e) { print('无法自动更新.env文件: $e'); } break; } } } if (!foundWorkingCluster) { print('\n❌ 所有集群区域测试均失败'); print('请联系火山引擎技术支持获取正确的集群区域'); } } } /// 测试不同的集群区域 Future _testCluster(String appId, String appKey, String cluster) async { try { final url = 'https://openspeech.bytedance.com/api/v3/sauc/bigmodel'; final headers = { 'Content-Type': 'application/json', 'Authorization': appKey, }; final body = jsonEncode({ 'app_id': appId, 'cluster': cluster, 'resource_id': appId, 'ping': true, }); final response = await http.post( Uri.parse(url), headers: headers, body: body, ).timeout(const Duration(seconds: 5)); if (response.statusCode == 200) { print('✅ 集群区域 $cluster 可用,认证有效'); return true; } else { print('❌ 集群区域 $cluster 不可用,状态码: ${response.statusCode}'); return false; } } catch (e) { print('❌ 测试集群区域 $cluster 时出错: $e'); return false; } } /// 尝试更新.env文件 Future _updateEnvFile(String newCluster) async { try { final file = File('.env'); if (!await file.exists()) { print('❌ .env文件不存在,无法自动更新'); return; } String content = await file.readAsString(); // 检查是否已有VOLCANO_CLUSTER final clusterRegex = RegExp(r'VOLCANO_CLUSTER=.*'); if (clusterRegex.hasMatch(content)) { // 替换现有的VOLCANO_CLUSTER content = content.replaceAll(clusterRegex, 'VOLCANO_CLUSTER=$newCluster'); } else { // 添加新的VOLCANO_CLUSTER content += '\nVOLCANO_CLUSTER=$newCluster'; } // 写入文件 await file.writeAsString(content); print('✅ 已自动更新.env文件中的VOLCANO_CLUSTER=$newCluster'); } catch (e) { print('❌ 更新.env文件失败: $e'); throw e; } }