feat: Phase 5 — 审查备注编辑器与 PDF 导出
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/HoHD/PR-Helper/models"
|
||||
)
|
||||
|
||||
// SaveNote upserts a review note (insert or update if exists for the same analysis_id + scope + scope_key).
|
||||
func SaveNote(db *sql.DB, analysisID int64, scope, scopeKey, content string) (*models.ReviewNote, error) {
|
||||
now := time.Now().UTC()
|
||||
|
||||
// Try to find existing note
|
||||
var existingID int64
|
||||
err := db.QueryRow(`SELECT id FROM review_notes WHERE analysis_id = ? AND scope = ? AND scope_key = ?`,
|
||||
analysisID, scope, scopeKey).Scan(&existingID)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
// Insert new
|
||||
result, err := db.Exec(`INSERT INTO review_notes (analysis_id, scope, scope_key, content, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)`,
|
||||
analysisID, scope, scopeKey, content, now, now)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("insert note: %w", err)
|
||||
}
|
||||
id, _ := result.LastInsertId()
|
||||
return &models.ReviewNote{
|
||||
ID: id,
|
||||
AnalysisID: analysisID,
|
||||
Scope: scope,
|
||||
ScopeKey: scopeKey,
|
||||
Content: content,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query existing note: %w", err)
|
||||
}
|
||||
|
||||
// Update existing
|
||||
_, err = db.Exec(`UPDATE review_notes SET content = ?, updated_at = ? WHERE id = ?`, content, now, existingID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("update note: %w", err)
|
||||
}
|
||||
|
||||
return &models.ReviewNote{
|
||||
ID: existingID,
|
||||
AnalysisID: analysisID,
|
||||
Scope: scope,
|
||||
ScopeKey: scopeKey,
|
||||
Content: content,
|
||||
UpdatedAt: now,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetNotes returns all review notes for a given analysis, optionally filtered by scope.
|
||||
func GetNotes(db *sql.DB, analysisID int64, scope string) ([]models.ReviewNote, error) {
|
||||
var rows *sql.Rows
|
||||
var err error
|
||||
|
||||
if scope != "" {
|
||||
rows, err = db.Query(`SELECT id, analysis_id, scope, scope_key, content, created_at, updated_at FROM review_notes WHERE analysis_id = ? AND scope = ? ORDER BY id`,
|
||||
analysisID, scope)
|
||||
} else {
|
||||
rows, err = db.Query(`SELECT id, analysis_id, scope, scope_key, content, created_at, updated_at FROM review_notes WHERE analysis_id = ? ORDER BY id`,
|
||||
analysisID)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query notes: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var notes []models.ReviewNote
|
||||
for rows.Next() {
|
||||
var n models.ReviewNote
|
||||
if err := rows.Scan(&n.ID, &n.AnalysisID, &n.Scope, &n.ScopeKey, &n.Content, &n.CreatedAt, &n.UpdatedAt); err != nil {
|
||||
continue
|
||||
}
|
||||
notes = append(notes, n)
|
||||
}
|
||||
return notes, nil
|
||||
}
|
||||
|
||||
// DeleteNote deletes a specific review note by ID.
|
||||
func DeleteNote(db *sql.DB, noteID int64) error {
|
||||
_, err := db.Exec(`DELETE FROM review_notes WHERE id = ?`, noteID)
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user