| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- package repository
- import (
- "context"
- "database/sql"
- "time"
- "github.com/Wei-Shaw/sub2api/internal/service"
- )
- // --- Plan Repository ---
- type scheduledTestPlanRepository struct {
- db *sql.DB
- }
- func NewScheduledTestPlanRepository(db *sql.DB) service.ScheduledTestPlanRepository {
- return &scheduledTestPlanRepository{db: db}
- }
- func (r *scheduledTestPlanRepository) Create(ctx context.Context, plan *service.ScheduledTestPlan) (*service.ScheduledTestPlan, error) {
- row := r.db.QueryRowContext(ctx, `
- INSERT INTO scheduled_test_plans (account_id, model_id, cron_expression, enabled, max_results, auto_recover, next_run_at, created_at, updated_at)
- VALUES ($1, $2, $3, $4, $5, $6, $7, NOW(), NOW())
- RETURNING id, account_id, model_id, cron_expression, enabled, max_results, auto_recover, last_run_at, next_run_at, created_at, updated_at
- `, plan.AccountID, plan.ModelID, plan.CronExpression, plan.Enabled, plan.MaxResults, plan.AutoRecover, plan.NextRunAt)
- return scanPlan(row)
- }
- func (r *scheduledTestPlanRepository) GetByID(ctx context.Context, id int64) (*service.ScheduledTestPlan, error) {
- row := r.db.QueryRowContext(ctx, `
- SELECT id, account_id, model_id, cron_expression, enabled, max_results, auto_recover, last_run_at, next_run_at, created_at, updated_at
- FROM scheduled_test_plans WHERE id = $1
- `, id)
- return scanPlan(row)
- }
- func (r *scheduledTestPlanRepository) ListByAccountID(ctx context.Context, accountID int64) ([]*service.ScheduledTestPlan, error) {
- rows, err := r.db.QueryContext(ctx, `
- SELECT id, account_id, model_id, cron_expression, enabled, max_results, auto_recover, last_run_at, next_run_at, created_at, updated_at
- FROM scheduled_test_plans WHERE account_id = $1
- ORDER BY created_at DESC
- `, accountID)
- if err != nil {
- return nil, err
- }
- defer func() { _ = rows.Close() }()
- return scanPlans(rows)
- }
- func (r *scheduledTestPlanRepository) ListDue(ctx context.Context, now time.Time) ([]*service.ScheduledTestPlan, error) {
- rows, err := r.db.QueryContext(ctx, `
- SELECT id, account_id, model_id, cron_expression, enabled, max_results, auto_recover, last_run_at, next_run_at, created_at, updated_at
- FROM scheduled_test_plans
- WHERE enabled = true AND next_run_at <= $1
- ORDER BY next_run_at ASC
- `, now)
- if err != nil {
- return nil, err
- }
- defer func() { _ = rows.Close() }()
- return scanPlans(rows)
- }
- func (r *scheduledTestPlanRepository) Update(ctx context.Context, plan *service.ScheduledTestPlan) (*service.ScheduledTestPlan, error) {
- row := r.db.QueryRowContext(ctx, `
- UPDATE scheduled_test_plans
- SET model_id = $2, cron_expression = $3, enabled = $4, max_results = $5, auto_recover = $6, next_run_at = $7, updated_at = NOW()
- WHERE id = $1
- RETURNING id, account_id, model_id, cron_expression, enabled, max_results, auto_recover, last_run_at, next_run_at, created_at, updated_at
- `, plan.ID, plan.ModelID, plan.CronExpression, plan.Enabled, plan.MaxResults, plan.AutoRecover, plan.NextRunAt)
- return scanPlan(row)
- }
- func (r *scheduledTestPlanRepository) Delete(ctx context.Context, id int64) error {
- _, err := r.db.ExecContext(ctx, `DELETE FROM scheduled_test_plans WHERE id = $1`, id)
- return err
- }
- func (r *scheduledTestPlanRepository) UpdateAfterRun(ctx context.Context, id int64, lastRunAt time.Time, nextRunAt time.Time) error {
- _, err := r.db.ExecContext(ctx, `
- UPDATE scheduled_test_plans SET last_run_at = $2, next_run_at = $3, updated_at = NOW() WHERE id = $1
- `, id, lastRunAt, nextRunAt)
- return err
- }
- // --- Result Repository ---
- type scheduledTestResultRepository struct {
- db *sql.DB
- }
- func NewScheduledTestResultRepository(db *sql.DB) service.ScheduledTestResultRepository {
- return &scheduledTestResultRepository{db: db}
- }
- func (r *scheduledTestResultRepository) Create(ctx context.Context, result *service.ScheduledTestResult) (*service.ScheduledTestResult, error) {
- row := r.db.QueryRowContext(ctx, `
- INSERT INTO scheduled_test_results (plan_id, status, response_text, error_message, latency_ms, started_at, finished_at, created_at)
- VALUES ($1, $2, $3, $4, $5, $6, $7, NOW())
- RETURNING id, plan_id, status, response_text, error_message, latency_ms, started_at, finished_at, created_at
- `, result.PlanID, result.Status, result.ResponseText, result.ErrorMessage, result.LatencyMs, result.StartedAt, result.FinishedAt)
- out := &service.ScheduledTestResult{}
- if err := row.Scan(
- &out.ID, &out.PlanID, &out.Status, &out.ResponseText, &out.ErrorMessage,
- &out.LatencyMs, &out.StartedAt, &out.FinishedAt, &out.CreatedAt,
- ); err != nil {
- return nil, err
- }
- return out, nil
- }
- func (r *scheduledTestResultRepository) ListByPlanID(ctx context.Context, planID int64, limit int) ([]*service.ScheduledTestResult, error) {
- rows, err := r.db.QueryContext(ctx, `
- SELECT id, plan_id, status, response_text, error_message, latency_ms, started_at, finished_at, created_at
- FROM scheduled_test_results
- WHERE plan_id = $1
- ORDER BY created_at DESC
- LIMIT $2
- `, planID, limit)
- if err != nil {
- return nil, err
- }
- defer func() { _ = rows.Close() }()
- var results []*service.ScheduledTestResult
- for rows.Next() {
- r := &service.ScheduledTestResult{}
- if err := rows.Scan(
- &r.ID, &r.PlanID, &r.Status, &r.ResponseText, &r.ErrorMessage,
- &r.LatencyMs, &r.StartedAt, &r.FinishedAt, &r.CreatedAt,
- ); err != nil {
- return nil, err
- }
- results = append(results, r)
- }
- return results, rows.Err()
- }
- func (r *scheduledTestResultRepository) PruneOldResults(ctx context.Context, planID int64, keepCount int) error {
- _, err := r.db.ExecContext(ctx, `
- DELETE FROM scheduled_test_results
- WHERE id IN (
- SELECT id FROM (
- SELECT id, ROW_NUMBER() OVER (PARTITION BY plan_id ORDER BY created_at DESC) AS rn
- FROM scheduled_test_results
- WHERE plan_id = $1
- ) ranked
- WHERE rn > $2
- )
- `, planID, keepCount)
- return err
- }
- // --- scan helpers ---
- type scannable interface {
- Scan(dest ...any) error
- }
- func scanPlan(row scannable) (*service.ScheduledTestPlan, error) {
- p := &service.ScheduledTestPlan{}
- if err := row.Scan(
- &p.ID, &p.AccountID, &p.ModelID, &p.CronExpression, &p.Enabled, &p.MaxResults, &p.AutoRecover,
- &p.LastRunAt, &p.NextRunAt, &p.CreatedAt, &p.UpdatedAt,
- ); err != nil {
- return nil, err
- }
- return p, nil
- }
- func scanPlans(rows *sql.Rows) ([]*service.ScheduledTestPlan, error) {
- var plans []*service.ScheduledTestPlan
- for rows.Next() {
- p, err := scanPlan(rows)
- if err != nil {
- return nil, err
- }
- plans = append(plans, p)
- }
- return plans, rows.Err()
- }
|