feat: 持久化 AI 审查结果,文件级内联展示与页面集成

This commit is contained in:
2026-06-19 23:24:06 +08:00
parent a89d057716
commit 0528eee4de
7 changed files with 518 additions and 23 deletions
+68 -10
View File
@@ -84,23 +84,18 @@ func (h *ReviewHandler) Review(c *gin.Context) {
h.db.Exec(`UPDATE repositories SET last_used = datetime('now') WHERE id = ?`, id)
// Run AI review
err = services.GenerateReview(h.db, localPath, req.Base, req.Head, topN, sendEvent)
reviewResult, err := services.GenerateReview(h.db, localPath, req.Base, req.Head, topN, sendEvent)
if err != nil {
sendEvent("error", map[string]interface{}{"message": err.Error()})
return
}
// Save analysis to DB
resultData := map[string]interface{}{
"base": req.Base,
"head": req.Head,
"top_n": topN,
}
resultJSON, _ := json.Marshal(resultData)
result, err := h.db.Exec(`INSERT INTO analyses (repo_id, type, base_ref, head_ref, result) VALUES (?, 'code_review', ?, ?, ?)`,
// Save analysis to DB with full review result
resultJSON, _ := json.Marshal(reviewResult)
res, err := h.db.Exec(`INSERT INTO analyses (repo_id, type, base_ref, head_ref, result) VALUES (?, 'code_review', ?, ?, ?)`,
id, req.Base, req.Head, string(resultJSON))
if err == nil {
analysisID, _ := result.LastInsertId()
analysisID, _ := res.LastInsertId()
sendEvent("analysis_saved", map[string]interface{}{
"analysis_id": analysisID,
})
@@ -166,6 +161,69 @@ func (h *ReviewHandler) GetNotes(c *gin.Context) {
c.JSON(http.StatusOK, notes)
}
// ListReviews handles GET /api/repos/:id/review/analyses — list past code review analyses.
func (h *ReviewHandler) ListReviews(c *gin.Context) {
id := c.Param("id")
rows, err := h.db.Query(`SELECT id, base_ref, head_ref, result, created_at FROM analyses WHERE repo_id = ? AND type = 'code_review' ORDER BY created_at DESC`, id)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
defer rows.Close()
var analyses []map[string]interface{}
for rows.Next() {
var aid int64
var baseRef, headRef, result string
var createdAt string
if err := rows.Scan(&aid, &baseRef, &headRef, &result, &createdAt); err != nil {
continue
}
analyses = append(analyses, map[string]interface{}{
"id": aid,
"base_ref": baseRef,
"head_ref": headRef,
"created_at": createdAt,
})
}
if analyses == nil {
analyses = []map[string]interface{}{}
}
c.JSON(http.StatusOK, analyses)
}
// GetReview handles GET /api/repos/:id/review/analyses/:aid — get a single review with full result.
func (h *ReviewHandler) GetReview(c *gin.Context) {
aid := c.Param("aid")
var result, baseRef, headRef, createdAt string
err := h.db.QueryRow(`SELECT result, base_ref, head_ref, created_at FROM analyses WHERE id = ? AND type = 'code_review'`, aid).Scan(&result, &baseRef, &headRef, &createdAt)
if err == sql.ErrNoRows {
c.JSON(http.StatusNotFound, gin.H{"error": "analysis not found"})
return
}
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// Parse the result JSON to include structured data
var reviewResult interface{}
if err := json.Unmarshal([]byte(result), &reviewResult); err != nil {
reviewResult = result
}
c.JSON(http.StatusOK, gin.H{
"id": aid,
"base_ref": baseRef,
"head_ref": headRef,
"created_at": createdAt,
"result": reviewResult,
})
}
// GeneratePDF handles POST /api/repos/:id/review/pdf — generate and download a PDF report.
func (h *ReviewHandler) GeneratePDF(c *gin.Context) {
id := c.Param("id")