diff --git a/services/review.go b/services/review.go index 8063075..b2c7cba 100644 --- a/services/review.go +++ b/services/review.go @@ -287,9 +287,9 @@ func GenerateReview(db *sql.DB, repoPath, base, head string, topN, concurrency i "score": 7, "overall": "总体评价(2-3 句话)", "findings": "按严重程度排序的主要发现汇总", - "recommendations": "改进建议优先级列表" + "recommendations": "改进建议,用换行分隔多条建议" } -请用中文回复。`, strings.Join(reviewParts, "\n\n")) +注意:所有字段必须是字符串类型,不要使用数组。请用中文回复。`, strings.Join(reviewParts, "\n\n")) messages := []goopenai.ChatCompletionMessage{ {Role: goopenai.ChatMessageRoleUser, Content: summaryPrompt}, @@ -299,20 +299,52 @@ func GenerateReview(db *sql.DB, repoPath, base, head string, topN, concurrency i if err == nil { jsonStr := extractJSON(summaryResponse) if json.Unmarshal([]byte(jsonStr), &summary) == nil { - summaryText := fmt.Sprintf("整体评分: %d/10\n\n%s", summary.Score, summary.Overall) - if summary.Findings != "" { - summaryText += "\n\n**主要发现:**\n" + summary.Findings - } - if summary.Recommendations != "" { - summaryText += "\n\n**改进建议:**\n" + summary.Recommendations - } if callback != nil { - callback("summary", map[string]interface{}{"content": summaryText}) + callback("summary", map[string]interface{}{ + "score": summary.Score, + "overall": summary.Overall, + "findings": summary.Findings, + "recommendations": summary.Recommendations, + }) } } else { - // If parsing fails, send raw response as summary - if callback != nil { - callback("summary", map[string]interface{}{"content": summaryResponse}) + // Try flexible parsing (handle recommendations as array) + var raw map[string]json.RawMessage + if json.Unmarshal([]byte(jsonStr), &raw) == nil { + var flexible struct { + Score int `json:"score"` + Overall string `json:"overall"` + Findings string `json:"findings"` + } + json.Unmarshal(raw["score"], &flexible.Score) + json.Unmarshal(raw["overall"], &flexible.Overall) + json.Unmarshal(raw["findings"], &flexible.Findings) + + recommendations := "" + if rec, ok := raw["recommendations"]; ok { + var arr []string + if json.Unmarshal(rec, &arr) == nil { + recommendations = strings.Join(arr, "\n") + } else { + var s string + json.Unmarshal(rec, &s) + recommendations = s + } + } + + if callback != nil { + callback("summary", map[string]interface{}{ + "score": flexible.Score, + "overall": flexible.Overall, + "findings": flexible.Findings, + "recommendations": recommendations, + }) + } + } else { + // If all parsing fails, send raw response as summary + if callback != nil { + callback("summary", map[string]interface{}{"content": summaryResponse}) + } } } } else { diff --git a/templates/pages/repo.html b/templates/pages/repo.html index 966440c..6d0339e 100644 --- a/templates/pages/repo.html +++ b/templates/pages/repo.html @@ -427,6 +427,34 @@ } } + // ── Utilities ──────────────────────────────────────────────── + function escapeHtml(text) { + if (!text) return ''; + return String(text) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + } + + function renderSummaryHtml(data) { + let html = '
${escapeHtml(data.overall)}
${escapeHtml(data.findings)}
${escapeHtml(data.recommendations)}
${escapeHtml(data.overall)}
${escapeHtml(data.findings)}
${escapeHtml(data.recommendations)}