Architecture et Implémentation d'une Plateforme de Gestion de Club de Fitness avec Spring Boot et Vue.js

Architecture Technique et Stack Technologique

Le développement de la plateforme de gestion de club de fitness repose sur une architecture découplée, assurant une séparation claire entre le frontend et le backend. Cette approche garantit une maintenabilité optimale et une scalabilité adaptée aux besoins d'une salle de sport moderne.

Backend avec Spring Boot

Spring Boot est utilisé pour orchestrer la logique métier et exposer les API REST. Son mécanisme d'auto-configuration et son serveur embarqué permettent un déploiement rapide. L'intégration avec Spring Security et la gestion des transactions sont simplifiées, offrant une base robuste pour les opérations critiques comme les réservations de cours et la gestion des abonnements.


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
import java.util.List;

@SpringBootApplication
public class FitnessClubApplication {
    public static void main(String[] args) {
        SpringApplication.run(FitnessClubApplication.class, args);
    }
}

@RestController
@RequestMapping("/api/courses")
class CourseController {
    
    @GetMapping("/featured")
    public List<String> getFeaturedCourses() {
        return Arrays.asList("Yoga Matinal", "CrossFit Intensif", "Pilates Doux");
    }
}

Frontend avec Vue.js et UniApp

L'interface d'administration est construite avec Vue.js, tirant parti de son système de réactivité et de son architecture basée sur les composants. Pour l'application mobile destinée aux membres du club, UniApp est utilisé afin de compiler une base de code unique vers les mini-programmes WeChat et les applications natives, assurant une expérience utilisateur fluide sur tous les appareils.



<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Gestion des Abonnements</title>
  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
  <div id="membership-app">
    <h3>Statut de l'abonnement : {{ status }}</h3>
    <button @click="renewMembership">Renouveler l'abonnement</button>
  </div>
  
  <script>
    const { createApp, ref } = Vue;
    createApp({
      setup() {
        const status = ref('Actif');
        const renewMembership = () => {
          status.value = 'Renouvelé avec succès';
        };
        return { status, renewMembership };
      }
    }).mount('#membership-app');
  </script>
</body>
</html>

Persistance des Données avec MyBatis

MyBatis est privilégié pour la couche d'accès aux données. Contrairement aux ORM complets, il offre un contrôle précis sur les requêtes SQL, ce qui est crucial pour optimiser les rapports de fréquentation du club et les requêtes complexes d'analyse des abonnements.

Stratégie de Test et Validation

Une campagne de tests rigoureuse a été menée pour valider la fiabilité du système. Les tests fonctionnels se concentrent sur les parcours critiques des utilisateurs, notamment l'authentification et la gestion des profils.

Tests d'Authentification

Données d'entrée Résultat attendu Résultat obtenu Analyse
Identifiant: admin, MDP: admin123, Captcha: Valide Connexion réussie Connexion réussie Conforme
Identifiant: admin, MDP: erreur, Captcha: Valide Échec (MDP incorrect) Échec (MDP incorrect) Conforme
Identifiant: vide, MDP: admin123, Captcha: Valide Échec (Champ requis) Échec (Champ requis) Conforme

Tests de Gestion des Membres

Action Données Résultat attendu Résultat obtenu
Création Nom: Jean, Rôle: Membre Standard Profil créé et visible Profil créé et visible
Modification ID: 101, Rôle: Membre VIP Rôle mis à jour Rôle mis à jour
Suppression ID: 102 Profil archivé Profil archivé

Implémentation de la Sécurité et Authentification

La sécurisation des API repose sur l'émission de JSON Web Tokens (JWT). Un filtre d'interception valide chaque requête etnrante, garantissant que seules les sessions autorisées peuvent accéder aux ressources protégées.


import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

// Génération du Token
public class JwtProvider {
    private final String secretKey = "SuperSecretFitnessKey";

    public String createToken(Long memberId, String role) {
        Date now = new Date();
        Date expiry = new Date(now.getTime() + 3600000); // Expiration dans 1 heure
        
        return Jwts.builder()
            .setSubject(Long.toString(memberId))
            .claim("role", role)
            .setIssuedAt(now)
            .setExpiration(expiry)
            .signWith(SignatureAlgorithm.HS512, secretKey)
            .compact();
    }
}

// Filtre d'Interception
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) 
            throws IOException, ServletException {
        
        String header = req.getHeader("Authorization");
        if (header != null && header.startsWith("Bearer ")) {
            String token = header.substring(7);
            if (JwtProvider.validate(token)) {
                Long userId = JwtProvider.getUserId(token);
                req.setAttribute("userId", userId);
                chain.doFilter(req, res);
                return;
            }
        }
        res.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Accès non autorisé");
    }
}

Modélisation de la Base de Données

La structure relationnelle est conçue pour supporter les entités principales du club. Voici le schéma de la table gérant les cours collectifs proposés aux adhérents.


CREATE TABLE `fitness_class` (
  `class_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'Identifiant unique du cours',
  `class_name` varchar(150) NOT NULL COMMENT 'Nom du cours',
  `instructor` varchar(100) NOT NULL COMMENT 'Nom du coach',
  `capacity` int(11) NOT NULL COMMENT 'Nombre maximum de participants',
  `schedule` datetime NOT NULL COMMENT 'Date et heure du cours',
  `created_at` timestamp DEFAULT CURRENT_TIMESTAMP COMMENT 'Date de création',
  PRIMARY KEY (`class_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Table des cours collectifs';

INSERT INTO `fitness_class` (`class_name`, `instructor`, `capacity`, `schedule`)
VALUES ('Yoga Matinal', 'Alice Dupont', 20, '2023-11-01 08:00:00');

INSERT INTO `fitness_class` (`class_name`, `instructor`, `capacity`, `schedule`)
VALUES ('CrossFit Intensif', 'Marc Leroy', 15, '2023-11-01 18:30:00');

Étiquettes: SpringBoot Vue.js uniapp MyBatis JWT

Publié le 7 juillet à 01h50