Skip to main content

Relacionamento Personal-Aluno

O relacionamento entre personal trainers e alunos é um componente central do FitLocus, permitindo que profissionais de educação física gerenciem seus clientes e criem treinos personalizados para cada um deles.

Visão Geral

O FitLocus implementa um sistema de relacionamento entre personal trainers e alunos que permite:

  • Vinculação segura entre profissionais e seus clientes
  • Controle de acesso baseado nesse relacionamento
  • Atribuição de treinos personalizados
  • Acompanhamento de progresso individualizado
  • Comunicação direta entre as partes

Modelo de Dados

O relacionamento é implementado através da entidade PersonalStudent, que estabelece uma relação muitos-para-muitos entre usuários do tipo PERSONAL e usuários do tipo ALUNO:
@Entity
@Table(name = "personal_student")
public class PersonalStudent {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name = "personal_id")
    private Long personalId;
    
    @Column(name = "student_id")
    private Long studentId;
    
    @Column(name = "created_at")
    private LocalDateTime createdAt;
    
    @Column(name = "status")
    @Enumerated(EnumType.STRING)
    private RelationshipStatus status;
    
    // Getters e setters...
}
O status do relacionamento pode ser:
public enum RelationshipStatus {
    PENDING,    // Convite enviado, aguardando aceitação
    ACTIVE,     // Relacionamento ativo
    INACTIVE    // Relacionamento pausado ou encerrado
}

Fluxo de Vinculação

1. Convite do Personal Trainer

O personal trainer inicia o processo enviando um convite para o aluno:
@PostMapping("/invite")
public ResponseEntity<PersonalStudentDTO> inviteStudent(@RequestBody InviteRequest request) {
    // Validação e criação do relacionamento com status PENDING
}
O convite pode ser enviado de duas formas:
  • Para um usuário já cadastrado no sistema (via email)
  • Para um novo usuário (gerando um link de convite)

2. Aceitação pelo Aluno

O aluno recebe o convite e pode aceitá-lo:
@PostMapping("/accept-invitation/{invitationId}")
public ResponseEntity<PersonalStudentDTO> acceptInvitation(@PathVariable Long invitationId) {
    // Atualização do status para ACTIVE
}

3. Gerenciamento do Relacionamento

Após estabelecido, o relacionamento pode ser gerenciado por ambas as partes:
@PutMapping("/{relationshipId}/status")
public ResponseEntity<PersonalStudentDTO> updateStatus(
        @PathVariable Long relationshipId,
        @RequestBody StatusUpdateRequest request) {
    // Atualização do status (ACTIVE ou INACTIVE)
}

Segurança e Controle de Acesso

Um aspecto crítico do sistema é garantir que personal trainers só possam acessar dados de seus próprios alunos. Isso é implementado através de validações em todas as operações:
// Exemplo de validação de acesso
public boolean canAccessStudentData(Long personalId, Long studentId) {
    return personalStudentRepository.existsByPersonalIdAndStudentIdAndStatus(
        personalId, 
        studentId, 
        RelationshipStatus.ACTIVE
    );
}
Nas consultas ao banco de dados, todas as operações que envolvem dados de alunos devem incluir um filtro pelo relacionamento:
// Exemplo de consulta segura
@GetMapping("/students/{studentId}/trainings")
public ResponseEntity<List<TrainingDTO>> getStudentTrainings(
        @PathVariable Long studentId,
        @AuthenticationPrincipal UserDetails userDetails) {
    
    Long personalId = userService.getUserIdFromUserDetails(userDetails);
    
    // Verificação de relacionamento
    if (!personalStudentService.canAccessStudentData(personalId, studentId)) {
        return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
    }
    
    // Busca de treinos
    List<TrainingDTO> trainings = trainingService.getTrainingsByStudentId(studentId);
    return ResponseEntity.ok(trainings);
}

Limites de Alunos por Personal

O número de alunos que um personal trainer pode gerenciar é limitado pelo seu tipo de assinatura:
PlanoLimite de Alunos
PERSONAL_INICIANTE10
PERSONAL_BASIC50
PERSONAL_INTERMEDIARIO100
PERSONAL_AVANCADO200
PERSONAL_ILIMITADOIlimitado
O sistema implementa validações para garantir que esses limites sejam respeitados:
// Exemplo de validação de limite
public boolean canAddMoreStudents(Long personalId) {
    User personal = userRepository.findById(personalId)
        .orElseThrow(() -> new ResourceNotFoundException("Personal not found"));
    
    int currentStudentCount = personalStudentRepository
        .countByPersonalIdAndStatus(personalId, RelationshipStatus.ACTIVE);
    
    return switch (personal.getSubscriptionType()) {
        case PERSONAL_INICIANTE -> currentStudentCount < 10;
        case PERSONAL_BASIC -> currentStudentCount < 50;
        case PERSONAL_INTERMEDIARIO -> currentStudentCount < 100;
        case PERSONAL_AVANCADO -> currentStudentCount < 200;
        case PERSONAL_ILIMITADO -> true;
        default -> false;
    };
}

Atribuição de Treinos

O relacionamento personal-aluno é a base para a atribuição de treinos personalizados:
@PostMapping("/students/{studentId}/assign-training")
public ResponseEntity<TrainingAssignmentDTO> assignTraining(
        @PathVariable Long studentId,
        @RequestBody TrainingAssignmentRequest request,
        @AuthenticationPrincipal UserDetails userDetails) {
    
    Long personalId = userService.getUserIdFromUserDetails(userDetails);
    
    // Verificação de relacionamento
    if (!personalStudentService.canAccessStudentData(personalId, studentId)) {
        return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
    }
    
    // Atribuição do treino
    TrainingAssignmentDTO assignment = trainingService.assignTrainingToStudent(
        request.getTrainingId(), 
        studentId, 
        request.getScheduledDate()
    );
    
    return ResponseEntity.ok(assignment);
}

Acompanhamento de Progresso

O personal trainer pode acompanhar o progresso de seus alunos através de relatórios e métricas:
@GetMapping("/students/{studentId}/progress")
public ResponseEntity<StudentProgressDTO> getStudentProgress(
        @PathVariable Long studentId,
        @AuthenticationPrincipal UserDetails userDetails) {
    
    Long personalId = userService.getUserIdFromUserDetails(userDetails);
    
    // Verificação de relacionamento
    if (!personalStudentService.canAccessStudentData(personalId, studentId)) {
        return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
    }
    
    // Busca de progresso
    StudentProgressDTO progress = progressService.getStudentProgress(studentId);
    return ResponseEntity.ok(progress);
}

Interface do Usuário

Visão do Personal Trainer

O personal trainer tem acesso a uma interface que permite:
  • Visualizar lista de todos os seus alunos
  • Enviar convites para novos alunos
  • Gerenciar relacionamentos existentes
  • Atribuir treinos a alunos específicos
  • Acompanhar o progresso individual de cada aluno
  • Comunicar-se diretamente com os alunos

Visão do Aluno

O aluno tem acesso a uma interface que permite:
  • Visualizar seu personal trainer atual
  • Aceitar ou recusar convites de personal trainers
  • Visualizar treinos atribuídos pelo personal
  • Compartilhar progresso e métricas com o personal
  • Comunicar-se diretamente com o personal

Considerações Importantes

  1. Privacidade de Dados: O sistema garante que dados sensíveis dos alunos só sejam acessíveis pelos personal trainers vinculados a eles.
  2. Múltiplos Relacionamentos: Um aluno pode ter relacionamentos com múltiplos personal trainers, permitindo especialização (ex: um para musculação, outro para corrida).
  3. Histórico de Relacionamentos: O sistema mantém um histórico de todos os relacionamentos, mesmo após seu encerramento, para fins de auditoria e continuidade.
  4. Notificações: Eventos importantes no ciclo de vida do relacionamento (convites, aceitações, atribuições de treino) geram notificações para ambas as partes.
  5. Transição de Personal: O sistema facilita a transição quando um aluno muda de personal trainer, preservando histórico de treinos e progresso.

Implementação Frontend

No frontend, o relacionamento personal-aluno é gerenciado através de serviços e componentes específicos:
// Exemplo de serviço para gerenciamento de relacionamentos
export const personalStudentService = {
  // Listar alunos de um personal
  getStudents: async () => {
    const response = await api.get('/personal/students');
    return response.data;
  },
  
  // Enviar convite para um aluno
  inviteStudent: async (email: string) => {
    const response = await api.post('/personal/invite', { email });
    return response.data;
  },
  
  // Aceitar convite (visão do aluno)
  acceptInvitation: async (invitationId: number) => {
    const response = await api.post(`/student/accept-invitation/${invitationId}`);
    return response.data;
  },
  
  // Atualizar status do relacionamento
  updateRelationshipStatus: async (relationshipId: number, status: 'ACTIVE' | 'INACTIVE') => {
    const response = await api.put(`/personal-student/${relationshipId}/status`, { status });
    return response.data;
  }
};

Conclusão

O relacionamento personal-aluno é um pilar fundamental do FitLocus, permitindo uma experiência personalizada e segura para ambas as partes. A implementação cuidadosa deste relacionamento, com foco em segurança, privacidade e usabilidade, é essencial para o sucesso da plataforma.