diff --git a/backend/internal/repository/user_repo.go b/backend/internal/repository/user_repo.go index b2190b68..7378611d 100644 --- a/backend/internal/repository/user_repo.go +++ b/backend/internal/repository/user_repo.go @@ -19,6 +19,7 @@ import ( "github.com/Wei-Shaw/sub2api/ent/usersubscription" "github.com/Wei-Shaw/sub2api/internal/pkg/pagination" "github.com/Wei-Shaw/sub2api/internal/service" + "github.com/lib/pq" entsql "entgo.io/ent/dialect/sql" ) @@ -298,6 +299,51 @@ func normalizeEmailAuthIdentitySubject(email string) string { return normalized } +func (r *userRepository) GetLatestUsedAtByUserIDs(ctx context.Context, userIDs []int64) (map[int64]*time.Time, error) { + result := make(map[int64]*time.Time, len(userIDs)) + if len(userIDs) == 0 { + return result, nil + } + if r.sql == nil { + return nil, fmt.Errorf("sql executor is not configured") + } + + rows, err := r.sql.QueryContext(ctx, ` + SELECT user_id, MAX(created_at) AS last_used_at + FROM usage_logs + WHERE user_id = ANY($1) + GROUP BY user_id + `, pq.Array(userIDs)) + if err != nil { + return nil, err + } + defer func() { _ = rows.Close() }() + + for rows.Next() { + var ( + userID int64 + lastUsedAt time.Time + ) + if err := rows.Scan(&userID, &lastUsedAt); err != nil { + return nil, err + } + ts := lastUsedAt.UTC() + result[userID] = &ts + } + if err := rows.Err(); err != nil { + return nil, err + } + return result, nil +} + +func (r *userRepository) GetLatestUsedAtByUserID(ctx context.Context, userID int64) (*time.Time, error) { + latestByUserID, err := r.GetLatestUsedAtByUserIDs(ctx, []int64{userID}) + if err != nil { + return nil, err + } + return latestByUserID[userID], nil +} + func (r *userRepository) Delete(ctx context.Context, id int64) error { affected, err := r.client.User.Delete().Where(dbuser.IDEQ(id)).Exec(ctx) if err != nil {