Files
sub2api-cn-relay-manager/internal/store/sqlite/route_failover_events_repo.go
2026-05-28 21:24:05 +08:00

164 lines
4.5 KiB
Go

package sqlite
import (
"context"
"fmt"
"strings"
)
type RouteFailoverEvent struct {
ID int64
RequestID string
LogicalGroupID string
PublicModel string
FromRouteID string
ToRouteID string
Reason string
FailureCount int
CreatedAt string
}
type RouteFailoverEventFilter struct {
RequestID string
LogicalGroupID string
PublicModel string
FromRouteID string
ToRouteID string
Limit int
}
type RouteFailoverEventsRepo struct {
db execQuerier
}
func newRouteFailoverEventsRepo(db execQuerier) *RouteFailoverEventsRepo {
return &RouteFailoverEventsRepo{db: db}
}
func (r *RouteFailoverEventsRepo) Create(ctx context.Context, row RouteFailoverEvent) (int64, error) {
row, err := normalizeRouteFailoverEvent(row)
if err != nil {
return 0, err
}
result, err := r.db.ExecContext(
ctx,
`INSERT INTO route_failover_events (
request_id,
logical_group_id,
public_model,
from_route_id,
to_route_id,
reason,
failure_count
) VALUES (?, ?, ?, ?, ?, ?, ?)`,
row.RequestID,
row.LogicalGroupID,
row.PublicModel,
row.FromRouteID,
row.ToRouteID,
row.Reason,
row.FailureCount,
)
if err != nil {
return 0, fmt.Errorf("insert route failover event %q: %w", row.RequestID, err)
}
id, err := result.LastInsertId()
if err != nil {
return 0, fmt.Errorf("read inserted route failover event id for %q: %w", row.RequestID, err)
}
return id, nil
}
func (r *RouteFailoverEventsRepo) ListRecent(ctx context.Context, filter RouteFailoverEventFilter) ([]RouteFailoverEvent, error) {
clauses := make([]string, 0, 5)
args := make([]any, 0, 6)
if requestID := strings.TrimSpace(filter.RequestID); requestID != "" {
clauses = append(clauses, "request_id = ?")
args = append(args, requestID)
}
if logicalGroupID := strings.TrimSpace(filter.LogicalGroupID); logicalGroupID != "" {
clauses = append(clauses, "logical_group_id = ?")
args = append(args, logicalGroupID)
}
if publicModel := strings.TrimSpace(filter.PublicModel); publicModel != "" {
clauses = append(clauses, "public_model = ?")
args = append(args, publicModel)
}
if fromRouteID := strings.TrimSpace(filter.FromRouteID); fromRouteID != "" {
clauses = append(clauses, "from_route_id = ?")
args = append(args, fromRouteID)
}
if toRouteID := strings.TrimSpace(filter.ToRouteID); toRouteID != "" {
clauses = append(clauses, "to_route_id = ?")
args = append(args, toRouteID)
}
query := `SELECT id, request_id, logical_group_id, public_model, from_route_id, to_route_id, reason, failure_count, created_at
FROM route_failover_events`
if len(clauses) > 0 {
query += " WHERE " + strings.Join(clauses, " AND ")
}
query += " ORDER BY id DESC LIMIT ?"
args = append(args, normalizeRouteLogListLimit(filter.Limit))
rows, err := r.db.QueryContext(ctx, query, args...)
if err != nil {
return nil, fmt.Errorf("list route failover events: %w", err)
}
defer rows.Close()
items := make([]RouteFailoverEvent, 0)
for rows.Next() {
var item RouteFailoverEvent
if err := rows.Scan(
&item.ID,
&item.RequestID,
&item.LogicalGroupID,
&item.PublicModel,
&item.FromRouteID,
&item.ToRouteID,
&item.Reason,
&item.FailureCount,
&item.CreatedAt,
); err != nil {
return nil, fmt.Errorf("scan route failover event: %w", err)
}
items = append(items, item)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("iterate route failover events: %w", err)
}
return items, nil
}
func normalizeRouteFailoverEvent(row RouteFailoverEvent) (RouteFailoverEvent, error) {
row.RequestID = strings.TrimSpace(row.RequestID)
row.LogicalGroupID = strings.TrimSpace(row.LogicalGroupID)
row.PublicModel = strings.TrimSpace(row.PublicModel)
row.FromRouteID = strings.TrimSpace(row.FromRouteID)
row.ToRouteID = strings.TrimSpace(row.ToRouteID)
row.Reason = strings.TrimSpace(row.Reason)
switch {
case row.RequestID == "":
return RouteFailoverEvent{}, fmt.Errorf("request_id is required")
case row.LogicalGroupID == "":
return RouteFailoverEvent{}, fmt.Errorf("logical_group_id is required")
case row.PublicModel == "":
return RouteFailoverEvent{}, fmt.Errorf("public_model is required")
case row.FromRouteID == "":
return RouteFailoverEvent{}, fmt.Errorf("from_route_id is required")
case row.ToRouteID == "":
return RouteFailoverEvent{}, fmt.Errorf("to_route_id is required")
case row.Reason == "":
return RouteFailoverEvent{}, fmt.Errorf("reason is required")
case row.FailureCount < 0:
return RouteFailoverEvent{}, fmt.Errorf("failure_count must be >= 0")
}
return row, nil
}