From a454826433aa8cb23e660c6ac68ecf0ef443b853 Mon Sep 17 00:00:00 2001 From: qhd <1766646056@qq.com> Date: Tue, 1 Sep 2026 10:27:11 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=88=86=E6=9E=90=E5=93=8D=E5=BA=94JSO?= =?UTF-8?q?N=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service/error_analysis.go | 28 ++++++++++++++++++++++ service/error_analysis_test.go | 43 ++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 service/error_analysis.go create mode 100644 service/error_analysis_test.go diff --git a/service/error_analysis.go b/service/error_analysis.go new file mode 100644 index 0000000..c1232f2 --- /dev/null +++ b/service/error_analysis.go @@ -0,0 +1,28 @@ +package service + +import ( + "encoding/json" + "fmt" + "strings" +) + +// parseAnalysisResponse 解析分析模型输出的判定 JSON。 +// 容错:剥 ```json 代码块/首尾空白/多余文字,取首个 {...}。 +func parseAnalysisResponse(content string) (retryable bool, reason string, err error) { + s := strings.TrimSpace(content) + s = strings.TrimPrefix(s, "```json") + s = strings.TrimSuffix(s, "```") + s = strings.TrimSpace(s) + start, end := strings.IndexByte(s, '{'), strings.LastIndexByte(s, '}') + if start < 0 || end <= start { + return false, "", fmt.Errorf("分析响应中未找到JSON对象: %q", content) + } + var obj struct { + Retryable bool `json:"retryable"` + Reason string `json:"reason"` + } + if err = json.Unmarshal([]byte(s[start:end+1]), &obj); err != nil { + return false, "", fmt.Errorf("分析响应JSON解析失败: %v", err) + } + return obj.Retryable, obj.Reason, nil +} diff --git a/service/error_analysis_test.go b/service/error_analysis_test.go new file mode 100644 index 0000000..992d93e --- /dev/null +++ b/service/error_analysis_test.go @@ -0,0 +1,43 @@ +package service + +import "testing" + +func TestParseAnalysisResponse(t *testing.T) { + cases := []struct { + name string + in string + wantRetry bool + wantErr bool + }{ + {"纯JSON", `{"retryable": true, "reason": "限流"}`, true, false}, + {"带json代码块", "```json\n{\"retryable\": false, \"reason\": \"参数错误\"}\n```", false, false}, + {"带前后文字", `分析结果: {"retryable": true, "reason": "瞬时故障"} 完毕`, true, false}, + {"非法响应", `抱歉,我无法分析`, false, true}, + {"空串", ``, false, true}, + } + for _, c := range cases { + retry, _, err := parseAnalysisResponse(c.in) + if c.wantErr { + if err == nil { + t.Fatalf("[%s] 期望错误, got retry=%v", c.name, retry) + } + continue + } + if err != nil { + t.Fatalf("[%s] 不应报错: %v", c.name, err) + } + if retry != c.wantRetry { + t.Fatalf("[%s] retryable = %v, want %v", c.name, retry, c.wantRetry) + } + } +} + +func TestParseAnalysisResponseReason(t *testing.T) { + _, reason, err := parseAnalysisResponse(`{"retryable": true, "reason": "服务过载"}`) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + if reason != "服务过载" { + t.Fatalf("reason = %q, want 服务过载", reason) + } +}