Files
bot-pontos/backend/internal/adapters/postgres/days.go
2026-10-23 22:31:02 -03:00

65 lines
2.3 KiB
Go

package postgres
import (
"context"
"botpontos/internal/domain"
)
const dayCols = `user_id, to_char(day, 'YYYY-MM-DD'), status, zero_point_checks, last_point_count,
next_check_at, resumed_by, updated_at`
func readDay(row scanner) (domain.UserDay, error) {
var d domain.UserDay
var status string
var resumed *string
if err := row.Scan(&d.UserID, &d.Day, &status, &d.ZeroPointChecks, &d.LastPointCount,
&d.NextCheckAt, &resumed, &d.UpdatedAt); err != nil {
return d, mapErr(err)
}
d.Status = domain.DayStatus(status)
if resumed != nil {
r := domain.ResumeSource(*resumed)
d.ResumedBy = &r
}
return d, nil
}
// GetUserDay devolve a situação do dia; found=false se o dia ainda não foi consultado.
func (s *Store) GetUserDay(ctx context.Context, userID int64, day string) (domain.UserDay, bool, error) {
d, err := readDay(s.pool.QueryRow(ctx, `SELECT `+dayCols+` FROM user_days WHERE user_id = $1 AND day = $2::text::date`, userID, day))
if err == domain.ErrNotFound {
return domain.NewUserDay(userID, day), false, nil
}
return d, err == nil, err
}
func (s *Store) SaveUserDay(ctx context.Context, d domain.UserDay) error {
var resumed *string
if d.ResumedBy != nil {
v := string(*d.ResumedBy)
resumed = &v
}
_, err := s.pool.Exec(ctx, `
INSERT INTO user_days (user_id, day, status, zero_point_checks, last_point_count, next_check_at, resumed_by)
VALUES ($1, $2::text::date, $3::text::day_status, $4, $5, $6, $7)
ON CONFLICT (user_id, day) DO UPDATE SET
status = EXCLUDED.status,
zero_point_checks = EXCLUDED.zero_point_checks,
last_point_count = EXCLUDED.last_point_count,
next_check_at = EXCLUDED.next_check_at,
resumed_by = EXCLUDED.resumed_by`,
d.UserID, d.Day, string(d.Status), d.ZeroPointChecks, d.LastPointCount, d.NextCheckAt, resumed)
return mapErr(err)
}
// ResetNextChecks antecipa as próximas consultas do dia para agora; usado quando a
// janela muda (9.13), para a reconciliação recalcular tudo no próximo tick. Inclui
// o dia anterior para cobrir usuários em fusos diferentes perto da meia-noite.
func (s *Store) ResetNextChecks(ctx context.Context, day string) error {
_, err := s.pool.Exec(ctx, `
UPDATE user_days SET next_check_at = now()
WHERE day >= $1::text::date - 1 AND next_check_at IS NOT NULL`, day)
return err
}