91、商品服务-API-新增商品-商品保存debug完成

本文详细介绍了如何批量重启服务,并将最大占用内存设置为100兆,确保系统资源的有效利用。通过具体代码示例,展示了如何在不同场景下进行内存优化,包括高级部分的完善策略。

批量重启

最大占用内存设置 100兆,所有服务都给上

 

/**
     * //TODO 高级部分完善
     * @param vo
     */
    @Transactional
    @Override
    public void saveSpuInfo(SpuSaveVo vo) {

        //1、保存spu基本信息 pms_spu_info
        SpuInfoEntity infoEntity = new SpuInfoEntity();
        BeanUtils.copyProperties(vo,infoEntity);
        infoEntity.setCreateTime(new Date());
        infoEntity.setUpdateTime(new Date());
        this.saveBaseSpuInfo(infoEntity);

        //2、保存Spu的描述图片 pms_spu_info_desc
        List<String> decript = vo.getDecript();
        SpuInfoDescEntity descEntity = new SpuInfoDescEntity();
        descEntity.setSpuId(infoEntity.getId());
        descEntity.setDecript(String.join(",",decript));
        spuInfoDescService.saveSpuInfoDesc(descEntity);

        //3、保存spu的图片集 pms_spu_images
        List<String> images = vo.getImages();
        imagesService.saveImages(infoEntity.getId(),images);

        //4、保存spu的规格参数;pms_product_attr_value
        List<BaseAttrs> baseAttrs = vo.getBaseAttrs();
        List<ProductAttrValueEntity> collect = baseAttrs.stream().map(attr -> {
            ProductAttrValueEntity valueEntity = new ProductAttrValueEntity();
            valueEntity.setAttrId(attr.getAttrId());
            AttrEntity id = attrService.getById(attr.getAttrId());
            valueEntity.setAttrName(id.getAttrName());
            valueEntity.setAttrValue(attr.getAttrValues());
            valueEntity.setQuickShow(attr.getShowDesc());
            valueEntity.setSpuId(infoEntity.getId());

            return valueEntity;
        }).collect(Collectors.toList());
        attrValueService.saveProductAttr(collect);

        //5、保存spu的积分信息;gulimall_sms->sms_spu_bounds
        Bounds bounds = vo.getBounds();
        SpuBoundTo spuBoundTo = new SpuBoundTo();
        BeanUtils.copyProperties(bounds,spuBoundTo);
        spuBoundTo.setSpuId(infoEntity.getId());
        R r = couponFeignService.saveSpuBounds(spuBoundTo);
        if(r.getCode() != 0){
            log.error("远程保存spu积分信息失败");
        }

        //5、保存当前spu对应的所有sku信息;

        List<Skus> skus = vo.getSkus();
        if(skus!=null && skus.size()>0){
            skus.forEach(item->{
                String defaultImg = "";
                for (Images image : item.getImages()) {
                    if(image.getDefaultImg() == 1){
                        defaultImg = image.getImgUrl();
                    }
                }
                //    private String skuName;
                //    private BigDecimal price;
                //    private String skuTitle;
                //    private String skuSubtitle;
                SkuInfoEntity skuInfoEntity = new SkuInfoEntity();
                BeanUtils.copyProperties(item,skuInfoEntity);
                skuInfoEntity.setBrandId(infoEntity.getBrandId());
                skuInfoEntity.setCatalogId(infoEntity.getCatalogId());
                skuInfoEntity.setSaleCount(0L);
                skuInfoEntity.setSpuId(infoEntity.getId());
                skuInfoEntity.setSkuDefaultImg(defaultImg);
                //5.1)、sku的基本信息;pms_sku_info
                skuInfoService.saveSkuInfo(skuInfoEntity);

                Long skuId = skuInfoEntity.getSkuId();

                List<SkuImagesEntity> imagesEntities = item.getImages().stream().map(img -> {
                    SkuImagesEntity skuImagesEntity = new SkuImagesEntity();
                    skuImagesEntity.setSkuId(skuId);
                    skuImagesEntity.setImgUrl(img.getImgUrl());
                    skuImagesEntity.setDefaultImg(img.getDefaultImg());
                    return skuImagesEntity;
                }).filter(entity->{
                    //返回true就是需要,false就是剔除
                    return !StringUtils.isEmpty(entity.getImgUrl());
                }).collect(Collectors.toList());
                //5.2)、sku的图片信息;pms_sku_image
                skuImagesService.saveBatch(imagesEntities);
                //TODO 没有图片路径的无需保存

                List<Attr> attr = item.getAttr();
                List<SkuSaleAttrValueEntity> skuSaleAttrValueEntities = attr.stream().map(a -> {
                    SkuSaleAttrValueEntity attrValueEntity = new SkuSaleAttrValueEntity();
                    BeanUtils.copyProperties(a, attrValueEntity);
                    attrValueEntity.setSkuId(skuId);

                    return attrValueEntity;
                }).collect(Collectors.toList());
                //5.3)、sku的销售属性信息:pms_sku_sale_attr_value
                skuSaleAttrValueService.saveBatch(skuSaleAttrValueEntities);

                // //5.4)、sku的优惠、满减等信息;gulimall_sms->sms_sku_ladder\sms_sku_full_reduction\sms_member_price
                SkuReductionTo skuReductionTo = new SkuReductionTo();
                BeanUtils.copyProperties(item,skuReductionTo);
                skuReductionTo.setSkuId(skuId);
                if(skuReductionTo.getFullCount() >0 || skuReductionTo.getFullPrice().compareTo(new BigDecimal("0")) == 1){
                    R r1 = couponFeignService.saveSkuReduction(skuReductionTo);
                    if(r1.getCode() != 0){
                        log.error("远程保存sku优惠信息失败");
                    }
                }



            });
        }

    }

 

<think>我们需要在调试台中显示发送的音频帧和服务器返回的结果信息。这可以通过在发送和接收时添加Debug.Log来实现。但是要注意,发送的音频帧数据量可能很大,所以我们只记录关键信息(如帧大小、状态等)而不是完整的音频数据。 修改点: 1. 在发送初始化帧、音频帧和结束帧时,记录发送的JSON内容(对于音频帧,由于数据量大,只记录帧大小和状态)。 2. 在收到服务器消息时,记录完整的JSON响应(包括状态、错误信息等)。 但是注意:音频帧的base64数据很长,打印整个字符串会导致性能问题,所以只打印摘要信息。 具体修改: - 在SendInitFrame和SendEndFrame中,打印完整的JSON(因为数据量小)。 - 在SendAudioFrame中,打印摘要信息(如帧大小、状态和sid)。 - 在OnMessage回调中,打印完整的服务器响应(因为我们需要查看status=2的结果)。 另外,我们可以将服务器返回的每个消息(包括中间状态)都显示在UI的resultText中,但考虑到中间状态消息较多,我们只将最终结果显示在UI上,而中间状态和错误信息可以显示在调试日志中。 同时,为了避免日志过多,我们可以提供一个开关来控制是否显示详细日志。 我们添加一个bool变量来控制详细日志: private bool verboseLogging = true; 然后,在关键点添加日志输出。 代码修改如下:</think>以下是增强调试功能的解决方案,增加发送和接收数据的详细显示,便于排查问题: ```csharp using UnityEngine; using System; using System.Text; using System.Collections; using System.Security.Cryptography; using NativeWebSocket; using System.Globalization; using UnityEngine.UI; using System.Threading.Tasks; using System.IO; public class XunfeiISEController : MonoBehaviour { // 配置参数 const string APP_ID = "d11f22d5"; const string API_KEY = "6c1a2fc3bac9767a92aeb80098c89b61"; const string API_SECRET = "YjJjZmM1MjI3N2ZjMmFiZmQ2Y2E2NWI1"; const string HOST = "ise-api.xfyun.cn"; const string PATH = "/v2/open-ise"; // UI元素 public Button startButton; public Button stopButton; public Text statusText; public Text resultText; public Text progressText; public Text debugText; // 新增:调试信息显示 public ScrollRect debugScroll; // 新增:调试信息滚动区域 WebSocket websocket; bool isConnected = false; private AudioClip recordingClip; private bool isRecording = false; private int recordingPosition = 0; private string sid = null; private float lastSendTime = 0f; private const float SEND_INTERVAL = 0.1f; private int totalFramesSent = 0; private int totalFramesRecorded = 0; private bool waitingForResult = false; private bool verboseLogging = true; // 控制详细日志 // 定义JSON解析结构 [Serializable] public class ISEResponse { public int code; public string message; public ISEData data; } [Serializable] public class ISEData { public string sid; public int status; public string data; } void Start() { // 初始化UI状态 UpdateUIState(ConnectionState.Disconnected); // 绑定按钮事件 startButton.onClick.AddListener(StartEvaluation); stopButton.onClick.AddListener(StopEvaluation); // 初始禁用停止按钮 stopButton.interactable = false; // 初始化调试文本 debugText.text = "调试信息将显示在此处...\n"; } // 开始评测流程 public async void StartEvaluation() { if (isConnected || isRecording) return; try { // 重置状态 totalFramesSent = 0; totalFramesRecorded = 0; resultText.text = ""; progressText.text = "准备开始..."; waitingForResult = false; debugText.text = "调试信息:\n"; // 清空调试信息 AddDebugLog("开始评测流程..."); UpdateUIState(ConnectionState.Connecting); // 1. 生成鉴权URL string authUrl = GenerateAuthUrl(); AddDebugLog($"生成鉴权URL: {authUrl}"); // 2. 创建WebSocket连接 websocket = new WebSocket(authUrl); // 设置事件回调 websocket.OnOpen += () => { AddDebugLog("WebSocket连接成功!"); isConnected = true; UpdateUIState(ConnectionState.Connected); SendInitFrame(); }; websocket.OnError += (err) => { AddDebugLog($"WebSocket错误: {err}", true); isConnected = false; UpdateUIState(ConnectionState.Error); resultText.text = $"连接错误: {err}"; }; websocket.OnClose += (code) => { AddDebugLog($"连接关闭,代码: {code}"); isConnected = false; if (!waitingForResult) { UpdateUIState(ConnectionState.Disconnected); } }; websocket.OnMessage += (data) => { string json = Encoding.UTF8.GetString(data); AddDebugLog($"收到服务器响应:\n{FormatJson(json)}"); // 解析结果 try { ISEResponse response = JsonUtility.FromJson<ISEResponse>(json); // 保存会话ID if (response.data != null && !string.IsNullOrEmpty(response.data.sid)) { sid = response.data.sid; AddDebugLog($"会话ID: {sid}"); } // 处理最终评测结果 if (response.data != null && response.data.status == 2) { if (!string.IsNullOrEmpty(response.data.data)) { // 解码Base64结果 byte[] resultBytes = Convert.FromBase64String(response.data.data); string xmlResult = Encoding.UTF8.GetString(resultBytes); AddDebugLog($"最终结果XML:\n{xmlResult}"); // 解析XML并显示结果 ParseResultXML(xmlResult); // 评测完成后关闭连接 CloseConnection(); waitingForResult = false; } } else if (response.code != 0) // 处理错误响应 { AddDebugLog($"API错误: {response.code} - {response.message}", true); resultText.text = $"错误: {response.code} - {response.message}"; CloseConnection(); } else if (response.data != null) { AddDebugLog($"处理状态: {response.data.status}"); } } catch (Exception e) { AddDebugLog($"解析错误: {e.Message}", true); resultText.text = $"解析错误: {e.Message}"; } }; // 3. 使用await解决CS4014警告 AddDebugLog("正在连接WebSocket..."); await websocket.Connect(); } catch (Exception e) { AddDebugLog($"连接失败: {e.Message}", true); UpdateUIState(ConnectionState.Error); resultText.text = $"连接失败: {e.Message}"; } } // 停止评测流程 public void StopEvaluation() { if (!isRecording) return; AddDebugLog("停止评测..."); Microphone.End(null); isRecording = false; // 发送剩余音频数据 SendAudioFrames(true); // 发送结束帧 SendEndFrame(); // 设置等待结果状态 waitingForResult = true; UpdateUIState(ConnectionState.WaitingForResult); progressText.text = "评测结束,等待结果..."; } // 关闭WebSocket连接 private async void CloseConnection() { if (websocket != null && websocket.State == WebSocketState.Open) { try { AddDebugLog("正在关闭WebSocket连接..."); await websocket.Close(); } catch (Exception e) { AddDebugLog($"关闭错误: {e.Message}", true); } finally { UpdateUIState(ConnectionState.Disconnected); } } } // 更新UI状态 private void UpdateUIState(ConnectionState state) { switch (state) { case ConnectionState.Disconnected: statusText.text = "状态: 未连接"; statusText.color = Color.gray; startButton.interactable = true; stopButton.interactable = false; break; case ConnectionState.Connecting: statusText.text = "状态: 连接中..."; statusText.color = Color.yellow; startButton.interactable = false; stopButton.interactable = false; break; case ConnectionState.Connected: statusText.text = "状态: 已连接"; statusText.color = Color.green; startButton.interactable = false; stopButton.interactable = true; break; case ConnectionState.Recording: statusText.text = "状态: 录音中..."; statusText.color = Color.cyan; startButton.interactable = false; stopButton.interactable = true; break; case ConnectionState.WaitingForResult: statusText.text = "状态: 等待结果..."; statusText.color = Color.magenta; startButton.interactable = false; stopButton.interactable = false; break; case ConnectionState.Error: statusText.text = "状态: 错误!"; statusText.color = Color.red; startButton.interactable = true; stopButton.interactable = false; break; } } // 生成鉴权URL string GenerateAuthUrl() { // 获取RFC1123格式的GMT时间 string date = DateTime.UtcNow.ToString("r", CultureInfo.InvariantCulture); // 构建签名字符串 string signatureOrigin = $"host: {HOST}\ndate: {date}\nGET {PATH} HTTP/1.1"; // HMAC-SHA256签名 byte[] signatureBytes = HMACSHA256( Encoding.UTF8.GetBytes(API_SECRET), Encoding.UTF8.GetBytes(signatureOrigin) ); string signature = Convert.ToBase64String(signatureBytes); // 构建授权参数 string authorization = $"api_key=\"{API_KEY}\", algorithm=\"hmac-sha256\", headers=\"host date request-line\", signature=\"{signature}\""; // Base64编码授权参数 string encodedAuth = Convert.ToBase64String(Encoding.UTF8.GetBytes(authorization)); // 构造完整URL return $"wss://{HOST}{PATH}?authorization={encodedAuth}&date={Uri.EscapeDataString(date)}&host={HOST}"; } // 发送初始化帧 void SendInitFrame() { // 评测文本 string textContent = "When you don't know what you're doing, it's helpful to begin by learning about what you should not do."; // 构建初始化帧数据结构 string initJson = @"{ ""common"": { ""app_id"": """ + APP_ID + @""" }, ""business"": { ""aue"": ""raw"", ""auf"": ""audio/L16;rate=16000"", ""category"": ""read_sentence"", ""cmd"": ""ssb"", ""ent"": ""en_vip"", ""sub"": ""ise"", ""text"": """ + Convert.ToBase64String(Encoding.UTF8.GetBytes(textContent)) + @""", ""ttp_skip"": true }, ""data"": { ""status"": 0 } }"; // 发送初始化帧 websocket.Send(Encoding.UTF8.GetBytes(initJson)); AddDebugLog($"发送初始化帧:\n{FormatJson(initJson)}"); // 开始录音 StartRecording(); } // 开始录音 void StartRecording() { if (!isRecording) { AddDebugLog("开始录音..."); recordingClip = Microphone.Start(null, false, 30, 16000); recordingPosition = 0; isRecording = true; lastSendTime = Time.time; UpdateUIState(ConnectionState.Recording); progressText.text = "录音中..."; } } // 发送音频数据帧 private void SendAudioFrames(bool sendAll = false) { if (recordingClip == null || string.IsNullOrEmpty(sid) || !isConnected) return; int currentPos = Microphone.GetPosition(null); int sampleSize = currentPos - recordingPosition; // 处理循环缓冲区 if (sampleSize < 0) sampleSize += recordingClip.samples; if (sampleSize > 0) { // 读取音频数据 float[] samples = new float[sampleSize]; recordingClip.GetData(samples, recordingPosition); // 转换为16位PCM byte[] pcmData = new byte[sampleSize * 2]; for (int i = 0; i < sampleSize; i++) { short value = (short)(samples[i] * 32767); byte[] bytes = BitConverter.GetBytes(value); pcmData[i * 2] = bytes[0]; pcmData[i * 2 + 1] = bytes[1]; } // 计算帧大小 int frameSize = 16000 * 16 / 50; frameSize = Math.Max(512, frameSize); // 分帧发送 for (int offset = 0; offset < pcmData.Length; offset += frameSize) { int remaining = pcmData.Length - offset; int sendSize = Math.Min(remaining, frameSize); if (!sendAll && offset + frameSize > pcmData.Length) break; byte[] frame = new byte[sendSize]; Buffer.BlockCopy(pcmData, offset, frame, 0, sendSize); SendAudioFrame(frame); totalFramesSent++; // 更新进度 progressText.text = $"已发送: {totalFramesSent}帧"; } recordingPosition = currentPos; totalFramesRecorded = sampleSize / (frameSize / 2); } } // 发送单帧音频数据 private void SendAudioFrame(byte[] audioData) { if (websocket == null || !isConnected || string.IsNullOrEmpty(sid)) return; string base64Audio = Convert.ToBase64String(audioData); // 创建简化的调试信息(避免显示大量base64数据) string debugInfo = @"{ ""data"": { ""status"": 1, ""data"": ""[音频数据: " + audioData.Length + " 字节]"" }, ""sid"": """ + sid + @""" }"; // 实际发送的数据 string audioFrame = @"{ ""data"": { ""status"": 1, ""data"": """ + base64Audio + @""" }, ""sid"": """ + sid + @""" }"; websocket.Send(Encoding.UTF8.GetBytes(audioFrame)); if (verboseLogging) { AddDebugLog($"发送音频帧: {audioData.Length}字节\n{FormatJson(debugInfo)}"); } else { AddDebugLog($"发送音频帧: {audioData.Length}字节"); } } // 发送结束帧 private void SendEndFrame() { if (websocket == null || !isConnected || string.IsNullOrEmpty(sid)) return; string endFrame = @"{ ""data"": { ""status"": 2, ""data"": """" }, ""sid"": """ + sid + @""" }"; websocket.Send(Encoding.UTF8.GetBytes(endFrame)); AddDebugLog($"发送结束帧:\n{FormatJson(endFrame)}"); } // 解析评测结果XML private void ParseResultXML(string xml) { try { // 提取总分 if (xml.Contains("<total_score>")) { int start = xml.IndexOf("<total_score>") + "<total_score>".Length; int end = xml.IndexOf("</total_score>"); if (start > 0 && end > start) { string scoreStr = xml.Substring(start, end - start); if (float.TryParse(scoreStr, out float totalScore)) { // 显示结果 resultText.text = $"总分: {totalScore:F1}"; // 提取其他评分项 ExtractScore(xml, "pronunciation_score", "发音得分"); ExtractScore(xml, "intonation_score", "语调得分"); ExtractScore(xml, "integrity_score", "完整度得分"); ExtractScore(xml, "fluency_score", "流畅度得分"); // 添加星级评价 resultText.text += $"\n\n星级评价: {GetStarRating(totalScore)}"; } } } else { resultText.text = "未找到评分信息"; AddDebugLog("XML中未找到total_score标签"); } } catch (Exception e) { AddDebugLog($"XML解析错误: {e.Message}", true); resultText.text = $"解析错误: {e.Message}"; } } // 获取星级评价 private string GetStarRating(float score) { if (score >= 95) return "★★★★★ (优秀)"; if (score >= 85) return "★★★★☆ (良好)"; if (score >= 75) return "★★★☆☆ (中等)"; if (score >= 60) return "★★☆☆☆ (及格)"; return "★☆☆☆☆ (需加强)"; } // 提取特定分数项 private void ExtractScore(string xml, string tag, string displayName) { string startTag = $"<{tag}>"; string endTag = $"</{tag}>"; if (xml.Contains(startTag) && xml.Contains(endTag)) { int start = xml.IndexOf(startTag) + startTag.Length; int end = xml.IndexOf(endTag); if (start > 0 && end > start) { string scoreStr = xml.Substring(start, end - start); if (float.TryParse(scoreStr, out float score)) { resultText.text += $"\n{displayName}: {score:F1}"; } } } } void Update() { // 处理消息队列 if (websocket != null && isConnected) { #if !UNITY_WEBGL || UNITY_EDITOR websocket.DispatchMessageQueue(); #endif } // 录音时定期发送音频帧 if (isRecording && isConnected && !string.IsNullOrEmpty(sid)) { if (Time.time - lastSendTime >= SEND_INTERVAL) { SendAudioFrames(); lastSendTime = Time.time; progressText.text = $"录音中...已发送: {totalFramesSent}帧"; } } // 处理等待结果超时 if (waitingForResult && Time.time - lastSendTime > 30f) { AddDebugLog("等待结果超时,强制关闭连接", true); resultText.text = "错误: 等待结果超时"; CloseConnection(); waitingForResult = false; } // 自动滚动到底部 if (debugScroll.verticalNormalizedPosition <= 0.01f) { debugScroll.verticalNormalizedPosition = 0f; } } void OnDestroy() { // 关闭WebSocket if (websocket != null) { websocket.Close(); } // 停止录音 if (isRecording) { Microphone.End(null); } } // HMAC-SHA256计算 byte[] HMACSHA256(byte[] key, byte[] data) { using (var hmac = new HMACSHA256(key)) { return hmac.ComputeHash(data); } } // 连接状态枚举 private enum ConnectionState { Disconnected, Connecting, Connected, Recording, WaitingForResult, Error } // 添加调试日志 private void AddDebugLog(string message, bool isError = false) { string timestamp = DateTime.Now.ToString("HH:mm:ss.fff"); string formattedMessage = $"[{timestamp}] {message}\n"; if (isError) { Debug.LogError(message); debugText.text += $"<color=red>{formattedMessage}</color>"; } else { Debug.Log(message); debugText.text += formattedMessage; } // 限制日志长度 if (debugText.text.Length > 10000) { debugText.text = debugText.text.Substring(debugText.text.Length - 8000); } } // 格式化JSON字符串 private string FormatJson(string json) { if (!verboseLogging) return json; try { // 简单缩进格式化 int indentLevel = 0; bool inQuotes = false; StringBuilder formatted = new StringBuilder(); for (int i = 0; i < json.Length; i++) { char c = json[i]; if (c == '"' && (i == 0 || json[i-1] != '\\')) { inQuotes = !inQuotes; } if (!inQuotes) { switch (c) { case '{': case '[': formatted.Append(c); indentLevel++; formatted.Append("\n" + new string(' ', indentLevel * 2)); break; case '}': case ']': indentLevel--; formatted.Append("\n" + new string(' ', indentLevel * 2) + c); break; case ',': formatted.Append(c); formatted.Append("\n" + new string(' ', indentLevel * 2)); break; case ':': formatted.Append(": "); break; default: formatted.Append(c); break; } } else { formatted.Append(c); } } return formatted.ToString(); } catch { return json; } } // 调试工具:保存日志到文件 public void SaveDebugLogToFile() { string path = Application.persistentDataPath + "/debug_log.txt"; File.WriteAllText(path, debugText.text); AddDebugLog($"调试日志已保存到: {path}"); } // 调试工具:切换详细日志模式 public void ToggleVerboseLogging() { verboseLogging = !verboseLogging; AddDebugLog($"详细日志模式: {(verboseLogging ? "开启" : "关闭")}"); } } ``` ### 关键调试功能增强: #### 1. 新增调试UI组件 ```csharp public Text debugText; // 调试信息显示区域 public ScrollRect debugScroll; // 调试信息滚动区域 // 在Start方法中初始化 debugText.text = "调试信息将显示在此处...\n"; ``` #### 2. 增强调试日志系统 ```csharp // 添加调试日志 private void AddDebugLog(string message, bool isError = false) { string timestamp = DateTime.Now.ToString("HH:mm:ss.fff"); string formattedMessage = $"[{timestamp}] {message}\n"; if (isError) { Debug.LogError(message); debugText.text += $"<color=red>{formattedMessage}</color>"; } else { Debug.Log(message); debugText.text += formattedMessage; } // 自动滚动到底部 Canvas.ForceUpdateCanvases(); debugScroll.verticalNormalizedPosition = 0f; } ``` #### 3. JSON格式化工具 ```csharp // 格式化JSON字符串 private string FormatJson(string json) { // 实现简单的JSON格式化 // ... } ``` #### 4. 详细数据记录 **发送初始化帧:** ```csharp AddDebugLog($"发送初始化帧:\n{FormatJson(initJson)}"); ``` **发送音频帧:** ```csharp // 创建简化的调试信息(避免显示大量base64数据) string debugInfo = @"{ ""data"": { ""status"": 1, ""data"": ""[音频数据: " + audioData.Length + " 字节]"" }, ""sid"": """ + sid + @""" }"; if (verboseLogging) { AddDebugLog($"发送音频帧: {audioData.Length}字节\n{FormatJson(debugInfo)}"); } else { AddDebugLog($"发送音频帧: {audioData.Length}字节"); } ``` **发送结束帧:** ```csharp AddDebugLog($"发送结束帧:\n{FormatJson(endFrame)}"); ``` **接收服务器响应:** ```csharp websocket.OnMessage += (data) => { string json = Encoding.UTF8.GetString(data); AddDebugLog($"收到服务器响应:\n{FormatJson(json)}"); // ... }; ``` #### 5. 调试工具函数 ```csharp // 调试工具:保存日志到文件 public void SaveDebugLogToFile() { string path = Application.persistentDataPath + "/debug_log.txt"; File.WriteAllText(path, debugText.text); AddDebugLog($"调试日志已保存到: {path}"); } // 调试工具:切换详细日志模式 public void ToggleVerboseLogging() { verboseLogging = !verboseLogging; AddDebugLog($"详细日志模式: {(verboseLogging ? "开启" : "关闭")}"); } ``` #### 6. 错误状态处理增强 ```csharp websocket.OnError += (err) => { AddDebugLog($"WebSocket错误: {err}", true); // ... }; websocket.OnClose += (code) => { AddDebugLog($"连接关闭,代码: {code}"); // ... }; ``` ### 使用说明: 1. **UI设置**: - 在Canvas中添加新的Text组件用于显示调试信息 - 添加ScrollRect组件包裹调试文本 - 可添加两个按钮: - "保存日志":绑定SaveDebugLogToFile方法 - "详细日志":绑定ToggleVerboseLogging方法 2. **调试功能**: - 所有发送和接收的JSON数据都会格式化显示 - 音频帧数据会显示大小但不显示完整内容(避免性能问题) - 错误信息以红色显示 - 时间戳精确到毫秒 - 日志自动滚动到底部 3. **日志控制**: - `verboseLogging`变量控制是否显示详细JSON - 日志长度超过10000字符时自动截断 - 可通过UI按钮切换详细日志模式 4. **错误排查流程**: - 观察发送的初始化帧是否正确 - 确认音频帧是否按预期发送 - 检查服务器返回的状态码 - 查看是否有错误信息(红色日志) - 保存日志文件分析 ### 调试输出示例: ``` [14:25:03.456] 开始评测流程... [14:25:03.501] 生成鉴权URL: wss://ise-api.xfyun.cn/v2/open-ise?authorization=...&date=... [14:25:03.502] 正在连接WebSocket... [14:25:04.123] WebSocket连接成功! [14:25:04.125] 发送初始化帧: { "common": { "app_id": "d11f22d5" }, "business": { "aue": "raw", "auf": "audio/L16;rate=16000", "category": "read_sentence", "cmd": "ssb", "ent": "en_vip", "sub": "ise", "text": "V2hlbiB5b3UgZG9uJ3Qga25vdyB3aGF0IHlvdSdyZSBkb2luZywgaXQncyBoZWxwZnVsIHRvIGJlZ2luIGJ5IGxlYXJuaW5nIGFib3V0IHdoYXQgeW91IHNob3VsZCBub3QgZG8u", "ttp_skip": true }, "data": { "status": 0 } } [14:25:04.126] 开始录音... [14:25:04.230] 发送音频帧: 5120字节 { "data": { "status": 1, "data": "[音频数据: 5120 字节]" }, "sid": "abcdefg123456" } [14:25:04.330] 收到服务器响应: { "code": 0, "message": "success", "data": { "sid": "abcdefg123456", "status": 1 } } [14:25:06.789] 发送结束帧: { "data": { "status": 2, "data": "" }, "sid": "abcdefg123456" } [14:25:07.123] 收到服务器响应: { "code": 0, "message": "success", "data": { "sid": "abcdefg123456", "status": 2, "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHJlc3VsdD4KICA8dG90YWxfc2NvcmU+OTIuNTwvdG90YWxfc2NvcmU+CiAgPHByb251bmNpYXRpb25fc2NvcmU+OTUuMDwvcHJvbnVuY2lhdGlvbl9zY29yZT4KICA8aW50b25hdGlvbl9zY29yZT44OS41PC9pbnRvbmF0aW9uX3Njb3JlPgogIDxpbnRlZ3JpdHlfc2NvcmU+OTAuMDwvaW50ZWdyaXR5X3Njb3JlPgogIDxmbHVlbmN5X3Njb3JlPjg4LjA8L2ZsdWVuY3lfc2NvcmU+CjwvcmVzdWx0Pg==" } } [14:25:07.125] 最终结果XML: <?xml version="1.0" encoding="UTF-8"?> <result> <total_score>92.5</total_score> <pronunciation_score>95.0</pronunciation_score> <intonation_score>89.5</intonation_score> <integrity_score>90.0</integrity_score> <fluency_score>88.0</fluency_score> </result> ``` ### 调试技巧: 1. **常见问题排查**: - 如果未收到响应:检查网络连接和鉴权URL - 如果收到错误代码:参考讯飞API文档的错误代码表 - 如果未收到最终结果:检查是否发送了结束帧,等待时间是否足够 2. **日志分析**: - 使用`SaveDebugLogToFile`保存完整日志 - 搜索"error"或"warning"定位问题 - 比较发送数据和API文档要求是否一致 3. **性能优化**: - 在正式版本中设置`verboseLogging = false` - 避免在移动设备上显示过多日志 - 定期清理日志文本
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值