package store import ( "context" "fmt" "time" ) // Finding ist das Ergebnis einer Regel für einen Beitrag. Append-only: // siehe Migration. ExtractionID ist optional (nil wenn ein Finding nicht // direkt aus einer Extraktion, sondern z. B. manuell erzeugt wurde). type Finding struct { ID string SubmissionID string ExtractionID *string RuleID string RuleVersion int Severity string Message string CreatedAt time.Time } // CreateFinding speichert ein Finding. func (s *Store) CreateFinding(ctx context.Context, submissionID string, extractionID *string, ruleID string, ruleVersion int, severity, message string) (Finding, error) { var f Finding err := s.Pool.QueryRow(ctx, ` INSERT INTO finding (submission_id, extraction_id, rule_id, rule_version, severity, message) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id, submission_id, extraction_id, rule_id, rule_version, severity, message, created_at `, submissionID, extractionID, ruleID, ruleVersion, severity, message).Scan( &f.ID, &f.SubmissionID, &f.ExtractionID, &f.RuleID, &f.RuleVersion, &f.Severity, &f.Message, &f.CreatedAt, ) if err != nil { return Finding{}, fmt.Errorf("store: create finding: %w", err) } return f, nil } // ListCurrentFindings liefert die aktuell gültigen Findings eines // Beitrags — Zeilen, die von keiner anderen Zeile per supersedes // ersetzt wurden (siehe Migrationskommentar zu finding.supersedes). func (s *Store) ListCurrentFindings(ctx context.Context, submissionID string) ([]Finding, error) { rows, err := s.Pool.Query(ctx, ` SELECT f.id, f.submission_id, f.extraction_id, f.rule_id, f.rule_version, f.severity, f.message, f.created_at FROM finding f WHERE f.submission_id = $1 AND NOT EXISTS (SELECT 1 FROM finding f2 WHERE f2.supersedes = f.id) ORDER BY f.created_at `, submissionID) if err != nil { return nil, fmt.Errorf("store: list current findings: %w", err) } defer rows.Close() var findings []Finding for rows.Next() { var f Finding if err := rows.Scan(&f.ID, &f.SubmissionID, &f.ExtractionID, &f.RuleID, &f.RuleVersion, &f.Severity, &f.Message, &f.CreatedAt); err != nil { return nil, fmt.Errorf("store: scan finding: %w", err) } findings = append(findings, f) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("store: list current findings: %w", err) } return findings, nil }