Conception et Implémentation d'un Système d'Examen en Ligne pour Étudiants avec Java

Cet article présente le développement d'un système de gestion d'examens en ligne pour étudiants. Suivant les méthodologies traditionnelles de développement logiciel, nous avons d'abord sélectionné les technologies appropriées pour le projet. Basé sur une analyse approfondie des besoins, nous avons conçu l'architecture des modules et la structure de la base de données. Nous avons élaboré des diagrammes fonctionnels, des flux de processus et des diagrammes entité-relation pour représenter l'ensemble du système.

Par la suite, nous avons implémenté l'architecture conçue en écrivant le code sourcce pour chaque fonctionnalité. Enfin, nous avons mené une série de tests sur le système, incluant des tests unitaires, des tests de performance et des tests d'intégration. Les résultats ont démontré que le système répond aux exigences fonctionnelles et fonctionne de manière stable sans défauts majeurs.

Environnement de Développement

  • Langage de développement : Java
  • Framework : Spring Boot
  • Version JDK : JDK 1.8
  • Serveur : Tomcat 7
  • Base de données : MySQL 5.7 (version obligatoire)
  • Outil de base de données : Navicat 11
  • Logiciel de développement : Eclipse/MyEclipse/IntelliJ IDEA
  • Gestionnaire de dépendances : Maven 3.3.9
  • Navigateur : Google Chrome

URL d'accès :

  • Interface d'administration : localhost:8080/nom_projet/admin/dist/index.html
  • Interface étudiant : localhost:8080/nom_projet/front/dist/index.html

Identifiants par défaut :

  • Nom d'utilisateur administrateur : admin
  • Mot de passe administrateur : admin

Technologies Utilisées

Présentation de Java

Java est un langage de programmation orienté objet et statique. Il se caractérise par son support du multithreading et de la programmation orientée objet. L'une de ses principales forces est la capacité à diviser les applications en modules indépendants et encapsulés, favorisant ainsi une maintainance aisée. Java intègre nativement des interfaces réseau qui facilitent le développement d'applications web.

Pour assurer la stabilité des applications, Java inclut un mécanisme de gestion automatique de la mémoire (garbage collector) et un système robuste de gestion des exceptions. Ces caractéristiques font de Java un langage privilégié pour le développement d'applications critiques.

La programmation orientée objet constitue le pilier de Java. Elle met à disposition l'encapsulation, l'héritage et le polymorphisme. L'encapsulation permet de protéger les données d'une classe, tandis que l'héritage favorise la réutilisation du code. Le polymorphisme, quant à lui, permet d'utiliser une interface commune pour manipuler différents types d'objets.

Framework Spring Boot

Spring Boot représente l'une des avancées les plus significatives dans le développement backend ces dernières années. Ce framework a révolutionné l'écosystème Spring en éliminant la complexité des configurations traditionnelles, simplifiant ainsi considérablement la création et le développement d'applications.

Spring Boot conserve tous les avantages du framework Spring tout en ajoutant une configuration automatisée qui effectue de nombreuses opérations en arrière-plan. Cette approche réduit la charge de configuration manuelle pour les développeurs. De plus, le framework intègre nativement de nombreuses bibliothèques populaires, résolvant ainsi les problèmes de dépendances et de conflits de versions.

Base de Données MySQL

MySQL est un système de gestion de bases de données relationnelles appartenant à Oracle. Son langage de requête non structuré permet aux utilisateurs d'interagir efficacement avec les données. Depuis son apparition, MySQL a gagné en popularité grâce à ses nombreux avantages par rapport aux autres systèmes de bases de données.

Les points forts de MySQL incluent sa rapidité d'exécution, sa flexibilité et sa robustesse en matière de sécurité. Sa syntaxe simplifiée permet d'accomplir des opérations complexes avec peu de code. Ces caractéristiques font de MySQL un choix privilégié pour le stockage et la gestion des données dans les applications web.

La base de données MySQL offre également une excellente sécurité grâce à des mécanismes d'authentification et de chiffrement. Sa capacité à gérer des données partagées avec une faible redondance et une bonne extensibilité en fait une solution idéale pour notre système d'examens en ligne.

Exemples de Code Fonctionnel

package com.gestionfichiers;

import java.io.File;
import java.io.IOException;
import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.annotation.PublicAccess;
import com.entite.ConfigEntite;
import com.service.ConfigService;
import com.utils.Reponse;

/**
 * Contrôleur pour la gestion des fichiers uploadés
 */
@RestController
@RequestMapping("/ressources")
public class GestionRessourcesController {
    
    @Autowired
    private ConfigService configurationService;
    
    /**
     * Upload d'un fichier
     */
    @RequestMapping("/televerser")
    @PublicAccess
    public Reponse televerserFichier(@RequestParam("fichier") MultipartFile fichier, String type) throws Exception {
        if (fichier.isEmpty()) {
            throw new Exception("Le fichier à téléverser ne peut pas être vide");
        }
        
        String extension = fichier.getOriginalFilename().substring(fichier.getOriginalFilename().lastIndexOf(".")+1);
        File chemin = new File(ClassLoader.getSystemResource("static").getPath());
        if(!chemin.exists()) {
            chemin = new File("");
        }
        File repertoireUpload = new File(chemin.getAbsolutePath(), "/upload/");
        if(!repertoireUpload.exists()) {
            repertoireUpload.mkdirs();
        }
        
        String nomFichier = new Date().getTime() + "." + extension;
        File destination = new File(repertoireUpload.getAbsolutePath() + "/" + nomFichier);
        fichier.transferTo(destination);
        
        if(type != null && type.equals("1")) {
            ConfigEntite config = configurationService.selectOne(new ConfigEntite().ajouteCondition("nom", "photoProfil"));
            if(config == null) {
                config = new ConfigEntite();
                config.setNom("photoProfil");
                config.setValeur(nomFichier);
            } else {
                config.setValeur(nomFichier);
            }
            configurationService.enregistrerOuMettreAJour(config);
        }
        
        return Reponse.succes().ajouteDonnee("fichier", nomFichier);
    }
    
    /**
     * Téléchargement d'un fichier
     */
    @PublicAccess
    @RequestMapping("/telecharger")
    public ResponseEntity<byte> telechargerFichier(@RequestParam String nomFichier) {
        try {
            File chemin = new File(ClassLoader.getSystemResource("static").getPath());
            if(!chemin.exists()) {
                chemin = new File("");
            }
            File repertoireUpload = new File(chemin.getAbsolutePath(), "/upload/");
            if(!repertoireUpload.exists()) {
                repertoireUpload.mkdirs();
            }
            
            File fichier = new File(repertoireUpload.getAbsolutePath() + "/" + nomFichier);
            if(fichier.exists()){
                HttpHeaders entetes = new HttpHeaders();
                entetes.setContentType(MediaType.APPLICATION_OCTET_STREAM);
                entetes.setContentDispositionFormData("attachment", nomFichier);
                
                return new ResponseEntity<byte>(org.apache.commons.io.FileUtils.readFileToByteArray(fichier), 
                                                 entetes, HttpStatus.CREATED);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new ResponseEntity<byte>(HttpStatus.INTERNAL_SERVER_ERROR);
    }
}
</byte></byte></byte>
package com.controleurforum;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.HashMap;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.entite.ForumEntite;
import com.entite.vue.ForumVue;
import com.service.ForumService;
import com.utils.Reponse;
import com.utils.MPUtil;
import com.utils.GenerateurID;

/**
 * Contrôleur pour le forum de discussion
 * @author 
 * @email 
 * @date 2023-03-05 11:20:58
 */
@RestController
@RequestMapping("/forum")
public class ForumControleur {
    
    @Autowired
    private ForumService forumService;
    
    /**
     * Liste pour l'interface d'administration
     */
    @RequestMapping("/page")
    public Reponse page(@RequestParam Map<string object=""> params, ForumEntite forum,
                       HttpServletRequest request){
        if(!request.getSession().getAttribute("role").toString().equals("administrateur")) {
            forum.setIdUtilisateur((Long)request.getSession().getAttribute("userId"));
        }
        
        return Reponse.succes().ajouteDonnee("donnees", 
            forumService.obtenirPage(params, MPUtil.trier(MPUtil.entre(MPUtil.commeOuEgal(forum), params), params)));
    }
    
    /**
     * Liste pour l'interface publique
     */
    @RequestMapping("/liste")
    public Reponse liste(@RequestParam Map<string object=""> params, ForumEntite forum, 
                        HttpServletRequest request){
        if(!request.getSession().getAttribute("role").toString().equals("administrateur")) {
            forum.setIdUtilisateur((Long)request.getSession().getAttribute("userId"));
        }
        
        return Reponse.succes().ajouteDonnee("donnees", 
            forumService.obtenirPage(params, MPUtil.trier(MPUtil.entre(MPUtil.commeOuEgal(forum), params), params)));
    }

    /**
     * Liste publique sans authentification
     */
    @RequestMapping("/listePublique")
    public Reponse listePublique(@RequestParam Map<string object=""> params, ForumEntite forum){
        return Reponse.succes().ajouteDonnee("donnees", 
            forumService.obtenirPage(params, MPUtil.trier(MPUtil.entre(MPUtil.commeOuEgal(forum), params), params)));
    }

    /**
     * Détail d'une discussion
     */
    @RequestMapping("/details/{id}")
    @PublicAccess
    public Reponse details(@PathVariable("id") Long id){
        ForumEntite forum = forumService.obtenirParId(id);
        obtenirSousDiscussions(forum);
        return Reponse.succes().ajouteDonnee("donnees", forum);
    }
    
    private ForumEntite obtenirSousDiscussions(ForumEntite forum) {
        List<forumentite> sousDiscussions = new ArrayList<>();
        sousDiscussions = forumService.obtenirListe(new ForumEntite().ajouteCondition("parentId", forum.getId()));
        
        if(sousDiscussions == null || sousDiscussions.size() == 0) {
            return null;
        }
        
        forum.setSousDiscussions(sousDiscussions);
        for(ForumEntite discussion : sousDiscussions) {
            obtenirSousDiscussions(discussion);
        }
        return forum;
    }

    /**
     * Création d'une nouvelle discussion
     */
    @RequestMapping("/creer")
    public Reponse creer(@RequestBody ForumEntite forum, HttpServletRequest request){
        forum.setId(GenerateurID.generer());
        forum.setIdUtilisateur((Long)request.getSession().getAttribute("userId"));
        forumService.creer(forum);
        return Reponse.succes();
    }

    /**
     * Modification d'une discussion
     */
    @RequestMapping("/modifier")
    @Transactional
    public Reponse modifier(@RequestBody ForumEntite forum){
        forumService.mettreAJour(forum);
        return Reponse.succes();
    }

    /**
     * Suppression d'une ou plusieurs discussions
     */
    @RequestMapping("/supprimer")
    public Reponse supprimer(@RequestBody Long[] ids){
        forumService.supprimerListe(ids);
        return Reponse.succes();
    }
    
    /**
     * Compteur de notifications
     */
    @RequestMapping("/notification/{colonne}/{type}")
    public Reponse notification(@PathVariable("colonne") String colonne, 
                               @PathVariable("type") String type,
                               @RequestParam Map<string object=""> map) {
        map.put("colonne", colonne);
        map.put("type", type);
        
        if(type.equals("2")) {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            Calendar c = Calendar.getInstance();
            Date dateDebut = null;
            Date dateFin = null;
            
            if(map.get("datedebut") != null) {
                Integer debut = Integer.parseInt(map.get("datedebut").toString());
                c.setTime(new Date()); 
                c.add(Calendar.DAY_OF_MONTH, debut);
                dateDebut = c.getTime();
                map.put("datedebut", sdf.format(dateDebut));
            }
            
            if(map.get("datefin") != null) {
                Integer fin = Integer.parseInt(map.get("datefin").toString());
                c.setTime(new Date());
                c.add(Calendar.DAY_OF_MONTH, fin);
                dateFin = c.getTime();
                map.put("datefin", sdf.format(dateFin));
            }
        }
        
        return Reponse.succes().ajouteDonnee("compteur", 
            forumService.compter(new ForumEntite().ajouteConditions(map)));
    }
}
</string></forumentite></string></string></string>

Tests Fonctionnels

Le système a d'abord été installé et testé localement sur un serveur. Ensuite, une fois la structure et les fonctionnalités maîtrisées, nous avons procédé à des tests boîte blanche et boîte noire.

Le développement de systèmes logiciels implique la résolution de problèmes complexes, ce qui expose à des erreurs à chaque étape du cycle de vie. Les tests logiciels visent à identifier ces erreurs avant que le système ne soit déployé en production.

Pour établir un plan de测试 approprié, nous avons suivi les principes suivants :

  • Tout test doit être tracé aux besoins clients
  • Le plan de test doit être établi dès que le modèle d'utilisation client est défini
  • Application du principe de Pareto : se concentrer sur les 20% de modules responsables de 80% des erreurs
  • Progression des tests, des unitaires à l'intégration complète
  • Conception de méthodes de test pour couvrir logiquement tout le code

Conclusion

Notre système présente plusieurs avantages par rapport aux autres solutions d'examens en ligne existantes : fonctionnalités complètes, facilité de maintenance, gestion de base de données simplifiée, interface conviviale, manipulation intuitive, haute performance et sécurité renforcée.

Sur le plan technique, ce système offre plusieurs atouts :

  • L'utilisation de Java pour les pages dynamiques assure une excellente maintenabilité et réutilisabilité du code
  • L'architecture Spring Boot permet une séparation claire entre la présentation et la logique métier, facilitant la gestion des modules, particulièrement adaptée aux projets de grande envergure
  • La base de données MySQL offre un support XML natif, une bonne extensibilité, une facilité d'utilisation et un niveau de sécurité élevé

En conclusion, ce projet constitue une expérience complète allant de la conception à l'implémentation. Nous espérons pouvoir continuer à l'améliorer pour en faire une solution toujours plus performante et adaptée aux besoins des établissements d'enseignement.

Étiquettes: Java Spring Boot MySQL Système d'examen en ligne Développement Web

Publié le 6 juillet à 23h54