feat(repo): enhance MoveToArchive functionality to handle duplicates and improve error handling

This commit is contained in:
Nik Afiq 2025-12-11 21:43:50 +09:00
parent c4ac2ed128
commit a0db346ab7

View File

@ -3,6 +3,7 @@ package repo
import ( import (
"context" "context"
"errors" "errors"
"time"
"watch-party-backend/internal/core/episode" "watch-party-backend/internal/core/episode"
@ -140,69 +141,90 @@ func (r *pgxEpisodeRepo) MoveToArchive(ctx context.Context, ids []int64) (episod
return res, nil return res, nil
} }
requested := make(map[int64]struct{}, len(ids))
for _, id := range ids {
requested[id] = struct{}{}
}
tx, err := r.pool.Begin(ctx) tx, err := r.pool.Begin(ctx)
if err != nil { if err != nil {
return res, err return res, err
} }
defer func() { _ = tx.Rollback(ctx) }() defer func() { _ = tx.Rollback(ctx) }()
// 1) Insert into archive from current (skip duplicates by id) // Grab the rows we need to archive.
insRows, err := tx.Query(ctx, ` rows, err := tx.Query(ctx, `
INSERT INTO current_archive (
id, ep_num, ep_title, season_name, start_time, playback_length, current_ep, date_created
)
SELECT id, ep_num, ep_title, season_name, start_time, playback_length, current_ep, date_created SELECT id, ep_num, ep_title, season_name, start_time, playback_length, current_ep, date_created
FROM current FROM current
WHERE id = ANY($1::bigint[]) WHERE id = ANY($1::bigint[])
ON CONFLICT (id) DO NOTHING
RETURNING id
`, ids) `, ids)
if err != nil { if err != nil {
return res, err return res, err
} }
for insRows.Next() { defer rows.Close()
var id int64
if err := insRows.Scan(&id); err != nil { for rows.Next() {
return res, err var (
} id int64
res.MovedIDs = append(res.MovedIDs, id) epNum int
} epTitle string
if err := insRows.Err(); err != nil { seasonName string
startTime string
playback string
currentEp bool
dateCreated time.Time
archivedID int64
inserted bool
)
if err := rows.Scan(&id, &epNum, &epTitle, &seasonName, &startTime, &playback, &currentEp, &dateCreated); err != nil {
return res, err return res, err
} }
// 2) Delete from current only those actually inserted to archive // First try to insert with the same id; on conflict, insert with a new id.
if len(res.MovedIDs) > 0 { err = tx.QueryRow(ctx, `
delRows, err := tx.Query(ctx, ` INSERT INTO current_archive (
DELETE FROM current id, ep_num, ep_title, season_name, start_time, playback_length, current_ep, date_created
WHERE id = ANY($1::bigint[]) )
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
ON CONFLICT (id) DO NOTHING
RETURNING id RETURNING id
`, res.MovedIDs) `, id, epNum, epTitle, seasonName, startTime, playback, currentEp, dateCreated).Scan(&archivedID)
if err != nil { if err != nil && !errors.Is(err, pgx.ErrNoRows) {
return res, err return res, err
} }
for delRows.Next() { if err == nil {
var id int64 inserted = true
if err := delRows.Scan(&id); err != nil {
return res, err
} }
res.DeletedIDs = append(res.DeletedIDs, id)
} if !inserted {
if err := delRows.Err(); err != nil { if err := tx.QueryRow(ctx, `
INSERT INTO current_archive (
id, ep_num, ep_title, season_name, start_time, playback_length, current_ep, date_created
)
VALUES (nextval(pg_get_serial_sequence('current','id')), $1, $2, $3, $4, $5, $6, $7)
RETURNING id
`, epNum, epTitle, seasonName, startTime, playback, currentEp, dateCreated).Scan(&archivedID); err != nil {
return res, err return res, err
} }
} }
// 3) Compute skipped = requested - moved // Delete from current using the original id.
sel := make(map[int64]struct{}, len(res.MovedIDs)) if _, err := tx.Exec(ctx, `DELETE FROM current WHERE id = $1`, id); err != nil {
for _, id := range res.MovedIDs { return res, err
sel[id] = struct{}{}
} }
for _, id := range ids {
if _, ok := sel[id]; !ok { res.MovedIDs = append(res.MovedIDs, id)
res.DeletedIDs = append(res.DeletedIDs, id)
delete(requested, id)
}
if err := rows.Err(); err != nil {
return res, err
}
// Any ids not found are treated as skipped.
for id := range requested {
res.SkippedIDs = append(res.SkippedIDs, id) res.SkippedIDs = append(res.SkippedIDs, id)
} }
}
if err := tx.Commit(ctx); err != nil { if err := tx.Commit(ctx); err != nil {
return res, err return res, err