6
respostas

Exception na hora de agendar a consulta

Não consigo fazer o agendamento da consulta, porque dá essa exception:

2023-05-16T11:14:15.661-03:00 ERROR 14400 --- [nio-8080-exec-6] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.dao.InvalidDataAccessApiUsageException: Argument [1] of type [java.lang.Long] did not match parameter type [med.voll.api.domain.medico.Medico (n/a)]] with root cause

java.lang.IllegalArgumentException: Argument [1] of type [java.lang.Long] did not match parameter type [med.voll.api.domain.medico.Medico (n/a)]

Quando eu tento pelo modo sem inserir o id aparece essa outra exception:

2023-05-16T11:26:12.378-03:00 ERROR 14400 --- [nio-8080-exec-9] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.dao.InvalidDataAccessApiUsageException: Parameter value [2023-10-10T10:00] did not match expected type [BasicSqmPathSource(id : Long) ]] with root cause

org.hibernate.type.descriptor.java.CoercionException: Cannot coerce value `2023-10-10T10:00` [java.time.LocalDateTime] as Long
    at org.hibernate.type.descriptor.java.LongJavaType.coerce(LongJavaType.java:155) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.type.descriptor.java.LongJavaType.coerce(LongJavaType.java:24) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]

Em ambas requisições o erro consta como 403 Forbidden, mesmo usando o token correto. Por que isso está acontecendo?

6 respostas

Oi Rodrigo!

O erro é por conta de algum parâmetro inválido. Manda aqui o json que você está enviando na requisição e também o código do seu record

O Json que eu enviei foi:

{
    "idPaciente": 1,
    "idMedico": 1,
    "data": "2023-10-10T10:00"
}

Os códigos dos records são esses:

package med.voll.api.domain.consulta;

import jakarta.validation.constraints.Future;
import jakarta.validation.constraints.NotNull;
import med.voll.api.domain.medico.Especialidade;

import java.time.LocalDateTime;

public record DadosAgendamentoConsulta(
        Long idMedico,

        @NotNull
        Long idPaciente,

        @NotNull
        @Future
        LocalDateTime data,

        Especialidade especialidade) {
}
package med.voll.api.domain.consulta;

import java.time.LocalDateTime;

public record DadosDetalhamentoConsulta(Long id, Long idMedico, Long idPaciente, LocalDateTime data) {
    public DadosDetalhamentoConsulta(Consulta consulta) {
        this(consulta.getId(), consulta.getMedico().getId(), consulta.getPaciente().getId(), consulta.getData());
    }
}

Blz. Manda aqui também o código da sua classe Service e do MedicoRepository

A Classe Service:

@Service
public class AgendaDeConsultas {
    @Autowired
    private ConsultaRepository consultaRepository;

    @Autowired
    private MedicoRepository medicoRepository;
    @Autowired
    private PacienteRepository pacienteRepository;

    @Autowired
    List<ValidadorAgendamentoDeConsulta> validadores;

    @Autowired
    private List<ValidadorCancelamentoDeConsulta> validadoresCancelamento;

    public DadosDetalhamentoConsulta agendar(DadosAgendamentoConsulta dados) {
        if (!pacienteRepository.existsById(dados.idPaciente())) {
            throw new ValidacaoException("Id do paciente informado não existe!");
        }

        if (dados.idMedico() != null && !medicoRepository.existsById(dados.idMedico())) {
            throw new ValidacaoException("Id do médico informado não existe!");
        }

        validadores.forEach(v -> v.validar(dados));

        var paciente = pacienteRepository.getReferenceById(dados.idPaciente());
        var medico = escolherMedico(dados);
        if (medico == null) {
            throw new ValidacaoException("Não existe médico disponível nessa data!");
        }

        var consulta = new Consulta(null, medico, paciente, dados.data(), null);
        consultaRepository.save(consulta);

        return new DadosDetalhamentoConsulta(consulta);
    }


    private Medico escolherMedico(DadosAgendamentoConsulta dados) {
        if(dados.idMedico() != null){
            return medicoRepository.getReferenceById(dados.idMedico());
        } if(dados.especialidade() == null){
            throw new ValidacaoException("Especialidade obrigatória!");
        }
        return medicoRepository.escolherMedicoAleatorioLivreNaData(dados.especialidade(), dados.data());
    }

    public void cancelar(DadosCancelamentoConsulta dados) {
        if (!consultaRepository.existsById(dados.idConsulta())) {
            throw new ValidacaoException("Id da consulta informado não existe!");
        }

        validadoresCancelamento.forEach(v -> v.validar(dados));


        var consulta = consultaRepository.getReferenceById(dados.idConsulta());
        consulta.cancelar(dados.motivo());
    }
}

A classe MedicoRepository:

public interface MedicoRepository extends JpaRepository<Medico, Long> {

    Page<Medico> findAllByAtivoTrue(Pageable paginacao);


    @Query("""
            select m from Medico m
            where
            m.ativo = 1
            and
            m.especialidade = :especialidade
            and
            m.id not in(
                select c.medico.id from Consulta c
                where
                c.data = :data
        and
                c.motivoCancelamento is null
            )
            order by rand()
            limit 1
       """)
    Medico escolherMedicoAleatorioLivreNaData(Especialidade especialidade, LocalDateTime data);

    @Query("""
            select m.ativo
            from Medico m
            where
            m.id = :id
            """)
    Boolean findAtivoById(Long id);
}

A princípio tudo certinho. Manda aqui a exception completa para identificação do ponto exato onde o erro acontece.

"java.lang.IllegalArgumentException: Argument [1] of type [java.lang.Long] did not match parameter type [med.voll.api.domain.medico.Medico (n/a)]"

Esse retorno diz que o argumento [1] que você está enviando no Json não corresponde a Medico. Ou seja, em algum lugar da sua API está sendo passado para algum método um parâmetro do tipo Long, mas esse método recebe um tipo Medico. Porém, nós não fazemos uso direto das entidades, usamos DTOs, não faz muito sentido ter algum parâmetro recebendo diretamenta a entidade Medico.

Como está o seu ConsultaRepository? Talvez o seu metodo "existsByMedicoIdAndData" esteja formatado sem a palavra chave "Id", assim:

//Errado
existsByMedicoAndData(Long idMedico, LocalDateTime data); 

// Correto
existsByMedicoIdAndData(Long idMedico, LocalDateTime data); 

Se ele estiver sem a palavra "id" na formatação do nome do metodo, o Spring mantará uma query usando uma entidade Medico que será passada no parâmetro do método, por isso que há essa incompatibilidade de Long, pois a query não está esperando um Long de id, está esperando um Medico. Adicionando "Id" depois de "Medico" ela passa a receber um Long como parâmetro.

Espero que tenha funcionado