Solucionado (ver solução)
Solucionado
(ver solução)
1
resposta

Bean Validation @Future Spring Boot

Olá,

Estou desenvolvendo uma API Rest que registra a permanência de hóspedes em um hotel. Tenho uma classe Hospedagem onde tem os atributos hospede, quarto, dataEntrada, dataSaida, valor. Quando vou fazer o check-in do hóspede, eu entro com o hóspede, quarto e a data de entrada (LocalDateTime, pois preciso registrar também o horário de entrada e saída) e estou validando os dados usando o Bean Validation.

Meu problema está na anotação @Future, pois não quero que o usuário "espertinho" registre um hóspede com data ou hora retroativa. Então no DTO DadosCheckin, no atributo dataEntrada eu adicionei a anotação @Future, mas se eu tentar adicionar uma anterior a de hoje, ele deixa. É necessário adicionar mais alguma informação além da anotação @Future?

Em relação a hora, a anotação @Future também faz esse tratamento ou só funciona com a data? Tipo agora é 13/10/2023 e são 10:07, se eu tentar incluir um hospede com a dada de entrada como 13/10/2023 as 10:25 (que teoricamente seria passado), a anotação @Future vai impedir? Aqui não está funcionando. Segue abaixo as minhas classes e DTOs:

Entidade Hospedagem:

package br.com.syshotel.api.domain.hospedagem;

import br.com.syshotel.api.domain.hospede.Hospede;
import br.com.syshotel.api.domain.quarto.Quarto;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;
import java.time.LocalDateTime;

@Table(name = "hospedagem")
@Entity(name = "Hospedagem")
@Getter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = "id")
public class Hospedagem {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "hospede_id")
    private Hospede hospede;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "quarto_id")
    private Quarto quarto;

    @Column(name = "data_entrada")
    private LocalDateTime dataEntrada;

    @Column(name = "data_saida")
    private  LocalDateTime dataSaida;

    private BigDecimal valor;


    public Hospedagem(LocalDateTime data, Hospede hospede, Quarto quarto) {
        this.hospede = hospede;
        this.quarto = quarto;
        this.dataEntrada = data;
    }
}

DTO Checkin

package br.com.syshotel.api.domain.hospedagem;

import br.com.syshotel.api.domain.hospede.Hospede;
import br.com.syshotel.api.domain.quarto.Quarto;
import jakarta.validation.constraints.Future;
import jakarta.validation.constraints.NotNull;
import lombok.NonNull;

import java.time.LocalDateTime;

public record DadosCheckIn(
        @NotNull
        Long hospedeId,

        @NotNull
        Long quartoId,

        @NotNull @Future
        LocalDateTime dataEntrada

) {
}

Hospedagem (Service)

package br.com.syshotel.api.domain.hospedagem;

import br.com.syshotel.api.domain.hospede.HospedeRepository;
import br.com.syshotel.api.domain.quarto.QuartoRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class HospedagemService {

    @Autowired
    private HospedagemRepository respository;

    @Autowired
    private HospedeRepository hospedeRepository;

    @Autowired
    private QuartoRepository quartoRepository;


    public  Hospedagem hospedar(DadosCheckIn dados) {
        var hospede = hospedeRepository.findById(dados.hospedeId()).get();
        var quarto = quartoRepository.findById(dados.quartoId()).get();
        var hospedagem = new Hospedagem(dados.dataEntrada(), hospede, quarto);
        respository.save(hospedagem);
        return hospedagem;
    }
}

Hospedagem Controller

package br.com.syshotel.api.controller;


import br.com.syshotel.api.domain.hospedagem.DadosCheckIn;
import br.com.syshotel.api.domain.hospedagem.DadosDetalharHospedagem;
import br.com.syshotel.api.domain.hospedagem.HospedagemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("hospedagem")
public class HospedagemController {
    @Autowired
    private HospedagemService service;


    @PostMapping
    @Transactional
    public ResponseEntity checkIn(@RequestBody DadosCheckIn dados){
        var hospedagem = service.hospedar(dados);
        return ResponseEntity.ok(new DadosDetalharHospedagem(hospedagem));
    }


}

Agradeço desde já.

1 resposta
solução!

Oi Damião, tudo bem?

Pelo que pude analisar em seu código, você está utilizando a anotação @Future corretamente no atributo dataEntrada do DTO DadosCheckIn. Essa anotação é responsável por validar se a data informada é uma data futura em relação à data atual.

No entanto, é importante ressaltar que a anotação @Future não leva em consideração o horário, apenas a data. Portanto, se você informar uma data futura, mas com um horário passado, a validação não será feita corretamente.

Para solucionar esse problema, você pode criar uma validação personalizada utilizando a anotação @ScriptAssert do Bean Validation. Com essa anotação, você pode escrever uma expressão em JavaScript que será executada durante a validação.

Aqui está um exemplo de como você pode utilizar a anotação @ScriptAssert para validar tanto a data quanto o horário:

import jakarta.validation.constraints.Future;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.ScriptAssert;
import lombok.NonNull;

import java.time.LocalDateTime;

@ScriptAssert(lang = "javascript", script = "_this.dataEntrada.isAfter(LocalDateTime.now())", message = "A data de entrada deve ser futura")
public record DadosCheckIn(
        @NotNull
        Long hospedeId,

        @NotNull
        Long quartoId,

        @NotNull @Future
        LocalDateTime dataEntrada

) {
}

Nesse exemplo, utilizamos a expressão _this.dataEntrada.isAfter(LocalDateTime.now()) para verificar se a data de entrada é posterior à data atual. Dessa forma, a validação será feita corretamente, considerando tanto a data quanto o horário.

Um abraço e bons estudos.